diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml new file mode 100644 index 0000000..ba4f2e5 --- /dev/null +++ b/.github/workflows/build-test.yml @@ -0,0 +1,31 @@ +name: Build and Test Node + +on: + pull_request: + branches: + - develop + - master + +jobs: + build: + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v3 + + - name: Install ganache + run: | + sudo apt-get update + sudo apt-get install -y npm + sudo npm i -g ganache-cli + ganache-cli -m "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat" --port 8544 --accounts 20 --networkId=9 --gasLimit=10000000 > /dev/null & + + - name: Setup JDK/Gradle + uses: actions/setup-java@v3 + with: + distribution: 'corretto' + java-version: '15' + cache: 'gradle' + + - name: Build + run: ./gradlew build --no-daemon --info diff --git a/CHANGELOG.md b/CHANGELOG.md index 26da60f..232101d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,25 +1,57 @@ # OCN Node Changelog -## 1.1.0-rc1 -### Upcoming +## 1.2.0-rc1 +### May 3, 2021 + +Changes auth tokes to conform to OCPI 2.2 d2 update. With this new version, +all new OCPI Parties will need to encode Authorization header tokens with base64. +The OCN Node will also send base64-encoded auth tokens to the OCPI Party. +Note that existing OCPI parties can continue using non-encoded tokens +without change. + +## 1.1.2 +### Dec 17, 2020 + +Fixes issue where ocn-node startup could fail if behind a load balancer or gateway (ocn-node issue #25) + +## 1.1.1 +### Nov 07, 2020 + +Fixes bug with deleted parties being communicated as having a "planned" status (ocn-node issue #22) + +## 1.1.0 +### Oct 16, 2020 + +1.1.0 release of Open Charging Network Node. Includes: + +- Forwarding of requests to "Ocn Services" as specified by the OCN Service Interface +- Initial HubClientInfo module implementation + +## 1.1.0-rc2 +### Sep 25, 2020 + +Fixes bug with handling request body when forwarding custom OCPI module requests. + +## 1.1.0-rc1 +### Jun 30, 2020 Adds the ability for requests to be forwarded to "Ocn Services" with matching permissions. The Ocn ServiceInterface, using the new Permissions contract in the OCN Registry, allows data to be shared and accessed using a permission system. -## 1.1.0-rc0 +## 1.1.0-rc0 ### Apr 28, 2020 Includes initial hubclientinfo OCPI module implementation. - Optional "Still-Alive" check requests connected parties versions endpoint at regular intervals. - Optional "Planned Party" search scans registry for newly planned parties at regular intervals. -- New configuration properties under `ocn.node`: `stillAliveEnabled`, `stillAliveRate`, +- New configuration properties under `ocn.node`: `stillAliveEnabled`, `stillAliveRate`, `plannedPartySearchEnabled`, `plannedPartySearchRate`. - + ## 1.0.0 ### Mar 03, 2020 Initial release of the Open Charging Network Node. - All OCPI modules included, except for hubclientinfo. - Custom OCPI module, *OcnRules*, for setting counter-party whitelist rules. -- Administrator API for generating TOKEN_A for planned parties. \ No newline at end of file +- Administrator API for generating TOKEN_A for planned parties. diff --git a/CONFIGURATION.md b/CONFIGURATION.md index 547f57f..2d28f1e 100644 --- a/CONFIGURATION.md +++ b/CONFIGURATION.md @@ -116,6 +116,12 @@ In the scenario of load balancing nodes, only one of the nodes should have this ### `ocn.node.plannedPartySearchRate` Sets rate in milliseconds at which the HubClientInfo module runs the planned party search task, outlined above. [Default: 3600000 (1 hour)] + +### `ocn.node.serviceInterfaceEnabled` +If turned on, permissions listed in the Registry will be honoured. For example, if a CPO has +agreed to forwarding permissions from a particular service, the node will honour the +permission by forwarding the request to the service. Set to false in order to ignore these +permissions. [Default: true] ### `ocn.node.web3.provider` Sets the JSON RPC provider URL for the OCN environment. This is the Ethereum blockchain node which provides @@ -124,10 +130,16 @@ environment and the production environment. The former runs on the [Volta test network](https://energyweb.atlassian.net/wiki/spaces/EWF/pages/702677023/Chain+Volta+Test+Network), whilst the latter runs on the [Energy Web Chain](https://energyweb.atlassian.net/wiki/spaces/EWF/pages/718078071/Chain+Energy+Web+Chain+Production+Network). +[Default: https://volta-rpc.energyweb.org] +[volta: https://volta-rpc.energyweb.org] +[ewc: https://rpc.energyweb.org] + More information about the environments can be found here: [test](https://shareandcharge.atlassian.net/wiki/spaces/OCN/pages/409206816/Public+Test+Network) and [production](https://shareandcharge.atlassian.net/wiki/spaces/OCN/pages/409305103/Production+Network). + + ### `ocn.node.web3.contracts.registry` Sets the OCN Registry smart contract address. For the public test environment, this is `0xd57595D5FA1F94725C426739C449b15D92758D55`. For the production network, this is diff --git a/Dockerfile b/Dockerfile index 84a77cd..dc082da 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,4 +4,4 @@ COPY build /ocn-node COPY src/main/resources/* /ocn-node/ WORKDIR /ocn-node -CMD ["java", "-jar", "./libs/ocn-node-1.0.0.jar"] +CMD ["java", "-jar", "./libs/ocn-node-1.2.0-rc2.jar"] diff --git a/README.md b/README.md index d27528b..816b7f1 100644 --- a/README.md +++ b/README.md @@ -1,88 +1,87 @@ # Open Charging Network Node -The Open Charging Network (OCN) node with Open Charge Point Interface (OCPI) v2.2 API. +The Open Charging Network (OCN) node with Open Charge Point Interface (OCPI) v2.2 API. This is a community project, aimed at incorporating and building on the open OCPI standard. As with OCPI, contributions are welcome in the form of comments, pull requests and raised issues. Visit our -[issue tracker](https://bitbucket.org/shareandcharge/ocn-node/issues) for an overview of current and past issues. -Questions may also be asked on [Stack Overflow](https://stackoverflow.com/questions/tagged/shareandcharge), or in the -[Gitter community](https://gitter.im/shareandcharge/community). +[issue tracker](https://github.com/energywebfoundation/ocn-node/issues) for an overview of current and past issues. +Questions may also be asked in the +[Slack community](https://app.slack.com/client/T0BNK39NX/CRP0VKEMD). Before contributing to the source code, please take the time to read over the [Developer Certificate of Origin](https://developercertificate.org/). For more information, see our -[contributing guidelines](https://shareandcharge.atlassian.net/wiki/spaces/OCN/pages/360611849/Contributing+to+the+Open+Charging+Network). +[contributing guidelines](https://energy-web-foundation.gitbook.io/energy-web/technology/application-layer/open-charging-network/open-source-development). ## The Open Charging Network The OCN is a decentralized eRoaming hub. To participate in the OCN, a node must be used to broker OCPI requests -(e.g. start/stop charging requests, POI data retrieval) between parties. A node can be set up and run by anyone, however -to connect to a node, two steps are needed: +(e.g. start/stop charging requests, POI data retrieval) between parties. A node can be set up and run by anyone, however +to connect to a node, two steps are needed: -1. A registration token (so-called Token A in OCPI terminology) must be generated for the prospective platform by the +1. A registration token (so-called Token A in OCPI terminology) must be generated for the prospective platform by the node administrator. -2. The platform must register themselves in the [OCN Registry](https://bitbucket.org/shareandcharge/ocn-registry), +2. The platform must register themselves in the [OCN Registry](https://github.com/energywebfoundation/ocn-registry), stating that they are using that particular node. -Once a registration token is obtained and the platform is listed in the registry, the OCPI credentials handshake with -the OCN Node can be initiated, providing access to all OCPI modules and interfaces used for peer-to-peer -communication. When a counter-party is found (either offline or via the registry), requests are sent to +Once a registration token is obtained and the platform is listed in the registry, the OCPI credentials handshake with +the OCN Node can be initiated, providing access to all OCPI modules and interfaces used for peer-to-peer +communication. When a counter-party is found (either offline or via the registry), requests are sent to them via the sender's OCN Node. -For more information about the OCN, check out the [wiki](https://bitbucket.org/shareandcharge/ocn-node/wiki/). +For more information about the OCN, check out the [wiki](https://energy-web-foundation.gitbook.io/energy-web/technology/application-layer/open-charging-network). ## HTTP API Documentation -The [HTTP API Documentation](https://shareandcharge.bitbucket.io) for the OCN Node describes endpoints which can be used +The [HTTP API Documentation](https://shareandcharge.bitbucket.io) for the OCN Node describes endpoints which can be used by administrators and users (OCPI parties). Outside of the full OCPI v2.2 API, OCN Nodes provide additional features, such as the custom OCPI module, _OcnRules_, as well as ways for admins to restrict use and users to query the OCN Registry. ## Dependencies -The OCN Node is built with Kotlin, targeting the JVM. See the sections on running and building a node for +The OCN Node is built with Kotlin, targeting the JVM. See the sections on running and building a node for further details. The choice of operating system is up to the administrator. By and large, the OCN Node has been developed and run on -Unix-like operating systems, particularly Ubuntu and Fedora. There is currently no guarantee that it will work on other -operating systems. +Unix-like operating systems, particularly Ubuntu and Fedora. There is currently no guarantee that it will work on other +operating systems. ## Tutorial: Running your own Local Open Charging Network -Before running a node and connecting it to a local, test or prod environment, it is recommended to first become -acquainted with how the network operates. The provided `docker-compose` file spins up a local environment with the OCN -Registry and two OCN Nodes pre-configured. A [tutorial](./examples) has been provided to guide administrators and users -of an OCN Node alike through various use case examples. To complete this tutorial it is necessary to install -[Docker Compose](https://docs.docker.com/compose/install/) in addition to the above dependencies. +Before running a node and connecting it to a local, test or prod environment, it is recommended to first become +acquainted with how the network operates. +A [tutorial](https://bitbucket.org/shareandcharge/ocn-demo) has been provided to guide administrators and users +of an OCN Node alike through various use case examples. ## Running a Node -First of all, ensure a [Java Runtime Environment](https://openjdk.java.net/install/) (at least version 8) is installed. +First of all, ensure a [Java Runtime Environment](https://openjdk.java.net/install/) (at least version 8) is installed. For example, via the Ubuntu package manager: ``` sudo apt install openjdk-8-jre ``` Pre-built OCN Node packages can be found on the repository's -[downloads page](https://bitbucket.org/shareandcharge/ocn-node/downloads/). For the rest of this section +[releases page](https://github.com/energywebfoundation/ocn-node/releases). For the rest of this section it will be assumed that this was the method chosen by the user. For information about building the node, see the subsequent section that follows. -Once downloaded, extract the contents of the archive and change directory: +Once downloaded, extract the contents of the archive and change directory: ``` -tar zxvf ocn-node-1.1.0-rc0.tar.gz -cd ocn-node-1.1-0-rc0 +tar zxvf ocn-node-1.1.2.tar.gz +cd ocn-node-1.1-2 ``` Now we can run our node: ``` -java -jar ocn-node-1.1.0-rc0.jar +java -jar ocn-node-1.1.2.jar ``` ### Configuration -By default the OCN Node will use the `dev` profile's runtime properties. These are specified in -`application.dev.properties`. This can be used to get a node up and running and connected to the -OCN public test environment right away. +By default the OCN Node will use the `dev` profile's runtime properties. These are specified in +`application.dev.properties`. This can be used to get a node up and running and connected to the +OCN public test environment right away. However, sooner or later it is likely that configuration options must be changed to match the environment. For example, to configure our local development environment correctly, we might wish to create a new profile @@ -94,7 +93,7 @@ To do so, we can make a copy of the `dev` profile, naming it however we so desir cp application.dev.properties application.custom-local-env.properties ``` -We can then edit our `custom-local-env` properties file to point to the local blockchain node. +We can then edit our `custom-local-env` properties file to point to the local blockchain node. If we wish to setup the node for a production environment, an example `prod` profile has been provided too: @@ -102,27 +101,27 @@ If we wish to setup the node for a production environment, an example `prod` pro cp application.prod.properties application.custom-prod-env.properties ``` -For details on all available configuration values, please visit our comprehensive +For details on all available configuration values, please visit our comprehensive [OCN Node Configuration documentation](./CONFIGURATION.md). ### Listing the Node in the OCN Registry A Node must be listed in the registry for it to be usable on the network. This can be achieved by installing -the OCN Registry CLI. Either clone the [OCN-Registry](https://bitbucket.org/shareandcharge/ocn-registry) repository +the OCN Registry CLI. Either clone the [OCN-Registry](https://github.com/energywebfoundation/ocn-registry) repository and follow the instructions in the README, or install the NPM package: ``` npm i -g @shareandcharge/ocn-registry ``` -Once installed, add your OCN Node url using the private key as set in the node's configuration (note that the wallet +Once installed, add your OCN Node url using the private key as set in the node's configuration (note that the wallet key must be funded and the correct network chosen using the `-n` flag: ``` ocn-registry set-node https://ocn.server.net -n prod -s 0x1c3e5453c0f9aa74a8eb0216310b2b013f017813a648fce364bf41dbc0b37647 ``` -Alternatively, to register a node on the public test environment, use `-n volta`. +Alternatively, to register a node on the public test environment, use `-n volta`. If successful the node is now available for prospective platforms to link themselves to in the OCN Registry. @@ -157,10 +156,10 @@ Edit the service file to match your environment, replacing the user and properti ``` [Service] User=ubuntu -WorkingDirectory=/home/ubuntu/ocn-node-1.1.0-rc0 -ExecStart=/usr/bin/java -jar -Dspring.config.location=application.custom-prod-env.properties ocn-node-1.1.0-rc0.jar +WorkingDirectory=/home/ubuntu/ocn-node-1.1.2 +ExecStart=/usr/bin/java -jar -Dspring.config.location=application.custom-prod-env.properties ocn-node-1.1.0.jar ``` - + Then, copy the service file to the `/etc/systemd/system` directory: ``` sudo cp ocn-node.service /etc/systemd/system @@ -180,8 +179,8 @@ journalctl -fu ocn-node -n 1000 ## Development -To be able to build the project, the [Java Development Kit](https://openjdk.java.net/install/) is required. -Make sure at least version 8 is installed and you have the JDK, not only the JRE. +To be able to build the project, the [Java Development Kit](https://openjdk.java.net/install/) is required. +Make sure at least version 8 is installed and you have the JDK, not only the JRE. Gradle tasks are configured in `build.gradle.kts` using the Kotlin DSL. The project can be built with: ``` @@ -196,7 +195,7 @@ Gradle tasks are configured in `build.gradle.kts` using the Kotlin DSL. The proj ### Run integration tests -Integration tests depend on `ganache-cli`, a local development blockchain, which is installed using NPM. In one terminal +Integration tests depend on `ganache-cli`, a local development blockchain, which is installed using NPM. In one terminal window, run the following task, which will attempt to install ganache if not already present and then run it: ``` ./gradlew ganache @@ -215,6 +214,7 @@ This is helpful for developing without having to worry about funding and managin ``` ocn.node.web3.provider = http://localhost:8544 ocn.node.web3.contracts.registry = 0x345ca3e014aaf5dca488057592ee47305d9b3e10 +ocn.node.web3.contracts.permissions = 0xf25186B5081Ff5cE73482AD761DB0eB0d25abfBF ``` ### Generating new build archives @@ -226,5 +226,5 @@ Make sure the project has been built already, then run: ### Generating new API documentation -Documentation is generated automatically on build. The asciidoc template can be found in -`src/docs/asciidoc/index.adoc` and the output in `build/asciidoc/html5/index.html`. \ No newline at end of file +Documentation is generated automatically on build. The asciidoc template can be found in +`src/docs/asciidoc/index.adoc` and the output in `build/asciidoc/html5/index.html`. diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index defc15d..bde5015 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -1,20 +1,35 @@ -# This is a sample build configuration for Java (Gradle). -# Check our guides at https://confluence.atlassian.com/x/zd-5Mw for more examples. -# Only use spaces to indent your .yml configuration. -# ----- -# You can specify a custom docker image from Docker Hub as your build environment. image: alpine:latest -pipelines: - default: - - step: +definitions: + steps: + - step: &build-and-test + name: Build and run tests caches: - gradle - node - script: # Modify the commands below to build your repository. - # You must commit the Gradle wrapper to your repository - # https://docs.gradle.org/current/userguide/gradle_wrapper.html + script: - apk add gradle npm - npm install -g ganache-cli - ganache-cli -m "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat" --port 8544 --accounts 20 --networkId=9 --gasLimit=10000000 > /dev/null & - ./gradlew build +pipelines: + default: + - step: *build-and-test + custom: + docker-image-deploy: + - variables: + - name: version + - step: + <<: *build-and-test + artifacts: + - build/** + - src/main/resources/** + - step: + name: Push to Docker Hub + script: + - docker login --username $DOCKER_USERNAME --password $DOCKER_PASSWORD + - docker build -t openchargingnetwork/ocn-node:${version} -t openchargingnetwork/ocn-node:latest . + - docker push openchargingnetwork/ocn-node:${version} + - docker push openchargingnetwork/ocn-node:latest + services: + - docker diff --git a/build.gradle.kts b/build.gradle.kts index a5c8818..3b15878 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,7 +15,7 @@ plugins { } group = "snc.openchargingnetwork.node" -version = "1.1.0-rc1" +version = "1.2.0-rc2" java.sourceCompatibility = JavaVersion.VERSION_1_8 val snippetsDir = "build/generated-snippets" @@ -32,7 +32,7 @@ repositories { } dependencies { - implementation("shareandcharge.openchargingnetwork:notary:1.0.1-beta1") + implementation("shareandcharge.openchargingnetwork:notary:1.0.1") implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.boot:spring-boot-starter-web") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") @@ -146,4 +146,4 @@ tasks.register("archive") { "CHANGELOG.md") } -} \ No newline at end of file +} diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..2684ae6 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,1288 @@ + + + + + + + +Open Charging Network (OCN) Node Documentation + + + + + +
+
+
+
+

The following documentation provides usage details, with examples, for administrators and users of +an OCN Node. It assumes that the node has already been set up according to instructions provided in the +repository.

+
+
+
+
+

Health

+
+
+

The health endpoint can be used to ensure that the message broker is reachable via HTTP.

+
+
+

Request

+
+
+
GET /health HTTP/1.1
+Host: localhost:8080
+
+
+
+
+

Response

+
+
+
HTTP/1.1 200 OK
+Content-Type: text/plain;charset=UTF-8
+Content-Length: 2
+
+OK
+
+
+
+
+
+
+

Registry

+
+
+

The registry API provides helpful OCN Registry queries. The +registry smart contract itself holds information about parties and nodes on the network. By extension, the following +API can be used to check whether the OCN Node has been correctly configured to interact with the registry smart +contract.

+
+
+

Get Node Information

+
+

This endpoint retrieves the publicly available information (root public URL, Ethereum wallet address) of a given OCN +Node. This information should match the OCN Registry listing of the OCPI party using the Node.

+
+
+

Request

+
+
+
GET /ocn/registry/node-info HTTP/1.1
+Host: localhost:8080
+
+
+
+
+

Response

+
+
+
HTTP/1.1 200 OK
+Content-Type: application/json
+Content-Length: 85
+
+{"url":"https://node.ocn.org","address":"0x9bc1169ca09555bf2721a5c9ec6d69c8073bfeb4"}
+
+
+
+
+
+

Get Party’s Node Information

+
+

This endpoint is used to find the OCN Registry listing of a given party. Two url path variables should be provided: +the OCPI country_code and party_id.

+
+
+

Request

+
+
+
GET /ocn/registry/node/DE/ABC HTTP/1.1
+Host: localhost:8080
+
+
+
+
+

Response

+
+
+
HTTP/1.1 200 OK
+Content-Type: application/json
+Content-Length: 85
+
+{"url":"https://node.ocn.org","address":"0x22D44D286d219e1B55E6A5f1a3c82Af69716756A"}
+
+
+
+
+
+
+
+

Admin

+
+
+

The admin API allows administrators to manage their OCN Node. Currently, the only admin functionality is the generatoion +of new Open Charge Point Interface registration tokens (so-called CREDENTIALS_TOKEN_A) for interested platforms.

+
+
+

Request

+
+
+
POST /admin/generate-registration-token HTTP/1.1
+Content-Type: application/json
+Authorization: Token 1234567890
+Content-Length: 40
+Host: localhost:8080
+
+[{"party_id":"SNC","country_code":"DE"}]
+
+
+
+

The Authorization header should be set in the format Token ${api_key}. The admin can find this in stdout when +the application runs (a new key will be generated each time unless specified in the application’s properties file).

+
+
+

Body

+
+
+
[{"party_id":"SNC","country_code":"DE"}]
+
+
+
+

The request body is a list of a platforms' roles, containing their party_id +and country_code. This allows a platform with multiple OCPI roles (i.e. EMSP and CPO) to register on the +same OCPI connection.

+
+
+
+
+

Response

+
+
+
HTTP/1.1 200 OK
+Content-Type: application/json
+Content-Length: 96
+
+{"token":"dacb3996-af67-4da1-b8b2-9102562143c0","versions":"https://node.ocn.org/ocpi/versions"}
+
+
+
+

The response contains the OCPI TOKEN_A and the message broker’s versions endpoint, both of which are needed to +start the registration process.

+
+
+
+
+
+

Open Charge Point Interface

+
+
+

The Open Charge Point Interface enables eMobility platforms to connect to one another. The Open Charging Network +uses this as it’s primary means of connecting such parties. It is important to have developed an OCPI version 2.2 +API beforehand, in order to connect to and use an OCN Node. This comprises, as a bare minimum, the credentials module. +Documentation for version 2.2 can be found on OCPI @ GitHub.

+
+
+

The node strives to provide all the functionality of OCPI, though there are currently some features missing:

+
+
+
    +
  • +

    The hubclientinfo module is still work in progress.

    +
  • +
  • +

    Hub-specific features such as broadcast push and intelligent routing are not included.

    +
  • +
+
+
+

See the issues page on Bitbucket for more.

+
+
+
+
+

Pagination and Proxying Resources

+
+
+

The OCN handles OCPI pagination requests in a special way. When sending a paginated request, for example, +to get the first 50 locations from a Charge Point Operator, the CPO will respond with a Link header which +only their OCN Node will be able to access.

+
+
+

Because of this, the OCN Node must proxy this header. The sender will receive from their OCN Node a Link +header such as https://some.ocn-node.net/ocpi/sender/2.2/locations/page/2 which will correspond to the +original CPO resource, for example, https://some.cpo.com/ocpi/cpo/2.2/locations?offset=100&limit=100.

+
+
+

Other examples of resources which require proxying include the Location header returned from the POST method +of the cdrs receiver interface, as well as any response_url used in the commands and charging profiles +modules.

+
+
+
+
+

Custom OCPI modules

+
+
+

The OCN supports the use of custom OCPI modules. Such modules can be registered with an OCN Node during +the credentials handshake.

+
+
+

When requesting version details from the node, the response will contain the following endpoints:

+
+
+
+
{
+  "identifier": "custom",
+  "role": "sender",
+  "endpoint": "https://some.ocn-node.net/ocpi/custom/sender"
+},
+{
+  "identifier": "custom",
+  "role": "receiver",
+  "endpoint": "https://some.ocn-node.net/ocpi/custom/receiver"
+}
+
+
+
+

This acts as a wildcard for all custom OCPI modules. Here’s how it works:

+
+
+
    +
  1. +

    CPO completes credentials handshake with OCN Node

    +
    +
      +
    1. +

      CPO fetches OCN Node versions, saves endpoint details for wildcard custom module

      +
    2. +
    3. +

      OCN Node fetches CPO versions, saves endpoint details for enriched-locations custom module

      +
    4. +
    +
    +
  2. +
  3. +

    CPO sends their custom module request to the OCN Node

    +
    +
      +
    1. +

      requests any HTTP method

      +
    2. +
    3. +

      places module name as first path variable, followed by any additional variables required by the +custom module: https://some.ocn-node.net/ocpi/custom/receiver/enriched-locations/location1

      +
    4. +
    5. +

      appends url-encoded queries and json body if necessary

      +
    6. +
    +
    +
  4. +
  5. +

    OCN Node routes the message to recipient’s OCN Node

    +
    +
      +
    1. +

      recipient’s OCN Node looks up enriched-locations in recipient’s endpoints

      +
    2. +
    3. +

      recipient’s OCN Node forwards the request to https://recipient.msp.com/ocpi/emsp/2.2/enriched-locations/location1

      +
    4. +
    +
    +
  6. +
+
+
+
+
+

OcnRules module

+
+
+

Module Identifier: ocnrules

+
+
+

Type: Configuration Module

+
+
+

OCN Nodes provide an additional, optional OCPI module: OcnRules. This allows platforms to configure filtering +rules for their chosen OCN Node. Current filtering rules include: overriding OCN signature requirement preferences; +blacklisting and whitelisting. Note that the blacklist and whitelist cannot be active at the same time. If the +whitelist is active, all senders not on the whitelist will have their requests filtered by the OCN Node (they will not +reach the receiver). Likewise, if the blacklist is active, all requests will reach the platform except those from +senders specified on the blacklist.

+
+
+

The signature rules allows the default OCN signature header requirement of the OCN Node to be overruled by the receiving +platform. By default, the value will match that of the OCN Node. The platform can set signatures to be required even if +the OCN Node does not do so by default. Note that the other way is not possible - a platform cannot tell the OCN Node +to turn off signature verification.

+
+
+

Interfaces

+
+

The OcnRules module only specifies a receiver interface, implemented by the OCN Node itself. Connected platforms +use this interface to update rules on their given OCN Node.

+
+
+

Receiver Interface

+
+

Endpoint structure: /ocpi/receiver/2.2/ocnrules

+
+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodDescription

GET

Retrieve the full list of rules as stored on the platform’s OCN Node.

POST

Add a single rule entry, for example to the whitelist or blacklist.

PUT

Update a rule

PATCH

n/a

DELETE

Delete a single rule entry

+
+
+
GET Method
+
+

Used by a platform to check their rules as stored on their OCN Node.

+
+
+
Response Data
+
+

The response contains the requested object.

+
+ +++++ + + + + + + + + + + + + + + +
TypeCard.Description

OcnRules

1

The requested OcnRules object.

+
+
+
+
+
POST Method
+
+

New rules entries are updated by the platform on the OCN Node. Applies to whitelist and blacklist.

+
+
+
Request parameters
+
+

The following parameter can be provided as URL segment.

+
+ ++++++ + + + + + + + + + + + + + + + + +
ParameterTypeRequiredDescription

create_list_type

OcnRulesListType

yes

The type of list which the new entry applies to.

+
+
+
Request Body
+
+

Body is required if sending a POST request to whitelist or blacklist.

+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + +

Type

Card.

Description

country_code

CIString(2)

Country code of role to add to the specified list

party_id

CIString(3)

Party ID (Provider ID) of role to add to the specified list

WhiteListModules

*

The counterparty role and list of modules information to add to the specified list.

+
+
+
Example: add party to blacklist
+
+
+
POST to URL: https://node.ocn.org/ocpi/receiver/2.2/ocnrules/blacklist
+
+    {
+        "country_code": "DE",
+        "party_id": "ABC",
+        "modules": ["cdrs", "locations"]
+    }
+
+
+
+
+
+
+
PUT Method
+
+

Updates all rules entries for a platform on the OCN Node. Applies to signatures, whitelist and blacklist.

+
+ ++++++ + + + + + + + + + + + + + + + + +
ParameterTypeRequiredDescription

list_type

OcnRulesType

yes

The type of rule to update.

+
+
Request Body
+
+

Body is required if sending a PUT request to whitelist or blacklist. If an empty list is sent, the OCN Node will +interpret that as the deletion of all rules for the given list. Therefore, the specified list will be deactivated. It is also mandatory to set which specific module will be blocked/blacklisted for the specific sender. If an empty list of modules is sent then the sender is eligible to use all the modules. If +updating signatures, then no body is required.

+
+ +++++ + + + + + + + + + + + + + + + + + +

Type

Card.

Description

BasicRole

*

The counter-party role information to add to the specified list.

WhiteListModules

*

The counter-party role and list of modules information to add to the specified list.

+
+
+
Example: update whitelist
+
+
+
PUT to URL: https://node.ocn.org/ocpi/receiver/2.2/ocnrules/whitelist
+
+[
+  {
+    "country_code": "DE",
+    "party_id": "ABC",
+    "modules": ["cdrs", "locations"]
+  },
+  {
+    "country_code": "CH",
+    "party_id": "DEF",
+    "modules": ["sessions", "tokens"]
+  }
+]
+
+
+
+
+
Example: update signatures toggle
+
+

There is no request body required for this request. The value will be set to the opposite of the current value (true +or false).

+
+
+
+
PUT to URL: https://node.ocn.org/ocpi/receiver/2.2/ocnrules/signatures
+
+
+
+
+
+
+
DELETE Method
+
+

Removes a single entry from a rules list. Applies to the whitelist and blacklist.

+
+
+
Request parameters
+
+

The following parameters can be provided as URL segments.

+
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterTypeRequiredDescription

list_type

OcnRulesListType

yes

The type of list which the new entry applies to.

country_code

CIString(2)

yes

Country code of role to add to the specified list

party_id

CIString(3)

yes

Party ID (Provider ID) of role to add to the specified list

+
+
+
Example: remove party from blacklist
+
+
+
DELETE to URL: https://node.ocn.org/ocpi/receiver/2.2/ocnrules/blacklist/NL/XYZ
+
+
+
+
+
+
+
+

Object Description

+
+
OcnRules Object
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyTypeCard.Description

signatures

Boolean

1

States whether request signature verification is enabled.

whitelist

OcnRulesList

1

Whitelist rules.

blacklist

OcnRulesList

1

Blacklist rules.

+
+
+
+
+

Data Types

+
+
OcnRulesList class
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + +
PropertyTypeCard.Description

active

Boolean

1

States whether the list is active.

list

BasicRole

*

Roles in the list.

+
+
+
BasicRole class
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + +
PropertyTypeCard.Description

country_code

CIString(2)

1

Country code of role.

party_id

CIString(3)

1

Party ID (Provider ID) of role.

+
+
+
WhiteListModules class
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyTypeCard.Description

country_code

CIString(2)

1

Country code of role.

party_id

CIString(3)

1

Party ID (Provider ID) of role.

modules

ModuleID

*

A list of 0 or more

+
+
+
OcnRulesType Enum
+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + +
ValueDescription

signatures

Parties must include valid OCN Signature header in requests to receiver.

whitelist

List of parties allowed to send messages to receiver.

blacklist

List of parties not allowed to send messages to receiver.

block-all

Enable an empty whitelist, effectively blocking all incoming requests.

+
+
+
OcnRulesListType Enum
+ ++++ + + + + + + + + + + + + + + + + +
ValueDescription

whitelist

List of parties allowed to send messages to receiver.

blacklist

List of parties not allowed to send messages to receiver.

+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index e23654e..0000000 --- a/examples/README.md +++ /dev/null @@ -1,397 +0,0 @@ -# Open Charge Network (OCN) Tutorial - -The following examples will guide you through administration of an OCN Node, as well as basic Open Charge Point -Interface implementation guidelines to aid with development of eMobility Service Provider (eMSP) or Charge Point -Operator (CPO) backoffices. - -## Setup - -To follow and run the examples, there are a few dependencies that must first be met: - -#### 1. Run a Local Open Charging Network - -You will first need to install [Docker](https://docs.docker.com/install/) and [docker-compose](https://docs.docker.com/compose/install/) -on your chosen platform to run the local network. - -Next, ensure that the OCN Registry and Node repositories are cloned under the same parent directory: - -``` -git clone git@bitbucket.org:shareandcharge/ocn-registry.git -git clone git@bitbucket.org:shareandcharge/ocn-node.git -``` - -The directory structure should look like so: - -``` -- ocn/ - | - ocn-registry/ - | - ocn-node/ -``` - -Then change directory to `ocn-node` and run the network: - -``` -cd ocn-node -./gradlew -Pprofile=docker -x test -x asciidoctor build -docker-compose up -``` - -The first build will take a few minutes but subsequent `docker-compose up` commands will be much faster. Whilst the Docker -images are being built, you may look over the details of the network below, which may or may not mean anything depending -on prior knowledge of Ethereum blockchain technology. Don't worry if they do not, as they will be explained in the following -section when we walk through the example requests. - -- OCN Registry smart contract deployed on a local development Ethereum blockchain (ganache) - - address: `0x345cA3e014Aaf5dcA488057592ee47305D9B3e10` - - owner: `0x627306090abaB3A6e1400e9345bC60c78a8BEf57` -- A Wallet containing 20 addresses each pre-funded with 100 ETH - - mnemonic: `candy maple cake sugar pudding cream honey rich smooth crumble sweet treat` -- Reachable blockchain JSON RPC API - - provider: `http://localhost:8544` -- Two OCN Nodes connected to the same OCN Registry as above - - node 1 address: `http://localhost:8080` - - node 2 address: `http://localhost:8081` - - admin API keys for both nodes: `randomkey` - -Once the images are built and the containers are running, we will see information about the two nodes displayed -in stdout, for example its API key, wallet address and public URL. - -The node checks its own health endpoint on start to make sure it is configured correctly. We can also manually ensure -that both nodes are running, like so: - -``` -curl localhost:8080/health -curl localhost:8081/health -``` - -Both requests should return `200 OK`. Now leave the network running and open a new terminal session. - -#### 2. Clone the OCN Demo Repository and run the Mock servers - -The OCN Demo repository contains mock eMSP and CPO backoffices, as well as a script which will allow us to register -to the OCN Registry. Clone it by returning to the parent directory: - -``` -cd .. -git clone git@bitbucket.org:shareandcharge/ocn-demo.git -cd ocn-demo -npm install -``` - -The demo contains two Charge Point Operators (CPOs) and one eMobility Service Provider (EMSP). Starting the demo will -start the respective backends and register the CPOs to the Open Charging Network that we have running in the background: - -``` -npm start -``` - -Observe the requests that are made to ganache, the ocn-node and the CPO server. This gives a quick overview of how -the OCN registration process using OCPI and the OCN Registry looks. For those familiar with OCPI, you will see the -CPOs receiving versions and version detail requests, as the OCN node processes the credentials registration. - -Leave the servers running in the background, alongside the network. The next step is to install Postman so that we can -register our EMSP to the network. - -#### 3. Install Postman and Import Collection - -In order to visualize HTTP API requests, we can use [Postman](https://www.getpostman.com/). Once installed, simply -import the JSON collection and environment files provided in this directory. You will need to change your environment -to the provided "OCN Node" environment that you just imported, by selecting it from the dropdown menu in the top right -corner of Postman. This will allow you to easily change common variables across all your requests in the future. - - -## Tutorial - -In this tutorial, we will create an OCPI 2.2 connection with the OCN Node at [http:localhost:8080](http://localhost:8080). -If you completed the above step, there should be one CPO already registered with our OCN Node and another at -[http://localhost:8081](http://localhost:8081), which gives us a chance to make different requests across the network. - -### 1. Adding a Party to the OCN Registry - -Before we create a connection to an OCN Node, we must enter our party into the -[OCN Registry](https://bitbucket.org/shareandcharge/ocn-registry) to become visible on the network. To do this, we must -sign a transaction which states our party's country code and party ID as well as the public information of the OCN Node -we will connect to. The OCN Node information that we need is its Ethereum wallet address. - -Provided in the Postman collection is a request `GET Node Info` under the OCN directory. Note that there is no -authorization needed to make this request and the variable `{{NODE_URL}}` has been configured to `http://localhost:8080` -within the "OCN Node" environment that you imported. To change environment variables, click the eye symbol button next -to the dropdown menu where you selected the environment. The response should be the following: - -```json -{ - "url": "http://localhost:8080", - "address": "0x9bc1169ca09555bf2721a5c9ec6d69c8073bfeb4" -} -``` - -You might recall briefly seeing the same information printed to stdout when running the local OCN. As we are also -running our node, we already know this information. If connecting to a remote node, we can use this request to provide -the same information. Now that we have the node's wallet address, we can use the OCN Registry Command Line Interface (CLI) -to add our EMSP party to the Registry, linking it to the node on `http://localhost:8080`. As we already have the -registry repository, we can use the CLI from the source code directly: - -``` -cd ocn-registry -npm install -export SIGNER=0x49b2e2b48cfc25fda1d1cbdb2197b83902142c6da502dcf1871c628ea524f11b -npx ts-node src set-party -c DE MSP -r EMSP -o 0x9bc1169ca09555bf2721a5c9ec6d69c8073bfeb4 -``` - -Here, we are adding a party to the registry with OCPI credentials (`country_code` and `party_id`) DE MSP. Our wallet -(the signer) has address `0xcb0236B37Ff19001633E38808bd124b60B1fE1ba`. We have one role: EMSP. Our OCN Node operator has -the wallet address `0x9bc1169ca09555bf2721a5c9ec6d69c8073bfeb4` and has already listed their node in the registry. You -can validate node and party listings as follows: - -##### Get party by OCPI credentials or wallet address -``` -npx ts-node src get-party -c DE MSP -npx ts-node src get-party -a 0xcb0236B37Ff19001633E38808bd124b60B1fE1ba -``` - -##### Get node by operator address -``` -npx ts-node src get-node 0x9bc1169ca09555bf2721a5c9ec6d69c8073bfeb4 -``` - -### 2. Generating a CREDENTIALS_TOKEN_A - -Now that we have listed ourselves in the OCN Registry smart contract, we need to create a connection with our OCN -Node. - -In order to connect to an OCN Node, the administrator first must generate a token to be used in the OCPI credentials -registration flow. This, described as `CREDENTIALS_TOKEN_A`, will be used to obtain information about the OCN Node, -e.g. the version of OCPI it is using and the modules it incorporates. - -In Postman, simply go to the `GET Generate Registration Token` request in the Admin directory of the OCN Node -collection and hit Send to make the request. The authorization token (`{{ADMIN_API_KEY`}}) has already been declared in -the "OCN Node" environment. - -You should see the following response: -``` -{ - "token": {{CREDENTIALS_TOKEN_A}}, - "versions": "http://localhost:8080/ocpi/versions" -} -``` - -Taking the provided token, we can now set our environment variable, using the eye symbol button in Postman, as -described before. - -### 2. Request Versions and Version Details - -Our EMSP backend runs on OCPI 2.2. As such we would like to establish a connection to the OCN Node using the same -OCPI version. In Postman, navigate to the Versions directory and send the `GET versions` request. - -This is our first chance to see the OCPI JSON response format, containing the OCPI status code of the response, the -timestamp, and (optionally) any data returned. If the request is made with an incorrect token, the HTTP status code -will be 401 rather than 200, and the `data` field will be (optionally) replaced with `status_message`. - -The data field should provide an array of supported versions and a url that will provide information regarding how to -use this version. - -Do the same for the next request in the directory, for retrieving version details. The response will contain an array -of endpoints supported by the node. The one we are most interested in for now is the `credentials` module: - -```json -{ - "identifier": "credentials", - "role": "SENDER", - "url": "http://localhost:8080/ocpi/2.2/credentials" -} -``` - -### 3. Registering EMSP credentials - -Now that we know where to send our credentials, we can open the `POST credentials` request in the credentials directory. -This request contains a JSON body of our own credentials to be sent to the OCN Node. This includes the token that the -OCN Node should use to authorize itself on *our* server, should it need to forward a request to us from a CPO (i.e. if -the CPO is pushing session or location updates to us). We also provide a url to our versions endpoint, such that the -OCN Node can go through the same process we just did, in finding a common OCPI version and obtaining a list of the -endpoints we have. Lastly, we describe the roles that we employ. Notice how this is an array. OCPI 2.2 adds the ability -for a platform operating multiple roles to communicate on a single OCPI connection. Therefore a platform that is both an -EMSP and CPO needs not register twice. - -```json -{ - "status_code": 1000, - "data": { - "token": "ef4c3b29-5679-4bb9-8a59-4c53fc3dacef", - "url": "http://localhost:8080/ocpi/versions", - "roles": [ - { - "role": "HUB", - "business_details": { - "name": "Open Charging Network Node" - }, - "party_id": "OCN", - "country_code": "DE" - } - ] - }, - "timestamp": "2020-01-15T09:56:56.547Z" -} -``` - -Once sent, you should see the OCN Node's credentials returned to you. There is a new token in the body: this is what's -known as token C and will be used to authorize any subsequent OCPI requests you make to your OCN Node. -The previous token is now discarded and will not be used again, so make sure to save this new token under the -environment variable `CREDENTIALS_TOKEN_C`. - -In addition, you should see that there were two requests made to the EMSP server in the logs of the OCN demo. This -shows that the OCN Node has requested and stored your OCPI module endpoints for future use. - -This now completes the registration to the OCN Node. - -### 4. Making OCPI requests to a CPO - -#### Requesting location data - -Now that we have registered to our OCN Node, we can send requests to one of the registered CPOs on the OCN. In this -request, we wish to fetch a list of the CPO's locations (i.e. charging stations under OCPI terminology). To do so, -send the `GET locations list` request in the locations directory of the Postman collection. - -The result should look like the following: -```json -{ - "status_code": 1000, - "data": [ - { - "country_code": "DE", - "party_id": "CPO", - "id": "LOC1", - "publish": true, - "address": "somestreet 1", - "city": "Essen", - "country": "DEU", - "coordinates": { - "latitude": "52.232", - "longitude": "0.809" - }, - "evses": [ - { - "uid": "1234", - "status": "AVAILABLE", - "connectors": [ - { - "id": "1", - "standard": "IEC_62196_T2", - "format": "SOCKET", - "power_type": "AC_3_PHASE", - "max_voltage": 400, - "max_amperage": 32, - "tariff_ids": [ - "xxx-123" - ], - "last_updated": "2019-08-13T14:44:25.561Z" - } - ], - "last_updated": "2019-08-13T14:44:25.561Z" - }, - { - "uid": "4567", - "status": "RESERVED", - "connectors": [ - { - "id": "1", - "standard": "IEC_62196_T2", - "format": "SOCKET", - "power_type": "AC_3_PHASE", - "max_voltage": 400, - "max_amperage": 32, - "tariff_ids": [ - "xyz-456" - ], - "last_updated": "2019-08-13T14:44:25.561Z" - } - ], - "last_updated": "2019-08-13T14:44:25.561Z" - } - ], - "last_updated": "2019-08-13T14:44:25.561Z" - } - ], - "timestamp": "2019-08-13T15:25:24.435Z" -} -``` - -We see that the request was successfully processed, returning an array of a single location. The OCPI location data -type follows a hierarchy of `location` -> `evse` -> `connector`. We can make also make requests that fetch a single -location, or a specific EVSE or connector. Take a look at the other requests in the locations directory to see how they -work. - -#### A note on headers - -The headers prefixed with `OCPI-` describe the desired routing of the message. The `OCPI-from-country-code` and -`OCPI-from-party-id` describe the OCPI party on the platform which is making the request (in our case we only have one -party per platform, but it could be the case that a CPO and EMSP role share the same platform connection with an OCN -node). Likewise, the `OCPI-to-country-code` and `OCPI-to-party-id` headers describe the recipient of the request. In -our case we are making requests to a CPO with country code `DE` and party ID `CPO`. This CPO is registered to the same -OCN Node as our EMSP, but what if we want to contact a "remote" party connected to a different OCN Node? We can -try this out by changing the request headers to the following: - -``` -OCPI-to-country-code: NL -OCPI-to-party-id: CPX -``` - -Note also the `X-Request-ID` and `X-Correlation-ID` headers. They don't play a role in our demonstration (both being -set to `"1"` for all requests, but in production it is strongly advised to generate unique IDs (uuid version 4 preferred) -for all requests, in order to help with any potential debugging. The request ID is unique for every request: in -forwarding such a request: an OCN Node will actually set a new request ID when forwarding a message. The correlation -ID meanwhile, is unique for every request-response: an OCN Node will never change the correlation ID. - -#### OCPI module dependencies and implementations - -Let's check out the other request types we can make now. Notice how on the Connector object there is a `tariff_ids` array. -What does this mean? - -Navigate to the tariffs directory and send the `GET tariffs` request. You should see in the response's body an array of -tariffs provided by the CPO, with IDs matching those found on the Connector object. This is an example of a dependency -between OCPI modules. - -However, all OCPI modules aside from `credentials` are optional (for the purpose of the demo the -EMSP and CPOs have not implemented the `credentials` interface, but it is important to do so, as the OCN Node could -need to update its credentials on your system). It is up to the EMSP/CPO (or any other role) to implement the modules -themselves. Therefore if we try to make, for instance, a `sessions` request to the CPO, we might receive a message -telling us that the CPO has not implemented the module (yet). - -#### More complex requests - -Our EMSP has actually implemented two OCPI modules: `commands` and `cdrs`. The first of which is the `SENDER` interface -which allows the CPO to asynchronously notify the EMSP of a command result, i.e. a session has been started by a charge -point. The second allows the CPO to push Charge Detail Records (CDRs), containing the final price of a charging session, -to the EMSP. This reduces the load on the CPO backend as the EMSP doesn't need to poll for new CDRs. - -We can see this first hand by sending the requests in the commands directory. The first, `POST START_SESSION`, will -send a start session request on behalf of a driver on the EMSP system. For this request, we can also monitor the output -of our demo servers. The initial response from our Postman request contains only an acknowledgement of the request. -After 5 seconds the CPO will send the async command result (the response from the charge point): - -``` -CPO [DE CPO] sending async STOP_SESSION response -EMSP [DE MSP] received async command response: {"result":"ACCEPTED"} -EMSP [DE MSP] -- POST /ocpi/emsp/2.2/commands/STOP_SESSION/2 200 59 - 0.734 ms -``` - -Likewise, when we make a `POST STOP_SESSION` request, we see the following a further 5 seconds after the async response -has been sent to the EMSP: - -``` -CPO [DE CPO] sending cdr after session end -EMSP [DE MSP] -- POST /ocpi/emsp/2.2/cdrs 200 59 - 0.487 ms -EMSP [DE MSP] -- GET /ocpi/emsp/2.2/cdrs/1 200 1124 - 0.784 ms -CPO [DE CPO] acknowledges cdr correctly stored on EMSP system -``` - -In this case, the CPO has sent a POST `cdrs` request to the EMSP, the response of which will contain a `Location` header -set by the EMSP describing where the CPO can find this charge detail record on the EMSP system. The CPO can then make -a GET request to this location to verify that the CDR was stored correctly by the EMSP. - -#### Next steps - -That marks the end of this tutorial. More examples and use cases will be added to this tutorial in the future, but for -now this should be enough to get started on creating an OCPI 2.2 platform that is ready to join the Open Charging Network. - -See the [OCN Node HTTP API documentation](https://shareandcharge.bitbucket.io) for more requests, including a link to -the full OCPI API. diff --git a/examples/ocn-node.postman_collection.json b/examples/ocn-node.postman_collection.json deleted file mode 100644 index 336aad3..0000000 --- a/examples/ocn-node.postman_collection.json +++ /dev/null @@ -1,1603 +0,0 @@ -{ - "info": { - "_postman_id": "6f7988c3-32a1-4884-a6bc-9e63ae5702ba", - "name": "OCN Node", - "description": "The OCN Node collection walks admins and users through the various use cases of the OCN Node.\n\nIncluded is the following requests:\n\n- Admin API\n\t- generate registration token\n\n- OCN\n\t- read data from the OCN Registry\n\n- OCPI\n\t- view OCPI versions\n\t- view OCPI version details (module enpdoints)\n\t- POST (register) credentials\n\t- PUT (update) credentials\n\t- and more...", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" - }, - "item": [ - { - "name": "Admin", - "item": [ - { - "name": "Generate Registration Token", - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Token {{ADMIN_API_KEY}}", - "type": "text" - }, - { - "key": "Content-Type", - "name": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "[\n\t{\n\t\t\"country_code\": \"DE\",\n\t\t\"party_id\": \"MSP\"\n\t}\t\n]" - }, - "url": { - "raw": "{{NODE_URL}}/admin/generate-registration-token", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "admin", - "generate-registration-token" - ] - }, - "description": "Generate a CREDENTIALS_TOKEN_A for a planned OCPI connection." - }, - "response": [ - { - "name": "Generate Registration Token", - "originalRequest": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Token {{ADMIN_API_KEY}}", - "type": "text" - }, - { - "key": "Content-Type", - "name": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "[\n\t{\n\t\t\"country_code\": \"DE\",\n\t\t\"party_id\": \"MSP\"\n\t}\t\n]", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{NODE_URL}}/admin/generate-registration-token", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "admin", - "generate-registration-token" - ] - } - }, - "status": "OK", - "code": 200, - "_postman_previewlanguage": "json", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Transfer-Encoding", - "value": "chunked" - }, - { - "key": "Date", - "value": "Wed, 15 Jan 2020 10:21:31 GMT" - }, - { - "key": "Keep-Alive", - "value": "timeout=60" - }, - { - "key": "Connection", - "value": "keep-alive" - } - ], - "cookie": [], - "body": "{\n \"token\": \"493a7355-28f8-4575-ad73-a5b934b5ef25\",\n \"versions\": \"http://localhost:8080/ocpi/versions\"\n}" - } - ] - } - ], - "description": "Admin API requests", - "protocolProfileBehavior": {} - }, - { - "name": "OCN", - "item": [ - { - "name": "Node Info", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{NODE_URL}}/ocn/registry/node-info", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocn", - "registry", - "node-info" - ] - }, - "description": "Get Node Info on OCN." - }, - "response": [ - { - "name": "Node Info", - "originalRequest": { - "method": "GET", - "header": [], - "url": { - "raw": "{{NODE_URL}}/ocn/registry/node-info", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocn", - "registry", - "node-info" - ] - } - }, - "status": "OK", - "code": 200, - "_postman_previewlanguage": "json", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Transfer-Encoding", - "value": "chunked" - }, - { - "key": "Date", - "value": "Wed, 15 Jan 2020 10:23:07 GMT" - }, - { - "key": "Keep-Alive", - "value": "timeout=60" - }, - { - "key": "Connection", - "value": "keep-alive" - } - ], - "cookie": [], - "body": "{\n \"url\": \"http://localhost:8080\",\n \"address\": \"0xc98a48f2e7895a4bcde9f9190eb5f4eb72e92e1a\"\n}" - } - ] - } - ], - "description": "OCN specific requests (i.e. getting node information, getting registry information)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "3b8ad01f-39cd-4931-adef-da013a0722e8", - "type": "text/javascript", - "exec": [ - "" - ] - } - }, - { - "listen": "test", - "script": { - "id": "0e3ec999-9f50-451a-800f-0c2451727c7e", - "type": "text/javascript", - "exec": [ - "" - ] - } - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "OCPI", - "item": [ - { - "name": "Versions", - "item": [ - { - "name": "Versions", - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Token {{CREDENTIALS_TOKEN_A}}", - "type": "text" - } - ], - "url": { - "raw": "{{NODE_URL}}/ocpi/versions", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocpi", - "versions" - ] - }, - "description": "Get all versions supported by the OCN Node" - }, - "response": [ - { - "name": "Versions", - "originalRequest": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Token {{CREDENTIALS_TOKEN_A}}", - "type": "text" - } - ], - "url": { - "raw": "{{NODE_URL}}/ocpi/versions", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocpi", - "versions" - ] - } - }, - "status": "OK", - "code": 200, - "_postman_previewlanguage": "json", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Transfer-Encoding", - "value": "chunked" - }, - { - "key": "Date", - "value": "Wed, 15 Jan 2020 10:23:58 GMT" - }, - { - "key": "Keep-Alive", - "value": "timeout=60" - }, - { - "key": "Connection", - "value": "keep-alive" - } - ], - "cookie": [], - "body": "{\n \"status_code\": 1000,\n \"data\": {\n \"versions\": [\n {\n \"version\": \"2.2\",\n \"url\": \"http://localhost:8080/ocpi/2.2\"\n }\n ]\n },\n \"timestamp\": \"2020-01-15T10:23:58.103Z\"\n}" - } - ] - }, - { - "name": "Version Details", - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Token {{CREDENTIALS_TOKEN_A}}", - "type": "text" - } - ], - "url": { - "raw": "{{NODE_URL}}/ocpi/2.2", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocpi", - "2.2" - ] - }, - "description": "See 2.2 module interfaces of the OCN Node." - }, - "response": [ - { - "name": "Version Details", - "originalRequest": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Token {{CREDENTIALS_TOKEN_A}}", - "type": "text" - } - ], - "url": { - "raw": "{{NODE_URL}}/ocpi/2.2", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocpi", - "2.2" - ] - } - }, - "status": "OK", - "code": 200, - "_postman_previewlanguage": "json", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Transfer-Encoding", - "value": "chunked" - }, - { - "key": "Date", - "value": "Wed, 15 Jan 2020 10:24:32 GMT" - }, - { - "key": "Keep-Alive", - "value": "timeout=60" - }, - { - "key": "Connection", - "value": "keep-alive" - } - ], - "cookie": [], - "body": "{\n \"status_code\": 1000,\n \"data\": {\n \"version\": \"2.2\",\n \"endpoints\": [\n {\n \"identifier\": \"cdrs\",\n \"role\": \"SENDER\",\n \"url\": \"http://localhost:8080/ocpi/sender/2.2/cdrs\"\n },\n {\n \"identifier\": \"cdrs\",\n \"role\": \"RECEIVER\",\n \"url\": \"http://localhost:8080/ocpi/receiver/2.2/cdrs\"\n },\n {\n \"identifier\": \"commands\",\n \"role\": \"SENDER\",\n \"url\": \"http://localhost:8080/ocpi/sender/2.2/commands\"\n },\n {\n \"identifier\": \"commands\",\n \"role\": \"RECEIVER\",\n \"url\": \"http://localhost:8080/ocpi/receiver/2.2/commands\"\n },\n {\n \"identifier\": \"credentials\",\n \"role\": \"SENDER\",\n \"url\": \"http://localhost:8080/ocpi/2.2/credentials\"\n },\n {\n \"identifier\": \"hubclientinfo\",\n \"role\": \"SENDER\",\n \"url\": \"http://localhost:8080/ocpi/2.2/hubclientinfo\"\n },\n {\n \"identifier\": \"locations\",\n \"role\": \"SENDER\",\n \"url\": \"http://localhost:8080/ocpi/sender/2.2/locations\"\n },\n {\n \"identifier\": \"locations\",\n \"role\": \"RECEIVER\",\n \"url\": \"http://localhost:8080/ocpi/receiver/2.2/locations\"\n },\n {\n \"identifier\": \"sessions\",\n \"role\": \"SENDER\",\n \"url\": \"http://localhost:8080/ocpi/sender/2.2/sessions\"\n },\n {\n \"identifier\": \"sessions\",\n \"role\": \"RECEIVER\",\n \"url\": \"http://localhost:8080/ocpi/receiver/2.2/sessions\"\n },\n {\n \"identifier\": \"tariffs\",\n \"role\": \"SENDER\",\n \"url\": \"http://localhost:8080/ocpi/sender/2.2/tariffs\"\n },\n {\n \"identifier\": \"tariffs\",\n \"role\": \"RECEIVER\",\n \"url\": \"http://localhost:8080/ocpi/receiver/2.2/tariffs\"\n },\n {\n \"identifier\": \"tokens\",\n \"role\": \"SENDER\",\n \"url\": \"http://localhost:8080/ocpi/sender/2.2/tokens\"\n },\n {\n \"identifier\": \"tokens\",\n \"role\": \"RECEIVER\",\n \"url\": \"http://localhost:8080/ocpi/receiver/2.2/tokens\"\n }\n ]\n },\n \"timestamp\": \"2020-01-15T10:24:32.091Z\"\n}" - } - ] - } - ], - "description": "OCPI Versions module example requests.\n\nSee [Versions documentation](https://github.com/ocpi/ocpi/blob/develop/version_information_endpoint.asciidoc) for more.", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "287ee307-a40f-4ca1-86df-620f57cd2078", - "type": "text/javascript", - "exec": [ - "" - ] - } - }, - { - "listen": "test", - "script": { - "id": "485b3acd-5326-44ba-8f6b-3213f3ebaf82", - "type": "text/javascript", - "exec": [ - "" - ] - } - } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true - }, - { - "name": "Credentials", - "item": [ - { - "name": "Credentials", - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Token {{CREDENTIALS_TOKEN_A}}", - "type": "text" - }, - { - "key": "Content-Type", - "name": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"token\": \"abc-123\",\n\t\"url\": \"http://localhost:3002/ocpi/versions\",\n\t\"roles\": [{\n\t\t\"country_code\": \"DE\",\n\t\t\"party_id\": \"MSP\",\n\t\t\"role\": \"EMSP\",\n\t\t\"business_details\": {\n\t\t\t\"name\": \"Test MSP\"\n\t\t}\n\t}]\n}" - }, - "url": { - "raw": "{{NODE_URL}}/ocpi/2.2/credentials", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocpi", - "2.2", - "credentials" - ] - }, - "description": "Register an EMSP role on the OCN Node." - }, - "response": [ - { - "name": "Credentials", - "originalRequest": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Token {{CREDENTIALS_TOKEN_A}}", - "type": "text" - }, - { - "key": "Content-Type", - "name": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"token\": \"abc-123\",\n\t\"url\": \"http://localhost:3002/ocpi/versions\",\n\t\"roles\": [{\n\t\t\"country_code\": \"DE\",\n\t\t\"party_id\": \"MSP\",\n\t\t\"role\": \"EMSP\",\n\t\t\"business_details\": {\n\t\t\t\"name\": \"Test MSP\"\n\t\t}\n\t}]\n}" - }, - "url": { - "raw": "{{NODE_URL}}/ocpi/2.2/credentials", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocpi", - "2.2", - "credentials" - ] - } - }, - "status": "OK", - "code": 200, - "_postman_previewlanguage": "json", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Transfer-Encoding", - "value": "chunked" - }, - { - "key": "Date", - "value": "Wed, 15 Jan 2020 09:56:56 GMT" - }, - { - "key": "Keep-Alive", - "value": "timeout=60" - }, - { - "key": "Connection", - "value": "keep-alive" - } - ], - "cookie": [], - "body": "{\n \"status_code\": 1000,\n \"data\": {\n \"token\": \"ef4c3b29-5679-4bb9-8a59-4c53fc3dacef\",\n \"url\": \"http://localhost:8080/ocpi/versions\",\n \"roles\": [\n {\n \"role\": \"HUB\",\n \"business_details\": {\n \"name\": \"Open Charging Network Node\"\n },\n \"party_id\": \"OCN\",\n \"country_code\": \"DE\"\n }\n ]\n },\n \"timestamp\": \"2020-01-15T09:56:56.547Z\"\n}" - } - ] - } - ], - "description": "Example OCPI credentials requests to the OCN Node.\n\nCheck [the OCPI credentials documentation](https://github.com/ocpi/ocpi/blob/develop/credentials.asciidoc) for more, which also includes a diagram of the registration process.", - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true - }, - { - "name": "Locations", - "item": [ - { - "name": "Location List", - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Token {{CREDENTIALS_TOKEN_C}}", - "type": "text" - }, - { - "key": "X-Request-ID", - "value": "1", - "type": "text" - }, - { - "key": "X-Correlation-ID", - "value": "1", - "type": "text" - }, - { - "key": "OCPI-from-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-from-party-id", - "value": "MSP", - "type": "text" - }, - { - "key": "OCPI-to-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-to-party-id", - "value": "CPO", - "type": "text" - } - ], - "url": { - "raw": "{{NODE_URL}}/ocpi/sender/2.2/locations", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocpi", - "sender", - "2.2", - "locations" - ] - }, - "description": "Request a list of locations from a CPO." - }, - "response": [ - { - "name": "Location List", - "originalRequest": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Token {{CREDENTIALS_TOKEN_C}}", - "type": "text" - }, - { - "key": "X-Request-ID", - "value": "1", - "type": "text" - }, - { - "key": "X-Correlation-ID", - "value": "1", - "type": "text" - }, - { - "key": "OCPI-from-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-from-party-id", - "value": "MSP", - "type": "text" - }, - { - "key": "OCPI-to-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-to-party-id", - "value": "CPO", - "type": "text" - } - ], - "url": { - "raw": "{{NODE_URL}}/ocpi/sender/2.2/locations", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocpi", - "sender", - "2.2", - "locations" - ] - } - }, - "status": "OK", - "code": 200, - "_postman_previewlanguage": "json", - "header": [ - { - "key": "Link", - "value": "http://localhost:8080/ocpi/sender/2.2/locations/page/12; rel=\"next\"" - }, - { - "key": "X-Total-Count", - "value": "1" - }, - { - "key": "X-Limit", - "value": "1" - }, - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Transfer-Encoding", - "value": "chunked" - }, - { - "key": "Date", - "value": "Wed, 15 Jan 2020 09:58:37 GMT" - }, - { - "key": "Keep-Alive", - "value": "timeout=60" - }, - { - "key": "Connection", - "value": "keep-alive" - } - ], - "cookie": [], - "body": "{\n \"status_code\": 1000,\n \"data\": [\n {\n \"country_code\": \"DE\",\n \"party_id\": \"CPO\",\n \"id\": \"LOC1\",\n \"type\": \"ON_STREET\",\n \"address\": \"somestreet 1\",\n \"city\": \"Essen\",\n \"postal_code\": \"45131\",\n \"country\": \"DEU\",\n \"coordinates\": {\n \"latitude\": \"52.232\",\n \"longitude\": \"0.809\"\n },\n \"evses\": [\n {\n \"uid\": \"1234\",\n \"evse_id\": \"XX-1234-YY\",\n \"status\": \"AVAILABLE\",\n \"connectors\": [\n {\n \"id\": \"1\",\n \"standard\": \"IEC_62196_T2\",\n \"format\": \"SOCKET\",\n \"power_type\": \"AC_3_PHASE\",\n \"max_voltage\": 400,\n \"max_amperage\": 32,\n \"tariff_ids\": [\n \"xxx-123\"\n ],\n \"last_updated\": \"2019-08-13T14:44:25.561Z\"\n }\n ],\n \"last_updated\": \"2019-08-13T14:44:25.561Z\"\n },\n {\n \"uid\": \"4567\",\n \"evse_id\": \"XX-4567-YY\",\n \"status\": \"RESERVED\",\n \"connectors\": [\n {\n \"id\": \"1\",\n \"standard\": \"IEC_62196_T2\",\n \"format\": \"SOCKET\",\n \"power_type\": \"AC_3_PHASE\",\n \"max_voltage\": 400,\n \"max_amperage\": 32,\n \"tariff_ids\": [\n \"xyz-456\"\n ],\n \"last_updated\": \"2019-08-13T14:44:25.561Z\"\n }\n ],\n \"last_updated\": \"2019-08-13T14:44:25.561Z\"\n }\n ],\n \"last_updated\": \"2019-08-13T14:44:25.561Z\"\n }\n ],\n \"timestamp\": \"2020-01-15T09:58:37.490Z\"\n}" - } - ] - }, - { - "name": "Location Object", - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Token {{CREDENTIALS_TOKEN_C}}", - "type": "text" - }, - { - "key": "X-Request-ID", - "value": "1", - "type": "text" - }, - { - "key": "X-Correlation-ID", - "value": "1", - "type": "text" - }, - { - "key": "OCPI-from-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-from-party-id", - "value": "MSP", - "type": "text" - }, - { - "key": "OCPI-to-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-to-party-id", - "value": "CPO", - "type": "text" - } - ], - "url": { - "raw": "{{NODE_URL}}/ocpi/sender/2.2/locations/LOC1", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocpi", - "sender", - "2.2", - "locations", - "LOC1" - ] - }, - "description": "Request a single location object from a CPO." - }, - "response": [ - { - "name": "Location Object", - "originalRequest": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Token {{CREDENTIALS_TOKEN_C}}", - "type": "text" - }, - { - "key": "X-Request-ID", - "value": "1", - "type": "text" - }, - { - "key": "X-Correlation-ID", - "value": "1", - "type": "text" - }, - { - "key": "OCPI-from-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-from-party-id", - "value": "MSP", - "type": "text" - }, - { - "key": "OCPI-to-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-to-party-id", - "value": "CPO", - "type": "text" - } - ], - "url": { - "raw": "{{NODE_URL}}/ocpi/sender/2.2/locations/LOC1", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocpi", - "sender", - "2.2", - "locations", - "LOC1" - ] - } - }, - "status": "OK", - "code": 200, - "_postman_previewlanguage": "json", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Transfer-Encoding", - "value": "chunked" - }, - { - "key": "Date", - "value": "Wed, 15 Jan 2020 10:26:06 GMT" - }, - { - "key": "Keep-Alive", - "value": "timeout=60" - }, - { - "key": "Connection", - "value": "keep-alive" - } - ], - "cookie": [], - "body": "{\n \"status_code\": 1000,\n \"data\": {\n \"country_code\": \"DE\",\n \"party_id\": \"CPO\",\n \"id\": \"LOC1\",\n \"type\": \"ON_STREET\",\n \"address\": \"somestreet 1\",\n \"city\": \"Essen\",\n \"postal_code\": \"45131\",\n \"country\": \"DEU\",\n \"coordinates\": {\n \"latitude\": \"52.232\",\n \"longitude\": \"0.809\"\n },\n \"evses\": [\n {\n \"uid\": \"1234\",\n \"evse_id\": \"XX-1234-YY\",\n \"status\": \"AVAILABLE\",\n \"connectors\": [\n {\n \"id\": \"1\",\n \"standard\": \"IEC_62196_T2\",\n \"format\": \"SOCKET\",\n \"power_type\": \"AC_3_PHASE\",\n \"max_voltage\": 400,\n \"max_amperage\": 32,\n \"tariff_ids\": [\n \"xxx-123\"\n ],\n \"last_updated\": \"2019-08-13T14:44:25.561Z\"\n }\n ],\n \"last_updated\": \"2019-08-13T14:44:25.561Z\"\n },\n {\n \"uid\": \"4567\",\n \"evse_id\": \"XX-4567-YY\",\n \"status\": \"RESERVED\",\n \"connectors\": [\n {\n \"id\": \"1\",\n \"standard\": \"IEC_62196_T2\",\n \"format\": \"SOCKET\",\n \"power_type\": \"AC_3_PHASE\",\n \"max_voltage\": 400,\n \"max_amperage\": 32,\n \"tariff_ids\": [\n \"xyz-456\"\n ],\n \"last_updated\": \"2019-08-13T14:44:25.561Z\"\n }\n ],\n \"last_updated\": \"2019-08-13T14:44:25.561Z\"\n }\n ],\n \"last_updated\": \"2019-08-13T14:44:25.561Z\"\n },\n \"timestamp\": \"2020-01-15T10:26:06.657Z\"\n}" - } - ] - }, - { - "name": "EVSE", - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Token {{CREDENTIALS_TOKEN_C}}", - "type": "text" - }, - { - "key": "X-Request-ID", - "value": "1", - "type": "text" - }, - { - "key": "X-Correlation-ID", - "value": "1", - "type": "text" - }, - { - "key": "OCPI-from-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-from-party-id", - "value": "MSP", - "type": "text" - }, - { - "key": "OCPI-to-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-to-party-id", - "value": "CPO", - "type": "text" - } - ], - "url": { - "raw": "{{NODE_URL}}/ocpi/sender/2.2/locations/LOC1/1234", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocpi", - "sender", - "2.2", - "locations", - "LOC1", - "1234" - ] - }, - "description": "Request a specific EVSE from a CPO." - }, - "response": [ - { - "name": "EVSE", - "originalRequest": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Token {{CREDENTIALS_TOKEN_C}}", - "type": "text" - }, - { - "key": "X-Request-ID", - "value": "1", - "type": "text" - }, - { - "key": "X-Correlation-ID", - "value": "1", - "type": "text" - }, - { - "key": "OCPI-from-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-from-party-id", - "value": "MSP", - "type": "text" - }, - { - "key": "OCPI-to-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-to-party-id", - "value": "CPO", - "type": "text" - } - ], - "url": { - "raw": "{{NODE_URL}}/ocpi/sender/2.2/locations/LOC1/1234", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocpi", - "sender", - "2.2", - "locations", - "LOC1", - "1234" - ] - } - }, - "status": "OK", - "code": 200, - "_postman_previewlanguage": "json", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Transfer-Encoding", - "value": "chunked" - }, - { - "key": "Date", - "value": "Wed, 15 Jan 2020 10:26:35 GMT" - }, - { - "key": "Keep-Alive", - "value": "timeout=60" - }, - { - "key": "Connection", - "value": "keep-alive" - } - ], - "cookie": [], - "body": "{\n \"status_code\": 1000,\n \"data\": {\n \"uid\": \"1234\",\n \"evse_id\": \"XX-1234-YY\",\n \"status\": \"AVAILABLE\",\n \"connectors\": [\n {\n \"id\": \"1\",\n \"standard\": \"IEC_62196_T2\",\n \"format\": \"SOCKET\",\n \"power_type\": \"AC_3_PHASE\",\n \"max_voltage\": 400,\n \"max_amperage\": 32,\n \"tariff_ids\": [\n \"xxx-123\"\n ],\n \"last_updated\": \"2019-08-13T14:44:25.561Z\"\n }\n ],\n \"last_updated\": \"2019-08-13T14:44:25.561Z\"\n },\n \"timestamp\": \"2020-01-15T10:26:35.951Z\"\n}" - } - ] - }, - { - "name": "Connector", - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Token {{CREDENTIALS_TOKEN_C}}", - "type": "text" - }, - { - "key": "X-Request-ID", - "value": "1", - "type": "text" - }, - { - "key": "X-Correlation-ID", - "value": "1", - "type": "text" - }, - { - "key": "OCPI-from-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-from-party-id", - "value": "MSP", - "type": "text" - }, - { - "key": "OCPI-to-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-to-party-id", - "value": "CPO", - "type": "text" - } - ], - "url": { - "raw": "{{NODE_URL}}/ocpi/sender/2.2/locations/LOC1/1234/1", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocpi", - "sender", - "2.2", - "locations", - "LOC1", - "1234", - "1" - ] - }, - "description": "Request a specific connector from a CPO." - }, - "response": [ - { - "name": "Connector", - "originalRequest": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Token {{CREDENTIALS_TOKEN_C}}", - "type": "text" - }, - { - "key": "X-Request-ID", - "value": "1", - "type": "text" - }, - { - "key": "X-Correlation-ID", - "value": "1", - "type": "text" - }, - { - "key": "OCPI-from-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-from-party-id", - "value": "MSP", - "type": "text" - }, - { - "key": "OCPI-to-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-to-party-id", - "value": "CPO", - "type": "text" - } - ], - "url": { - "raw": "{{NODE_URL}}/ocpi/sender/2.2/locations/LOC1/1234/1", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocpi", - "sender", - "2.2", - "locations", - "LOC1", - "1234", - "1" - ] - } - }, - "status": "OK", - "code": 200, - "_postman_previewlanguage": "json", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Transfer-Encoding", - "value": "chunked" - }, - { - "key": "Date", - "value": "Wed, 15 Jan 2020 10:27:06 GMT" - }, - { - "key": "Keep-Alive", - "value": "timeout=60" - }, - { - "key": "Connection", - "value": "keep-alive" - } - ], - "cookie": [], - "body": "{\n \"status_code\": 1000,\n \"data\": {\n \"id\": \"1\",\n \"standard\": \"IEC_62196_T2\",\n \"format\": \"SOCKET\",\n \"power_type\": \"AC_3_PHASE\",\n \"max_voltage\": 400,\n \"max_amperage\": 32,\n \"tariff_ids\": [\n \"xxx-123\"\n ],\n \"last_updated\": \"2019-08-13T14:44:25.561Z\"\n },\n \"timestamp\": \"2020-01-15T10:27:06.103Z\"\n}" - } - ] - } - ], - "description": "OCPI locations module request examples.\n\nSee [locations documentation](https://github.com/ocpi/ocpi/blob/develop/mod_locations.asciidoc) for more. ", - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true - }, - { - "name": "Tariffs", - "item": [ - { - "name": "Tariffs", - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Token {{CREDENTIALS_TOKEN_C}}", - "type": "text" - }, - { - "key": "X-Request-ID", - "value": "1", - "type": "text" - }, - { - "key": "X-Correlation-ID", - "value": "1", - "type": "text" - }, - { - "key": "OCPI-from-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-from-party-id", - "value": "MSP", - "type": "text" - }, - { - "key": "OCPI-to-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-to-party-id", - "value": "CPO", - "type": "text" - } - ], - "url": { - "raw": "{{NODE_URL}}/ocpi/sender/2.2/tariffs", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocpi", - "sender", - "2.2", - "tariffs" - ] - }, - "description": "Example tariffs request from EMSP to CPO." - }, - "response": [ - { - "name": "Tariffs", - "originalRequest": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Token {{CREDENTIALS_TOKEN_C}}", - "type": "text" - }, - { - "key": "X-Request-ID", - "value": "1", - "type": "text" - }, - { - "key": "X-Correlation-ID", - "value": "1", - "type": "text" - }, - { - "key": "OCPI-from-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-from-party-id", - "value": "MSP", - "type": "text" - }, - { - "key": "OCPI-to-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-to-party-id", - "value": "CPO", - "type": "text" - } - ], - "url": { - "raw": "{{NODE_URL}}/ocpi/sender/2.2/tariffs", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocpi", - "sender", - "2.2", - "tariffs" - ] - } - }, - "status": "OK", - "code": 200, - "_postman_previewlanguage": "json", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Transfer-Encoding", - "value": "chunked" - }, - { - "key": "Date", - "value": "Wed, 15 Jan 2020 10:27:27 GMT" - }, - { - "key": "Keep-Alive", - "value": "timeout=60" - }, - { - "key": "Connection", - "value": "keep-alive" - } - ], - "cookie": [], - "body": "{\n \"status_code\": 1000,\n \"data\": [\n {\n \"country_code\": \"DE\",\n \"party_id\": \"CPO\",\n \"id\": \"xxx-123\",\n \"currency\": \"EUR\",\n \"type\": \"REGULAR\",\n \"elements\": [\n {\n \"price_components\": [\n {\n \"type\": \"FLAT\",\n \"price\": 2.5,\n \"step_size\": 1\n }\n ]\n }\n ],\n \"last_updated\": \"2019-08-13T14:46:18.903Z\"\n },\n {\n \"country_code\": \"DE\",\n \"party_id\": \"CPO\",\n \"id\": \"xyz-456\",\n \"currency\": \"EUR\",\n \"type\": \"REGULAR\",\n \"elements\": [\n {\n \"price_components\": [\n {\n \"type\": \"FLAT\",\n \"price\": 1.2,\n \"step_size\": 1\n },\n {\n \"type\": \"TIME\",\n \"price\": 0.3,\n \"step_size\": 1000\n }\n ]\n }\n ],\n \"last_updated\": \"2019-08-13T14:46:18.903Z\"\n }\n ],\n \"timestamp\": \"2020-01-15T10:27:27.235Z\"\n}" - } - ] - } - ], - "description": "OCPI Tariffs example requests.\n\n", - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true - }, - { - "name": "Commands", - "item": [ - { - "name": "START_SESSION", - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Token {{CREDENTIALS_TOKEN_C}}", - "type": "text" - }, - { - "key": "X-Request-ID", - "value": "1", - "type": "text" - }, - { - "key": "X-Correlation-ID", - "value": "1", - "type": "text" - }, - { - "key": "OCPI-from-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-from-party-id", - "value": "MSP", - "type": "text" - }, - { - "key": "OCPI-to-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-to-party-id", - "value": "CPO", - "type": "text" - }, - { - "key": "Content-Type", - "name": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"response_url\": \"http://localhost:3002/ocpi/emsp/2.2/commands/START_SESSION/1\",\n\t\"token\": {\n\t\t\"country_code\": \"DE\",\n\t\t\"party_id\": \"MSP\",\n\t\t\"uid\": \"0102030405\",\n\t\t\"type\": \"APP_USER\",\n\t\t\"contract_id\": \"XX-12345\",\n\t\t\"issuer\": \"Test MSP\",\n\t\t\"valid\": true,\n\t\t\"whitelist\": \"ALWAYS\",\n\t\t\"last_updated\": \"2019-08-13T14:44:25.561Z\"\n\t},\n\t\"location_id\": \"LOC1\"\n}" - }, - "url": { - "raw": "{{NODE_URL}}/ocpi/receiver/2.2/commands/START_SESSION", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocpi", - "receiver", - "2.2", - "commands", - "START_SESSION" - ] - }, - "description": "Send a request to the CPO to start session for a driver." - }, - "response": [ - { - "name": "START_SESSION", - "originalRequest": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Token {{CREDENTIALS_TOKEN_C}}", - "type": "text" - }, - { - "key": "X-Request-ID", - "value": "1", - "type": "text" - }, - { - "key": "X-Correlation-ID", - "value": "1", - "type": "text" - }, - { - "key": "OCPI-from-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-from-party-id", - "value": "MSP", - "type": "text" - }, - { - "key": "OCPI-to-country-code", - "value": "DE", - "type": "text" - }, - { - "key": "OCPI-to-party-id", - "value": "CPO", - "type": "text" - }, - { - "key": "Content-Type", - "name": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"response_url\": \"http://localhost:3002/ocpi/emsp/2.2/commands/START_SESSION/1\",\n\t\"token\": {\n\t\t\"country_code\": \"DE\",\n\t\t\"party_id\": \"MSP\",\n\t\t\"uid\": \"0102030405\",\n\t\t\"type\": \"APP_USER\",\n\t\t\"contract_id\": \"XX-12345\",\n\t\t\"issuer\": \"Test MSP\",\n\t\t\"valid\": true,\n\t\t\"whitelist\": \"ALWAYS\",\n\t\t\"last_updated\": \"2019-08-13T14:44:25.561Z\"\n\t},\n\t\"location_id\": \"LOC1\"\n}" - }, - "url": { - "raw": "{{NODE_URL}}/ocpi/receiver/2.2/commands/START_SESSION", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocpi", - "receiver", - "2.2", - "commands", - "START_SESSION" - ] - } - }, - "status": "OK", - "code": 200, - "_postman_previewlanguage": "json", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Transfer-Encoding", - "value": "chunked" - }, - { - "key": "Date", - "value": "Wed, 15 Jan 2020 10:27:45 GMT" - }, - { - "key": "Keep-Alive", - "value": "timeout=60" - }, - { - "key": "Connection", - "value": "keep-alive" - } - ], - "cookie": [], - "body": "{\n \"status_code\": 1000,\n \"data\": {\n \"result\": \"ACCEPTED\",\n \"timeout\": 20\n },\n \"timestamp\": \"2020-01-15T10:27:45.229Z\"\n}" - } - ] - }, - { - "name": "STOP_SESSION", - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Token {{CREDENTIALS_TOKEN_C}}" - }, - { - "key": "X-Request-ID", - "type": "text", - "value": "1" - }, - { - "key": "X-Correlation-ID", - "type": "text", - "value": "1" - }, - { - "key": "OCPI-from-country-code", - "type": "text", - "value": "DE" - }, - { - "key": "OCPI-from-party-id", - "type": "text", - "value": "MSP" - }, - { - "key": "OCPI-to-country-code", - "type": "text", - "value": "NL" - }, - { - "key": "OCPI-to-party-id", - "type": "text", - "value": "CPX" - }, - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"response_url\": \"http://localhost:3002/ocpi/emsp/2.2/commands/STOP_SESSION/2\",\n\t\"session_id\": \"xyzxyzyxyz\"\n}" - }, - "url": { - "raw": "{{NODE_URL}}/ocpi/receiver/2.2/commands/STOP_SESSION", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocpi", - "receiver", - "2.2", - "commands", - "STOP_SESSION" - ] - }, - "description": "Send a request to the CPO to start session for a driver." - }, - "response": [ - { - "name": "STOP_SESSION", - "originalRequest": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Token {{CREDENTIALS_TOKEN_C}}" - }, - { - "key": "X-Request-ID", - "type": "text", - "value": "1" - }, - { - "key": "X-Correlation-ID", - "type": "text", - "value": "1" - }, - { - "key": "OCPI-from-country-code", - "type": "text", - "value": "DE" - }, - { - "key": "OCPI-from-party-id", - "type": "text", - "value": "MSP" - }, - { - "key": "OCPI-to-country-code", - "type": "text", - "value": "DE" - }, - { - "key": "OCPI-to-party-id", - "type": "text", - "value": "CPO" - }, - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"response_url\": \"http://localhost:3002/ocpi/emsp/2.2/commands/STOP_SESSION/2\",\n\t\"session_id\": \"xyzxyzyxyz\"\n}" - }, - "url": { - "raw": "{{NODE_URL}}/ocpi/receiver/2.2/commands/STOP_SESSION", - "host": [ - "{{NODE_URL}}" - ], - "path": [ - "ocpi", - "receiver", - "2.2", - "commands", - "STOP_SESSION" - ] - } - }, - "status": "OK", - "code": 200, - "_postman_previewlanguage": "json", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Transfer-Encoding", - "value": "chunked" - }, - { - "key": "Date", - "value": "Wed, 15 Jan 2020 10:18:40 GMT" - }, - { - "key": "Keep-Alive", - "value": "timeout=60" - }, - { - "key": "Connection", - "value": "keep-alive" - } - ], - "cookie": [], - "body": "{\n \"status_code\": 1000,\n \"data\": {\n \"result\": \"ACCEPTED\",\n \"timeout\": 20\n },\n \"timestamp\": \"2020-01-15T10:18:40.595Z\"\n}" - } - ] - } - ], - "description": "OCPI commands module requests", - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true - } - ], - "description": "OCPI 2.2 request examples to the OCN Node.\n\nSee [OCPI 2.2 documentation](https://github.com/ocpi/ocpi/tree/develop) for more.", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "9ced7075-5bf5-439e-ad13-e79929314081", - "type": "text/javascript", - "exec": [ - "" - ] - } - }, - { - "listen": "test", - "script": { - "id": "30c2c5aa-0ad6-4d42-9013-3e0dd9ec0ddb", - "type": "text/javascript", - "exec": [ - "" - ] - } - } - ], - "protocolProfileBehavior": {} - } - ], - "protocolProfileBehavior": {} -} \ No newline at end of file diff --git a/examples/ocn-node.postman_environment.json b/examples/ocn-node.postman_environment.json deleted file mode 100644 index 3fdba68..0000000 --- a/examples/ocn-node.postman_environment.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "id": "3ee11a88-66da-4b01-8a1c-f5902319024a", - "name": "OCN Node", - "values": [ - { - "key": "CREDENTIALS_TOKEN_A", - "value": "", - "enabled": true - }, - { - "key": "CREDENTIALS_TOKEN_C", - "value": "", - "enabled": true - }, - { - "key": "NODE_URL", - "value": "http://localhost:8080", - "enabled": true - }, - { - "key": "ADMIN_API_KEY", - "value": "randomkey", - "enabled": true - } - ], - "_postman_variable_scope": "environment", - "_postman_exported_at": "2020-01-15T10:45:44.942Z", - "_postman_exported_using": "Postman/7.14.0" -} \ No newline at end of file diff --git a/examples/openapi-spec.json b/examples/openapi-spec.json new file mode 100644 index 0000000..ccc3399 --- /dev/null +++ b/examples/openapi-spec.json @@ -0,0 +1 @@ +{"openapi":"3.0.1","info":{"title":"OpenAPI definition","version":"v0"},"servers":[{"url":"http://localhost:8080","description":"Generated server url"}],"paths":{"/admin/connection-status/{countryCode}/{partyID}":{"get":{"tags":["admin-controller"],"operationId":"getConnectionStatus","parameters":[{"name":"Authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"string"}}}}}}},"/admin/generate-registration-token":{"post":{"tags":["admin-controller"],"operationId":"generateRegistrationToken","parameters":[{"name":"Authorization","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BasicRole"}}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object"}}}}}}},"/health":{"get":{"tags":["health-controller"],"operationId":"getHealth","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"string"}}}}}}},"/ocn/message":{"post":{"tags":["message-controller"],"operationId":"postMessage","parameters":[{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"string"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseObject"}}}}}}},"/ocn/registry/node-info":{"get":{"tags":["registry-controller"],"operationId":"getMyNodeInfo","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object","additionalProperties":{"type":"string"}}}}}}}},"/ocn/registry/node/{countryCode}/{partyID}":{"get":{"tags":["registry-controller"],"operationId":"getNodeOf","parameters":[{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object"}}}}}}},"/ocpi/2.2":{"get":{"tags":["versions-controller"],"operationId":"getVersionsDetail","parameters":[{"name":"Authorization","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseVersionDetail"}}}}}}},"/ocpi/versions":{"get":{"tags":["versions-controller"],"operationId":"getVersions","parameters":[{"name":"Authorization","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseListVersion"}}}}}}},"/ocpi/sender/2.2/cdrs":{"get":{"tags":["cdrs-controller"],"operationId":"getCdrsFromDataOwner","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"date_from","in":"query","required":false,"schema":{"type":"string"}},{"name":"date_to","in":"query","required":false,"schema":{"type":"string"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseCDRList"}}}}}}},"/ocpi/sender/2.2/cdrs/page/{uid}":{"get":{"tags":["cdrs-controller"],"operationId":"getCdrPageFromDataOwner","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseCDRList"}}}}}}},"/ocpi/receiver/2.2/cdrs/{cdrID}":{"get":{"tags":["cdrs-controller"],"operationId":"getClientOwnedCdr","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"cdrID","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseCDR"}}}}}}},"/ocpi/receiver/2.2/cdrs":{"post":{"tags":["cdrs-controller"],"operationId":"postClientOwnedCdr","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CDR"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}}},"/ocpi/2.2/sender/chargingprofiles/result/{uid}":{"post":{"tags":["charging-profiles-controller"],"operationId":"postGenericChargingProfileResult","parameters":[{"name":"Authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenericChargingProfileResult"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}}},"/ocpi/2.2/sender/chargingprofiles/{sessionId}":{"put":{"tags":["charging-profiles-controller"],"operationId":"putSenderChargingProfile","parameters":[{"name":"Authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"sessionId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActiveChargingProfile"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}}},"/ocpi/2.2/receiver/chargingprofiles/{sessionId}":{"get":{"tags":["charging-profiles-controller"],"operationId":"getReceiverChargingProfile","parameters":[{"name":"Authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"sessionId","in":"path","required":true,"schema":{"type":"string"}},{"name":"duration","in":"query","required":true,"schema":{"type":"integer","format":"int32"}},{"name":"response_url","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseChargingProfileResponse"}}}}}},"put":{"tags":["charging-profiles-controller"],"operationId":"putReceiverChargingProfile","parameters":[{"name":"Authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"sessionId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetChargingProfile"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseChargingProfileResponse"}}}}}},"delete":{"tags":["charging-profiles-controller"],"operationId":"deleteReceiverChargingProfile","parameters":[{"name":"Authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"sessionId","in":"path","required":true,"schema":{"type":"string"}},{"name":"response_url","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseChargingProfileResponse"}}}}}}},"/ocpi/sender/2.2/commands/{command}/{uid}":{"post":{"tags":["commands-controller"],"operationId":"postAsyncResponse","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"command","in":"path","required":true,"schema":{"type":"string","enum":["CANCEL_RESERVATION","RESERVE_NOW","START_SESSION","STOP_SESSION","UNLOCK_CONNECTOR"]}},{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommandResult"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}}},"/ocpi/receiver/2.2/commands/CANCEL_RESERVATION":{"post":{"tags":["commands-controller"],"operationId":"postCancelReservation","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelReservation"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseCommandResponse"}}}}}}},"/ocpi/receiver/2.2/commands/RESERVE_NOW":{"post":{"tags":["commands-controller"],"operationId":"postReserveNow","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReserveNow"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseCommandResponse"}}}}}}},"/ocpi/receiver/2.2/commands/START_SESSION":{"post":{"tags":["commands-controller"],"operationId":"postStartSession","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartSession"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseCommandResponse"}}}}}}},"/ocpi/receiver/2.2/commands/STOP_SESSION":{"post":{"tags":["commands-controller"],"operationId":"postStopSession","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StopSession"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseCommandResponse"}}}}}}},"/ocpi/receiver/2.2/commands/UNLOCK_CONNECTOR":{"post":{"tags":["commands-controller"],"operationId":"postUnlockConnector","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnlockConnector"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseCommandResponse"}}}}}}},"/ocpi/2.2/credentials":{"get":{"tags":["credentials-controller"],"operationId":"getCredentials","parameters":[{"name":"Authorization","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseCredentials"}}}}}},"put":{"tags":["credentials-controller"],"operationId":"putCredentials","parameters":[{"name":"Authorization","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Credentials"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseCredentials"}}}}}},"post":{"tags":["credentials-controller"],"operationId":"postCredentials","parameters":[{"name":"Authorization","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Credentials"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseCredentials"}}}}}},"delete":{"tags":["credentials-controller"],"operationId":"deleteCredentials","parameters":[{"name":"Authorization","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponse"}}}}}}},"/ocpi/sender/2.2/locations":{"get":{"tags":["locations-controller"],"operationId":"getLocationListFromDataOwner","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"date_from","in":"query","required":false,"schema":{"type":"string"}},{"name":"date_to","in":"query","required":false,"schema":{"type":"string"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseLocationList"}}}}}}},"/ocpi/sender/2.2/locations/page/{uid}":{"get":{"tags":["locations-controller"],"operationId":"getLocationPageFromDataOwner","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseLocationList"}}}}}}},"/ocpi/sender/2.2/locations/{locationID}":{"get":{"tags":["locations-controller"],"operationId":"getLocationObjectFromDataOwner","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"locationID","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseLocation"}}}}}}},"/ocpi/sender/2.2/locations/{locationID}/{evseUID}":{"get":{"tags":["locations-controller"],"operationId":"getEvseObjectFromDataOwner","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"locationID","in":"path","required":true,"schema":{"type":"string"}},{"name":"evseUID","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseEvse"}}}}}}},"/ocpi/sender/2.2/locations/{locationID}/{evseUID}/{connectorID}":{"get":{"tags":["locations-controller"],"operationId":"getConnectorObjectFromDataOwner","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"locationID","in":"path","required":true,"schema":{"type":"string"}},{"name":"evseUID","in":"path","required":true,"schema":{"type":"string"}},{"name":"connectorID","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseConnector"}}}}}}},"/ocpi/receiver/2.2/locations/{countryCode}/{partyID}/{locationID}":{"get":{"tags":["locations-controller"],"operationId":"getClientOwnedLocation","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}},{"name":"locationID","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseLocation"}}}}}},"put":{"tags":["locations-controller"],"operationId":"putClientOwnedLocation","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}},{"name":"locationID","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Location"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}},"patch":{"tags":["locations-controller"],"operationId":"patchClientOwnedLocation","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}},{"name":"locationID","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"object"}}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}}},"/ocpi/receiver/2.2/locations/{countryCode}/{partyID}/{locationID}/{evseUID}":{"get":{"tags":["locations-controller"],"operationId":"getClientOwnedEvse","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}},{"name":"locationID","in":"path","required":true,"schema":{"type":"string"}},{"name":"evseUID","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseEvse"}}}}}},"put":{"tags":["locations-controller"],"operationId":"putClientOwnedEvse","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}},{"name":"locationID","in":"path","required":true,"schema":{"type":"string"}},{"name":"evseUID","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Evse"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}},"patch":{"tags":["locations-controller"],"operationId":"patchClientOwnedEvse","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}},{"name":"locationID","in":"path","required":true,"schema":{"type":"string"}},{"name":"evseUID","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"object"}}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}}},"/ocpi/receiver/2.2/locations/{countryCode}/{partyID}/{locationID}/{evseUID}/{connectorID}":{"get":{"tags":["locations-controller"],"operationId":"getClientOwnedConnector","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}},{"name":"locationID","in":"path","required":true,"schema":{"type":"string"}},{"name":"evseUID","in":"path","required":true,"schema":{"type":"string"}},{"name":"connectorID","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseConnector"}}}}}},"put":{"tags":["locations-controller"],"operationId":"putClientOwnedConnector","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}},{"name":"locationID","in":"path","required":true,"schema":{"type":"string"}},{"name":"evseUID","in":"path","required":true,"schema":{"type":"string"}},{"name":"connectorID","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Connector"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}},"patch":{"tags":["locations-controller"],"operationId":"patchClientOwnedConnector","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}},{"name":"locationID","in":"path","required":true,"schema":{"type":"string"}},{"name":"evseUID","in":"path","required":true,"schema":{"type":"string"}},{"name":"connectorID","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"object"}}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}}},"/ocpi/receiver/2.2/ocnrules/whitelist":{"put":{"tags":["ocn-rules-controller"],"operationId":"updateWhitelist","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OcnRulesListParty"}}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}},"post":{"tags":["ocn-rules-controller"],"operationId":"appendToWhitelist","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OcnRulesListParty"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}}},"/ocpi/receiver/2.2/ocnrules/blacklist":{"put":{"tags":["ocn-rules-controller"],"operationId":"updateBlacklist","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OcnRulesListParty"}}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}},"post":{"tags":["ocn-rules-controller"],"operationId":"appendToBlacklist","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OcnRulesListParty"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}}},"/ocpi/receiver/2.2/ocnrules/signatures":{"put":{"tags":["ocn-rules-controller"],"operationId":"updateSignatures","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}}},"/ocpi/receiver/2.2/ocnrules/block-all":{"put":{"tags":["ocn-rules-controller"],"operationId":"blockAll","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}}},"/ocpi/receiver/2.2/ocnrules/whitelist/{countryCode}/{partyID}":{"delete":{"tags":["ocn-rules-controller"],"operationId":"deleteFromWhitelist","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}}},"/ocpi/receiver/2.2/ocnrules/blacklist/{countryCode}/{partyID}":{"delete":{"tags":["ocn-rules-controller"],"operationId":"deleteFromBlacklist","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}}},"/ocpi/receiver/2.2/ocnrules":{"get":{"tags":["ocn-rules-controller"],"operationId":"getRules","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseOcnRules"}}}}}}},"/ocpi/sender/2.2/sessions":{"get":{"tags":["sessions-controller"],"operationId":"getSessionsFromDataOwner","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"date_from","in":"query","required":false,"schema":{"type":"string"}},{"name":"date_to","in":"query","required":false,"schema":{"type":"string"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseSessionList"}}}}}}},"/ocpi/sender/2.2/sessions/page/{uid}":{"get":{"tags":["sessions-controller"],"operationId":"getSessionsPageFromDataOwner","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseSessionList"}}}}}}},"/ocpi/sender/2.2/sessions/{sessionID}/charging_preferences":{"put":{"tags":["sessions-controller"],"operationId":"putChargingPreferences","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"sessionID","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChargingPreferences"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseChargingPreferencesResponse"}}}}}}},"/ocpi/receiver/2.2/sessions/{countryCode}/{partyID}/{sessionID}":{"get":{"tags":["sessions-controller"],"operationId":"getClientOwnedSession","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}},{"name":"sessionID","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseSession"}}}}}},"put":{"tags":["sessions-controller"],"operationId":"putClientOwnedSession","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}},{"name":"sessionID","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}},"patch":{"tags":["sessions-controller"],"operationId":"patchClientOwnedSession","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}},{"name":"sessionID","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"object"}}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}}},"/ocpi/receiver/2.2/tariffs/{countryCode}/{partyID}/{tariffID}":{"get":{"tags":["tariffs-controller"],"operationId":"getClientOwnedTariff","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}},{"name":"tariffID","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseTariff"}}}}}},"put":{"tags":["tariffs-controller"],"operationId":"putClientOwnedTariff","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}},{"name":"tariffID","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tariff"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}},"delete":{"tags":["tariffs-controller"],"operationId":"deleteClientOwnedTariff","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}},{"name":"tariffID","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}}},"/ocpi/sender/2.2/tariffs":{"get":{"tags":["tariffs-controller"],"operationId":"getTariffsFromDataOwner","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"date_from","in":"query","required":false,"schema":{"type":"string"}},{"name":"date_to","in":"query","required":false,"schema":{"type":"string"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseTariffList"}}}}}}},"/ocpi/sender/2.2/tariffs/page/{uid}":{"get":{"tags":["tariffs-controller"],"operationId":"getTariffsPageFromDataOwner","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseTariffList"}}}}}}},"/ocpi/sender/2.2/tokens":{"get":{"tags":["tokens-controller"],"operationId":"getTokensFromDataOwner","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"date_from","in":"query","required":false,"schema":{"type":"string"}},{"name":"date_to","in":"query","required":false,"schema":{"type":"string"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseTokenList"}}}}}}},"/ocpi/sender/2.2/tokens/page/{uid}":{"get":{"tags":["tokens-controller"],"operationId":"getTokensPageFromDataOwner","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseTokenList"}}}}}}},"/ocpi/sender/2.2/tokens/{tokenUID}/authorize":{"post":{"tags":["tokens-controller"],"operationId":"postRealTimeTokenAuthorization","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"tokenUID","in":"path","required":true,"schema":{"type":"string"}},{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["AD_HOC_USER","APP_USER","OTHER","RFID"],"default":"RFID"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocationReferences"}}}},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseAuthorizationInfo"}}}}}}},"/ocpi/receiver/2.2/tokens/{countryCode}/{partyID}/{tokenUID}":{"get":{"tags":["tokens-controller"],"operationId":"getClientOwnedToken","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}},{"name":"tokenUID","in":"path","required":true,"schema":{"type":"string"}},{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["AD_HOC_USER","APP_USER","OTHER","RFID"],"default":"RFID"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseToken"}}}}}},"put":{"tags":["tokens-controller"],"operationId":"putClientOwnedToken","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}},{"name":"tokenUID","in":"path","required":true,"schema":{"type":"string"}},{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["AD_HOC_USER","APP_USER","OTHER","RFID"],"default":"RFID"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Token"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}},"patch":{"tags":["tokens-controller"],"operationId":"patchClientOwnedToken","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCN-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Request-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Correlation-ID","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-from-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-country-code","in":"header","required":true,"schema":{"type":"string"}},{"name":"OCPI-to-party-id","in":"header","required":true,"schema":{"type":"string"}},{"name":"countryCode","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyID","in":"path","required":true,"schema":{"type":"string"}},{"name":"tokenUID","in":"path","required":true,"schema":{"type":"string"}},{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["AD_HOC_USER","APP_USER","OTHER","RFID"],"default":"RFID"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"object"}}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OcpiResponseUnit"}}}}}}}},"components":{"schemas":{"BasicRole":{"required":["country_code","party_id"],"type":"object","properties":{"party_id":{"type":"string"},"country_code":{"type":"string"}}},"OcpiResponseObject":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"type":"object"},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"Endpoint":{"required":["identifier","role","url"],"type":"object","properties":{"identifier":{"type":"string"},"role":{"type":"string","enum":["SENDER","RECEIVER"]},"url":{"type":"string"}}},"OcpiResponseVersionDetail":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"$ref":"#/components/schemas/VersionDetail"},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"VersionDetail":{"required":["endpoints","version"],"type":"object","properties":{"version":{"type":"string"},"endpoints":{"type":"array","items":{"$ref":"#/components/schemas/Endpoint"}}}},"OcpiResponseListVersion":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Version"}},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"Version":{"required":["url","version"],"type":"object","properties":{"version":{"type":"string"},"url":{"type":"string"}}},"CDR":{"required":["auth_method","cdr_location","cdr_token","charging_periods","country_code","currency","end_date_time","id","last_updated","party_id","start_date_time","total_cost","total_energy","total_time"],"type":"object","properties":{"country_code":{"type":"string"},"party_id":{"type":"string"},"id":{"type":"string"},"start_date_time":{"type":"string"},"end_date_time":{"type":"string"},"session_id":{"type":"string"},"cdr_token":{"$ref":"#/components/schemas/CdrToken"},"auth_method":{"type":"string","enum":["AUTH_REQUEST","COMMAND","WHITELIST"]},"authorization_reference":{"type":"string"},"cdr_location":{"$ref":"#/components/schemas/CdrLocation"},"meter_id":{"type":"string"},"currency":{"type":"string"},"tariffs":{"type":"array","items":{"$ref":"#/components/schemas/Tariff"}},"charging_periods":{"type":"array","items":{"$ref":"#/components/schemas/ChargingPeriod"}},"signed_data":{"$ref":"#/components/schemas/SignedData"},"total_cost":{"$ref":"#/components/schemas/Price"},"total_fixed_cost":{"$ref":"#/components/schemas/Price"},"total_energy":{"type":"number","format":"float"},"total_energy_cost":{"$ref":"#/components/schemas/Price"},"total_time":{"type":"number","format":"float"},"total_time_cost":{"$ref":"#/components/schemas/Price"},"total_parking_time":{"type":"number","format":"float"},"total_parking_cost":{"$ref":"#/components/schemas/Price"},"total_reservation_cost":{"$ref":"#/components/schemas/Price"},"remark":{"type":"string"},"invoice_reference_id":{"type":"string"},"credit":{"type":"boolean"},"credit_reference_id":{"type":"string"},"last_updated":{"type":"string"}}},"CdrDimension":{"required":["type","volume"],"type":"object","properties":{"type":{"type":"string","enum":["CURRENT","ENERGY","ENERGY_EXPORT","ENERGY_IMPORT","MAX_CURRENT","MIN_CURRENT","MAX_POWER","MIN_POWER","PARKING_TIME","POWER","RESERVATION_TIME","STATE_OF_CHARGE","TIME"]},"volume":{"type":"number","format":"float"}}},"CdrLocation":{"required":["address","city","connector_format","connector_id","connector_power_type","connector_standard","coordinates","country","evse_id","evse_uid","id","postal_code"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"address":{"type":"string"},"city":{"type":"string"},"postal_code":{"type":"string"},"country":{"type":"string"},"coordinates":{"$ref":"#/components/schemas/GeoLocation"},"evse_uid":{"type":"string"},"evse_id":{"type":"string"},"connector_id":{"type":"string"},"connector_standard":{"type":"string","enum":["CHADEMO","DOMESTIC_A","DOMESTIC_B","DOMESTIC_C","DOMESTIC_D","DOMESTIC_E","DOMESTIC_F","DOMESTIC_G","DOMESTIC_H","DOMESTIC_I","DOMESTIC_J","DOMESTIC_K","DOMESTIC_L","IEC_60309_2_single_16","IEC_60309_2_three_16","IEC_60309_2_three_32","IEC_60309_2_three_64","IEC_62196_T1","IEC_62196_T1_COMBO","IEC_62196_T2","IEC_62196_T2_COMBO","IEC_62196_T3A","IEC_62196_T3C","PANTOGRAPH_BOTTOM_UP","PANTOGRAPH_TOP_DOWN","TESLA_R","TESLA_S"]},"connector_format":{"type":"string","enum":["SOCKET","CABLE"]},"connector_power_type":{"type":"string","enum":["AC_1_PHASE","AC_3_PHASE","DC"]}}},"CdrToken":{"required":["contract_id","type","uid"],"type":"object","properties":{"uid":{"type":"string"},"type":{"type":"string","enum":["AD_HOC_USER","APP_USER","OTHER","RFID"]},"contract_id":{"type":"string"}}},"ChargingPeriod":{"required":["dimensions","start_date_time"],"type":"object","properties":{"start_date_time":{"type":"string"},"dimensions":{"type":"array","items":{"$ref":"#/components/schemas/CdrDimension"}},"tariff_id":{"type":"string"}}},"DisplayText":{"required":["language","text"],"type":"object","properties":{"language":{"type":"string"},"text":{"type":"string"}}},"EnergyMix":{"required":["is_green_energy"],"type":"object","properties":{"is_green_energy":{"type":"boolean"},"energy_sources":{"type":"array","items":{"$ref":"#/components/schemas/EnergySource"}},"environ_impact":{"type":"array","items":{"$ref":"#/components/schemas/EnvironmentalImpact"}},"supplier_name":{"type":"string"},"energy_product_name":{"type":"string"}}},"EnergySource":{"required":["percentage","source"],"type":"object","properties":{"source":{"type":"string","enum":["NUCLEAR","GENERAL_FOSSIL","COAL","GAS","GENERAL_GREEN","SOLAR","WIND","WATER"]},"percentage":{"type":"number","format":"float"}}},"EnvironmentalImpact":{"required":["amount","category"],"type":"object","properties":{"category":{"type":"string","enum":["NUCLEAR_WASTE","CARBON_DIOXIDE"]},"amount":{"type":"number","format":"float"}}},"GeoLocation":{"required":["latitude","longitude"],"type":"object","properties":{"latitude":{"type":"string"},"longitude":{"type":"string"}}},"OcpiResponseCDRList":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/CDR"}},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"Price":{"required":["excl_vat","incl_vat"],"type":"object","properties":{"excl_vat":{"type":"number","format":"float"},"incl_vat":{"type":"number","format":"float"}}},"PriceComponent":{"required":["price","step_size","type"],"type":"object","properties":{"type":{"type":"string","enum":["ENERGY","FLAT","PARKING_TIME","TIME"]},"price":{"type":"number","format":"float"},"vat":{"type":"number","format":"float"},"step_size":{"type":"integer","format":"int32"}}},"SignedData":{"required":["encoding_method","signed_values","url"],"type":"object","properties":{"encoding_method":{"type":"string"},"encoding_method_version":{"type":"integer","format":"int32"},"public_key":{"type":"string"},"signed_values":{"type":"array","items":{"$ref":"#/components/schemas/SignedValue"}},"url":{"type":"string"}}},"SignedValue":{"required":["nature","plain_data","signed_data"],"type":"object","properties":{"nature":{"type":"string"},"plain_data":{"type":"string"},"signed_data":{"type":"string"}}},"Tariff":{"required":["country_code","currency","elements","id","last_updated","party_id"],"type":"object","properties":{"country_code":{"type":"string"},"party_id":{"type":"string"},"id":{"type":"string"},"currency":{"type":"string"},"type":{"type":"string","enum":["AD_HOC_PAYMENT","PROFILE_CHEAP","PROFILE_FAST","PROFILE_GREEN","REGULAR"]},"tariff_alt_text":{"type":"array","items":{"$ref":"#/components/schemas/DisplayText"}},"tariff_alt_url":{"type":"string"},"min_price":{"$ref":"#/components/schemas/Price"},"max_price":{"$ref":"#/components/schemas/Price"},"elements":{"type":"array","items":{"$ref":"#/components/schemas/TariffElement"}},"start_date_time":{"type":"string"},"end_date_time":{"type":"string"},"energy_mix":{"$ref":"#/components/schemas/EnergyMix"},"last_updated":{"type":"string"}}},"TariffElement":{"required":["price_components"],"type":"object","properties":{"price_components":{"type":"array","items":{"$ref":"#/components/schemas/PriceComponent"}},"restrictions":{"$ref":"#/components/schemas/TariffRestrictions"}}},"TariffRestrictions":{"type":"object","properties":{"start_time":{"type":"string"},"end_time":{"type":"string"},"start_date":{"type":"string"},"end_date":{"type":"string"},"min_kwh":{"type":"number","format":"float"},"max_kwh":{"type":"number","format":"float"},"min_current":{"type":"number","format":"float"},"max_current":{"type":"number","format":"float"},"min_power":{"type":"number","format":"float"},"max_power":{"type":"number","format":"float"},"min_duration":{"type":"integer","format":"int32"},"max_duration":{"type":"integer","format":"int32"},"day_of_week":{"type":"array","items":{"type":"string","enum":["MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY","SUNDAY"]}},"reservation":{"type":"string","enum":["RESERVATION","RESERVATION_EXPIRES"]}}},"OcpiResponseCDR":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"$ref":"#/components/schemas/CDR"},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"OcpiResponseUnit":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"$ref":"#/components/schemas/Unit"},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"Unit":{"type":"object"},"ActiveChargingProfile":{"required":["charging_profile","start_date_time"],"type":"object","properties":{"start_date_time":{"type":"string"},"charging_profile":{"$ref":"#/components/schemas/ChargingProfile"}}},"ChargingProfile":{"required":["charging_rate_unit"],"type":"object","properties":{"start_date_time":{"type":"string"},"duration":{"type":"integer","format":"int32"},"charging_rate_unit":{"type":"string","enum":["W","A"]},"min_charging_rate":{"type":"number","format":"float"},"charging_profile_period":{"type":"array","items":{"$ref":"#/components/schemas/ChargingProfilePeriod"}}}},"ChargingProfilePeriod":{"required":["limit","start_period"],"type":"object","properties":{"start_period":{"type":"integer","format":"int32"},"limit":{"type":"number","format":"float"}}},"GenericChargingProfileResult":{"required":["result"],"type":"object","properties":{"result":{"type":"string","enum":["ACCEPTED","REJECTED","UNKNOWN"]},"profile":{"$ref":"#/components/schemas/ActiveChargingProfile"}}},"ChargingProfileResponse":{"required":["result","timeout"],"type":"object","properties":{"result":{"type":"string","enum":["ACCEPTED","NOT_SUPPORTED","REJECTED","TOO_OFTEN","UNKNOWN_SESSION"]},"timeout":{"type":"integer","format":"int32"}}},"OcpiResponseChargingProfileResponse":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"$ref":"#/components/schemas/ChargingProfileResponse"},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"SetChargingProfile":{"required":["charging_profile","response_url"],"type":"object","properties":{"charging_profile":{"$ref":"#/components/schemas/ChargingProfile"},"response_url":{"type":"string"}}},"CommandResult":{"required":["result"],"type":"object","properties":{"result":{"type":"string","enum":["ACCEPTED","CANCELED_RESERVATION","EVSE_OCCUPIED","EVSE_INOPERATIVE","FAILED","NOT_SUPPORTED","REJECTED","TIMEOUT","UNKNOWN_RESERVATION"]},"message":{"$ref":"#/components/schemas/DisplayText"}}},"CancelReservation":{"required":["reservation_id","response_url"],"type":"object","properties":{"response_url":{"type":"string"},"reservation_id":{"type":"string"}}},"CommandResponse":{"required":["result","timeout"],"type":"object","properties":{"result":{"type":"string","enum":["NOT_SUPPORTED","REJECTED","ACCEPTED","UNKNOWN_SESSION"]},"timeout":{"type":"integer","format":"int32"},"message":{"$ref":"#/components/schemas/DisplayText"}}},"OcpiResponseCommandResponse":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"$ref":"#/components/schemas/CommandResponse"},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"EnergyContract":{"required":["supplier_name"],"type":"object","properties":{"supplier_name":{"type":"string"},"contract_id":{"type":"string"}}},"ReserveNow":{"required":["expiry_date","location_id","reservation_id","response_url","token"],"type":"object","properties":{"response_url":{"type":"string"},"token":{"$ref":"#/components/schemas/Token"},"expiry_date":{"type":"string"},"reservation_id":{"type":"string"},"location_id":{"type":"string"},"evse_uid":{"type":"string"},"authorization_reference":{"type":"string"}}},"Token":{"required":["contract_id","country_code","issuer","last_updated","party_id","type","uid","valid","whitelist"],"type":"object","properties":{"country_code":{"type":"string"},"party_id":{"type":"string"},"uid":{"type":"string"},"type":{"type":"string","enum":["AD_HOC_USER","APP_USER","OTHER","RFID"]},"contract_id":{"type":"string"},"visual_number":{"type":"string"},"issuer":{"type":"string"},"group_id":{"type":"string"},"valid":{"type":"boolean"},"whitelist":{"type":"string","enum":["ALWAYS","ALLOWED","ALLOWED_OFFLINE","NEVER"]},"language":{"type":"string"},"default_profile_type":{"type":"string","enum":["CHEAP","FAST","GREEN","REGULAR"]},"energy_contract":{"$ref":"#/components/schemas/EnergyContract"},"last_updated":{"type":"string"}}},"StartSession":{"required":["location_id","response_url","token"],"type":"object","properties":{"response_url":{"type":"string"},"token":{"$ref":"#/components/schemas/Token"},"location_id":{"type":"string"},"evse_uid":{"type":"string"},"authorization_reference":{"type":"string"}}},"StopSession":{"required":["response_url","session_id"],"type":"object","properties":{"response_url":{"type":"string"},"session_id":{"type":"string"}}},"UnlockConnector":{"required":["connector_id","evse_uid","location_id","response_url"],"type":"object","properties":{"response_url":{"type":"string"},"location_id":{"type":"string"},"evse_uid":{"type":"string"},"connector_id":{"type":"string"}}},"BusinessDetails":{"required":["name"],"type":"object","properties":{"name":{"type":"string"},"website":{"type":"string"},"logo":{"$ref":"#/components/schemas/Image"}}},"Credentials":{"required":["roles","token","url"],"type":"object","properties":{"token":{"type":"string"},"url":{"type":"string"},"roles":{"type":"array","items":{"$ref":"#/components/schemas/CredentialsRole"}}}},"CredentialsRole":{"required":["business_details","country_code","party_id","role"],"type":"object","properties":{"role":{"type":"string","enum":["CPO","EMSP","HUB","NAP","NSP","OTHER","SCSP"]},"business_details":{"$ref":"#/components/schemas/BusinessDetails"},"party_id":{"type":"string"},"country_code":{"type":"string"}}},"Image":{"required":["category","type","url"],"type":"object","properties":{"url":{"type":"string"},"thumbnail":{"type":"string"},"category":{"type":"string","enum":["CHARGER","ENTRANCE","LOCATION","NETWORK","OPERATOR","OTHER","OWNER"]},"type":{"type":"string"},"width":{"type":"integer","format":"int32"},"height":{"type":"integer","format":"int32"}}},"OcpiResponseCredentials":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"$ref":"#/components/schemas/Credentials"},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"OcpiResponse":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"type":"object"},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"AdditionalGeoLocation":{"required":["latitude","longitude"],"type":"object","properties":{"latitude":{"type":"string"},"longitude":{"type":"string"},"name":{"$ref":"#/components/schemas/DisplayText"}}},"Connector":{"required":["format","id","last_updated","max_amperage","max_voltage","power_type","standard"],"type":"object","properties":{"id":{"type":"string"},"standard":{"type":"string","enum":["CHADEMO","DOMESTIC_A","DOMESTIC_B","DOMESTIC_C","DOMESTIC_D","DOMESTIC_E","DOMESTIC_F","DOMESTIC_G","DOMESTIC_H","DOMESTIC_I","DOMESTIC_J","DOMESTIC_K","DOMESTIC_L","IEC_60309_2_single_16","IEC_60309_2_three_16","IEC_60309_2_three_32","IEC_60309_2_three_64","IEC_62196_T1","IEC_62196_T1_COMBO","IEC_62196_T2","IEC_62196_T2_COMBO","IEC_62196_T3A","IEC_62196_T3C","PANTOGRAPH_BOTTOM_UP","PANTOGRAPH_TOP_DOWN","TESLA_R","TESLA_S"]},"format":{"type":"string","enum":["SOCKET","CABLE"]},"power_type":{"type":"string","enum":["AC_1_PHASE","AC_3_PHASE","DC"]},"max_voltage":{"type":"integer","format":"int32"},"max_amperage":{"type":"integer","format":"int32"},"max_electric_power":{"type":"integer","format":"int32"},"tariff_ids":{"type":"array","items":{"type":"string"}},"terms_and_conditions":{"type":"string"},"last_updated":{"type":"string"}}},"Evse":{"required":["connectors","last_updated","status","uid"],"type":"object","properties":{"uid":{"type":"string"},"evse_id":{"type":"string"},"status":{"type":"string","enum":["AVAILABLE","BLOCKED","CHARGING","INOPERATIVE","OUTOFORDER","PLANNED","REMOVED","RESERVED","UNKNOWN"]},"status_schedule":{"type":"array","items":{"$ref":"#/components/schemas/StatusSchedule"}},"capabilities":{"type":"array","items":{"type":"string","enum":["CHARGING_PROFILE_CAPABLE","CHARGING_PREFERENCES_CAPABLE","CHIP_CARD_SUPPORT","CONTACTLESS_CARD_SUPPORT","CREDIT_CARD_PAYABLE","DEBIT_CARD_PAYABLE","PED_TERMINAL","REMOTE_START_STOP_CAPABLE","RESERVABLE","RFID_READER","TOKEN_GROUP_CAPABLE","UNLOCK_CAPABLE"]}},"connectors":{"type":"array","items":{"$ref":"#/components/schemas/Connector"}},"floor_level":{"type":"string"},"coordinates":{"$ref":"#/components/schemas/GeoLocation"},"physical_reference":{"type":"string"},"directions":{"type":"array","items":{"$ref":"#/components/schemas/DisplayText"}},"parking_restrictions":{"type":"array","items":{"type":"string","enum":["EV_ONLY","PLUGGED","DISABLED","CUSTOMERS","MOTORCYCLES"]}},"images":{"type":"array","items":{"$ref":"#/components/schemas/Image"}},"last_updated":{"type":"string"}}},"ExceptionalPeriod":{"required":["period_begin","period_end"],"type":"object","properties":{"period_begin":{"type":"string"},"period_end":{"type":"string"}}},"Hours":{"required":["twentyfourseven"],"type":"object","properties":{"twentyfourseven":{"type":"boolean"},"regular_hours":{"type":"array","items":{"$ref":"#/components/schemas/RegularHours"}},"exceptional_openings":{"type":"array","items":{"$ref":"#/components/schemas/ExceptionalPeriod"}},"exceptional_closings":{"type":"array","items":{"$ref":"#/components/schemas/ExceptionalPeriod"}}}},"Location":{"required":["address","city","coordinates","country","country_code","id","last_updated","party_id","publish"],"type":"object","properties":{"country_code":{"type":"string"},"party_id":{"type":"string"},"id":{"type":"string"},"publish":{"type":"boolean"},"publish_allowed_to":{"type":"array","items":{"$ref":"#/components/schemas/PublishTokenType"}},"name":{"type":"string"},"address":{"type":"string"},"city":{"type":"string"},"postal_code":{"type":"string"},"state":{"type":"string"},"country":{"type":"string"},"coordinates":{"$ref":"#/components/schemas/GeoLocation"},"related_locations":{"type":"array","items":{"$ref":"#/components/schemas/AdditionalGeoLocation"}},"parking_type":{"type":"string","enum":["ALONG_MOTORWAY","PARKING_GARAGE","PARKING_LOT","ON_DRIVEWAY","ON_STREET","UNDERGROUND_GARAGE"]},"evses":{"type":"array","items":{"$ref":"#/components/schemas/Evse"}},"directions":{"type":"array","items":{"$ref":"#/components/schemas/DisplayText"}},"operator":{"$ref":"#/components/schemas/BusinessDetails"},"suboperator":{"$ref":"#/components/schemas/BusinessDetails"},"owner":{"$ref":"#/components/schemas/BusinessDetails"},"facilities":{"type":"array","items":{"type":"string","enum":["HOTEL","RESTAURANT","CAFE","MALL","SUPERMARKET","SPORT","RECREATION_AREA","NATURE","MUSEUM","BIKE_SHARING","BUS_STOP","TAXI_STAND","TRAM_STOP","METRO_STATION","TRAIN_STATION","AIRPORT","PARKING_LOT","CARPOOL_PARKING","FUEL_STATION","WIFI"]}},"time_zone":{"type":"string"},"opening_times":{"$ref":"#/components/schemas/Hours"},"charging_when_closed":{"type":"string"},"images":{"type":"array","items":{"$ref":"#/components/schemas/Image"}},"energy_mix":{"$ref":"#/components/schemas/EnergyMix"},"last_updated":{"type":"string"}}},"OcpiResponseLocationList":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Location"}},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"PublishTokenType":{"type":"object","properties":{"uid":{"type":"string"},"type":{"type":"string","enum":["AD_HOC_USER","APP_USER","OTHER","RFID"]},"visual_number":{"type":"string"},"issuer":{"type":"string"},"group_id ":{"type":"string"}}},"RegularHours":{"required":["period_begin","period_end","weekday"],"type":"object","properties":{"weekday":{"type":"integer","format":"int32"},"period_begin":{"type":"string"},"period_end":{"type":"string"}}},"StatusSchedule":{"required":["period_begin","status"],"type":"object","properties":{"period_begin":{"type":"string"},"period_end":{"type":"string"},"status":{"type":"string","enum":["AVAILABLE","BLOCKED","CHARGING","INOPERATIVE","OUTOFORDER","PLANNED","REMOVED","RESERVED","UNKNOWN"]}}},"OcpiResponseLocation":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"$ref":"#/components/schemas/Location"},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"OcpiResponseEvse":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"$ref":"#/components/schemas/Evse"},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"OcpiResponseConnector":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"$ref":"#/components/schemas/Connector"},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"OcnRulesListParty":{"required":["country_code","modules","party_id"],"type":"object","properties":{"party_id":{"type":"string"},"country_code":{"type":"string"},"modules":{"type":"array","items":{"type":"string"}}}},"OcnRules":{"required":["blacklist","signatures","whitelist"],"type":"object","properties":{"signatures":{"type":"boolean"},"whitelist":{"$ref":"#/components/schemas/OcnRulesList"},"blacklist":{"$ref":"#/components/schemas/OcnRulesList"}}},"OcnRulesList":{"required":["active","list"],"type":"object","properties":{"active":{"type":"boolean"},"list":{"type":"array","items":{"$ref":"#/components/schemas/OcnRulesListParty"}}}},"OcpiResponseOcnRules":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"$ref":"#/components/schemas/OcnRules"},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"OcpiResponseSessionList":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Session"}},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"Session":{"required":["auth_method","cdr_token","connector_id","country_code","currency","evse_uid","id","kwh","last_updated","location_id","party_id","start_date_time","status"],"type":"object","properties":{"country_code":{"type":"string"},"party_id":{"type":"string"},"id":{"type":"string"},"start_date_time":{"type":"string"},"end_date_time":{"type":"string"},"kwh":{"type":"number","format":"float"},"cdr_token":{"$ref":"#/components/schemas/CdrToken"},"auth_method":{"type":"string","enum":["AUTH_REQUEST","COMMAND","WHITELIST"]},"authorization_reference":{"type":"string"},"location_id":{"type":"string"},"evse_uid":{"type":"string"},"connector_id":{"type":"string"},"meter_id":{"type":"string"},"currency":{"type":"string"},"charging_periods":{"type":"array","items":{"$ref":"#/components/schemas/ChargingPeriod"}},"total_cost":{"$ref":"#/components/schemas/Price"},"status":{"type":"string","enum":["ACTIVE","COMPLETED","INVALID","PENDING","RESERVATION"]},"last_updated":{"type":"string"}}},"ChargingPreferences":{"required":["profile_type"],"type":"object","properties":{"profile_type":{"type":"string","enum":["CHEAP","FAST","GREEN","REGULAR"]},"departure_time":{"type":"string"},"energy_need":{"type":"number","format":"float"},"discharge_allowed":{"type":"boolean"}}},"OcpiResponseChargingPreferencesResponse":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"type":"string","enum":["ACCEPTED","DEPARTURE_REQUIRED","ENERGY_NEED_REQUIRED","NOT_POSSIBLE","PROFILE_TYPE_NOT_SUPPORTED"]},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"OcpiResponseSession":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"$ref":"#/components/schemas/Session"},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"OcpiResponseTariffList":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Tariff"}},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"OcpiResponseTariff":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"$ref":"#/components/schemas/Tariff"},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"OcpiResponseTokenList":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Token"}},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"LocationReferences":{"required":["location_id"],"type":"object","properties":{"location_id":{"type":"string"},"evse_uids":{"type":"array","items":{"type":"string"}},"connector_ids":{"type":"array","items":{"type":"string"}}}},"AuthorizationInfo":{"required":["allowed","token"],"type":"object","properties":{"allowed":{"type":"string","enum":["ALLOWED","BLOCKED","EXPIRED","NO_CREDIT","NOT_ALLOWED"]},"token":{"$ref":"#/components/schemas/Token"},"location":{"$ref":"#/components/schemas/LocationReferences"},"authorization_reference":{"type":"string"},"info":{"$ref":"#/components/schemas/DisplayText"}}},"OcpiResponseAuthorizationInfo":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"$ref":"#/components/schemas/AuthorizationInfo"},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}},"OcpiResponseToken":{"required":["status_code","timestamp"],"type":"object","properties":{"status_code":{"type":"integer","format":"int32"},"status_message":{"type":"string"},"data":{"$ref":"#/components/schemas/Token"},"timestamp":{"type":"string"},"ocn_signature":{"type":"string"}}}}}} \ No newline at end of file diff --git a/infra/ocn-node.service b/infra/ocn-node.service index db2f124..69d112f 100644 --- a/infra/ocn-node.service +++ b/infra/ocn-node.service @@ -5,9 +5,9 @@ After=network-online.target [Service] Type=simple User= -WorkingDirectory=/home//ocn-node-1.1.0-rc0 -ExecStart=/usr/bin/java -jar -Dspring.config.location=application.dev.properties ocn-node-1.1.0-rc0.jar +WorkingDirectory=/home//ocn-node-1.1.2 +ExecStart=/usr/bin/java -jar -Dspring.config.location=application.dev.properties ocn-node-1.1.2 Restart=on-failure [Install] -WantedBy=multi-user.target \ No newline at end of file +WantedBy=multi-user.target diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc index b5b50db..3510210 100644 --- a/src/docs/asciidoc/index.adoc +++ b/src/docs/asciidoc/index.adoc @@ -54,7 +54,7 @@ include::{snippets}/registry/node-of/http-response.adoc[] == Admin -The admin API allows administrators to manage their OCN Node. Currently, the only admin functionality is the generatoion +The admin API allows administrators to manage their OCN Node. Currently, the only admin functionality is the generation of new Open Charge Point Interface registration tokens (so-called `CREDENTIALS_TOKEN_A`) for interested platforms. === Request @@ -134,7 +134,7 @@ This acts as a wildcard for _all_ custom OCPI modules. Here's how it works: a. CPO fetches OCN Node versions, saves endpoint details for wildcard custom module b. OCN Node fetches CPO versions, saves endpoint details for `enriched-locations` custom module 2. CPO sends their custom module request to the OCN Node - a. requests any HTTP method + a. requests any HTTP method. If making a POST, PUT or PATCH request, the content-type should be application/json b. places module name as first path variable, followed by any additional variables required by the custom module: https://some.ocn-node.net/ocpi/custom/receiver/enriched-locations/location1 c. appends url-encoded queries and json body if necessary diff --git a/src/main/kotlin/snc/openchargingnetwork/node/config/NodeProperties.kt b/src/main/kotlin/snc/openchargingnetwork/node/config/NodeProperties.kt index b286941..c8a22fe 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/config/NodeProperties.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/config/NodeProperties.kt @@ -54,5 +54,5 @@ class NodeProperties { var plannedPartySearchEnabled: Boolean = true - var serviceEnabled: Boolean = true + var serviceInterfaceEnabled: Boolean = true } \ No newline at end of file diff --git a/src/main/kotlin/snc/openchargingnetwork/node/config/Verification.kt b/src/main/kotlin/snc/openchargingnetwork/node/config/Verification.kt index d0fc73c..3e85664 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/config/Verification.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/config/Verification.kt @@ -25,10 +25,14 @@ import java.net.InetAddress import java.net.URL import java.net.UnknownHostException import javax.net.ssl.SSLException +import org.slf4j.LoggerFactory @Component class Verification(private val properties: NodeProperties) { + companion object { + private val logger = LoggerFactory.getLogger(Verification::class.java) + } @EventListener(ApplicationReadyEvent::class) fun testRegistry() { if (properties.privateKey == null) { @@ -68,7 +72,7 @@ class Verification(private val properties: NodeProperties) { try { val response = khttp.get(healthURL) if (response.statusCode != 200) { - throw ConnectException("${response.statusCode} ${response.text}") + logger.warn("Received status code ${response.statusCode} from $healthURL application may not be healthy.") } } catch (e: ConnectException) { throw IllegalArgumentException("Unable to connect. Ensure $healthURL is reachable.") @@ -77,4 +81,4 @@ class Verification(private val properties: NodeProperties) { } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/AdminController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/AdminController.kt index aa9edbb..e25ad29 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/AdminController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/AdminController.kt @@ -29,6 +29,7 @@ import snc.openchargingnetwork.node.models.entities.Auth import snc.openchargingnetwork.node.models.entities.PlatformEntity import snc.openchargingnetwork.node.models.ocpi.RegistrationInfo import snc.openchargingnetwork.node.tools.generateUUIDv4Token +import snc.openchargingnetwork.node.tools.toBs64String import snc.openchargingnetwork.node.tools.urlJoin @@ -83,7 +84,7 @@ class AdminController(private val platformRepo: PlatformRepository, // generate and store new platform with authorization token //TODO: schedule deletion after 30 days if status still PLANNED (?) val tokenA = generateUUIDv4Token() - val platform = PlatformEntity(auth = Auth(tokenA = tokenA)) + val platform = PlatformEntity(auth = Auth(tokenA = tokenA.toBs64String())) platformRepo.save(platform) val responseBody = RegistrationInfo(tokenA, urlJoin(properties.url, "/ocpi/versions")) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsController.kt index b390854..0fa8179 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsController.kt @@ -29,11 +29,7 @@ import snc.openchargingnetwork.node.models.ocpi.Role import snc.openchargingnetwork.node.repositories.* import snc.openchargingnetwork.node.services.HttpService import snc.openchargingnetwork.node.services.RegistryService -import snc.openchargingnetwork.node.tools.extractToken -import snc.openchargingnetwork.node.tools.generateUUIDv4Token -import snc.openchargingnetwork.node.tools.getTimestamp -import snc.openchargingnetwork.node.tools.urlJoin - +import snc.openchargingnetwork.node.tools.* @RestController @RequestMapping("/ocpi/2.2/credentials") @@ -64,7 +60,7 @@ class CredentialsController(private val platformRepo: PlatformRepository, OcpiResponse( statusCode = OcpiStatus.SUCCESS.code, - data = myCredentials(it.auth.tokenC!!)) + data = myCredentials(it.auth.tokenC!!.fromBs64String())) } ?: throw OcpiClientInvalidParametersException("Invalid CREDENTIALS_TOKEN_C") } @@ -82,14 +78,14 @@ class CredentialsController(private val platformRepo: PlatformRepository, ?: throw OcpiClientInvalidParametersException("Invalid CREDENTIALS_TOKEN_A") // GET versions information endpoint with TOKEN_B (both provided in request body) - val versionsInfo = httpService.getVersions(body.url, body.token) + val versionsInfo = httpService.getVersions(body.url, body.token.toBs64String()) // try to match version 2.2 val correctVersion = versionsInfo.firstOrNull { it.version == "2.2" } ?: throw OcpiServerNoMatchingEndpointsException("Expected version 2.2 from $versionsInfo") // GET 2.2 version details - val versionDetail = httpService.getVersionDetail(correctVersion.url, body.token) + val versionDetail = httpService.getVersionDetail(correctVersion.url, body.token.toBs64String()) // ensure each role does not already exist; delete if planned for (role in body.roles) { @@ -109,7 +105,7 @@ class CredentialsController(private val platformRepo: PlatformRepository, val tokenC = generateUUIDv4Token() // set platform connection details - platform.auth = Auth(tokenA = null, tokenB = body.token, tokenC = tokenC) + platform.auth = Auth(tokenA = null, tokenB = body.token.toBs64String(), tokenC = tokenC.toBs64String()) platform.versionsUrl = body.url platform.status = ConnectionStatus.CONNECTED platform.lastUpdated = getTimestamp() @@ -157,20 +153,20 @@ class CredentialsController(private val platformRepo: PlatformRepository, ?: throw OcpiClientInvalidParametersException("Invalid CREDENTIALS_TOKEN_C") // GET versions information endpoint with TOKEN_B (both provided in request body) - val versionsInfo: List = httpService.getVersions(body.url, body.token) + val versionsInfo: List = httpService.getVersions(body.url, body.token.toBs64String()) // try to match version 2.2 val correctVersion = versionsInfo.firstOrNull { it.version == "2.2" } ?: throw OcpiClientInvalidParametersException("Expected version 2.2 from ${body.url}") // GET 2.2 version details - val versionDetail = httpService.getVersionDetail(correctVersion.url, body.token) + val versionDetail = httpService.getVersionDetail(correctVersion.url, body.token.toBs64String()) // generate TOKEN_C val tokenC = generateUUIDv4Token() // set platform connection information - platform.auth = Auth(tokenA = null, tokenB = body.token, tokenC = tokenC) + platform.auth = Auth(tokenA = null, tokenB = body.token.toBs64String(), tokenC = tokenC.toBs64String()) platform.versionsUrl = body.url platform.status = ConnectionStatus.CONNECTED platform.lastUpdated = getTimestamp() diff --git a/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt b/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt index b653b78..ae31bda 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt @@ -46,7 +46,7 @@ data class OcnHeaders(@JsonProperty("Authorization") val authorization: String, val sender: BasicRole, val receiver: BasicRole) { - fun toMap(routingHeaders: Boolean = true): Map { + fun toMap(routingHeaders: Boolean = true): MutableMap { val map = mutableMapOf() map["Authorization"] = authorization map["OCN-Signature"] = signature diff --git a/src/main/kotlin/snc/openchargingnetwork/node/models/ocpi/Locations.kt b/src/main/kotlin/snc/openchargingnetwork/node/models/ocpi/Locations.kt index 2926494..a580943 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/models/ocpi/Locations.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/models/ocpi/Locations.kt @@ -84,9 +84,9 @@ data class AdditionalGeoLocation(@JsonProperty("latitude") val latitude: String, @JsonInclude(JsonInclude.Include.NON_NULL) data class Hours(@JsonProperty("twentyfourseven") val twentyfourseven: Boolean, - @JsonProperty("regular_hours") val regularHours: RegularHours? = null, - @JsonProperty("exceptional_openings") val exceptionalOpenings: ExceptionalPeriod? = null, - @JsonProperty("exceptional_closings") val exceptionalClosings: ExceptionalPeriod? = null) + @JsonProperty("regular_hours") val regularHours: List? = null, + @JsonProperty("exceptional_openings") val exceptionalOpenings: List? = null, + @JsonProperty("exceptional_closings") val exceptionalClosings: List? = null) data class RegularHours(@JsonProperty("weekday") val weekday: Int, @JsonProperty("period_begin") val periodBegin: String, @@ -253,4 +253,4 @@ enum class ParkingType { ON_DRIVEWAY, ON_STREET, UNDERGROUND_GARAGE -} \ No newline at end of file +} diff --git a/src/main/kotlin/snc/openchargingnetwork/node/scheduledTasks/PlannedPartySearch.kt b/src/main/kotlin/snc/openchargingnetwork/node/scheduledTasks/PlannedPartySearch.kt index c3a6fb0..907d730 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/scheduledTasks/PlannedPartySearch.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/scheduledTasks/PlannedPartySearch.kt @@ -51,8 +51,17 @@ class PlannedPartySearch(private val registry: Registry, } .filter { val isMyParty = it.nodeOperator == myAddress + isMyParty + } + .filter { + val partyHasBeenDeleted = it.nodeOperator == "0x0000000000000000000000000000000000000000" + !partyHasBeenDeleted + } + .filter { + // Doing completed registration check after deleted party check as null countryCode and partyId + // from deleted parties may cause an issue with query for postgresql val hasCompletedRegistration = roleRepo.existsByCountryCodeAndPartyIDAllIgnoreCase(it.party.country, it.party.id) - isMyParty && !hasCompletedRegistration + !hasCompletedRegistration } for (party in plannedParties) { diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt index 35b7b2d..b0c52a5 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt @@ -41,7 +41,7 @@ class AsyncTaskService(private val registryService: RegistryService, private val // and also forward if service interface option is enabled val isDefaultModule = requestHandler.request.module != ModuleID.CUSTOM - if (isDefaultModule && fromLocalPlatform && properties.serviceEnabled) { + if (isDefaultModule && fromLocalPlatform && properties.serviceInterfaceEnabled) { val request = requestHandler.request registryService.getAgreementsByInterface(request.headers.sender, request.module, request.interfaceRole) .forEach { diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/HttpService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/HttpService.kt index 76e017e..035e663 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/HttpService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/HttpService.kt @@ -41,13 +41,13 @@ class HttpService { /** * Generic HTTP request expecting a response of type OcpiResponse as defined by the caller */ - fun makeOcpiRequest(method: HttpMethod, url: String, headers: Map, params: Map? = null, json: Map? = null): HttpResponse { + fun makeOcpiRequest(method: HttpMethod, url: String, headers: Map, params: Map? = null, data: String? = null): HttpResponse { val paramsWithStringValues = params?.mapValues { (_, value) -> value.toString() } ?: mapOf() val response = when (method) { HttpMethod.GET -> khttp.get(url, headers, paramsWithStringValues) - HttpMethod.POST -> khttp.post(url, headers, paramsWithStringValues, json = json) - HttpMethod.PUT -> khttp.put(url, headers, paramsWithStringValues, json = json) - HttpMethod.PATCH -> khttp.patch(url, headers, paramsWithStringValues, json = json) + HttpMethod.POST -> khttp.post(url, headers, paramsWithStringValues, data = data) + HttpMethod.PUT -> khttp.put(url, headers, paramsWithStringValues, data = data) + HttpMethod.PATCH -> khttp.patch(url, headers, paramsWithStringValues, data = data) HttpMethod.DELETE -> khttp.delete(url, headers) else -> throw IllegalStateException("Invalid method: $method") } @@ -67,17 +67,19 @@ class HttpService { * Generic HTTP request expecting a response of type OcpiResponse as defined by the caller */ final fun makeOcpiRequest(url: String, - headers: OcnHeaders, + ocnHeaders: OcnHeaders, requestVariables: OcpiRequestVariables): HttpResponse { // includes or excludes routing headers based on module type (functional or configuration) // TODO: credentials and versions must also include X-Request-ID/X-Correlation-ID - val headersMap = headers.toMap(routingHeaders = !configurationModules.contains(requestVariables.module)) + val headersMap = ocnHeaders.toMap(routingHeaders = !configurationModules.contains(requestVariables.module)) - var jsonBody: Map? = null + var jsonBody: String? = null if (requestVariables.body != null) { - val jsonString = mapper.writeValueAsString(requestVariables.body) - jsonBody = mapper.readValue(jsonString) + // Setting content-type to json as this is the expected format for standard and custom OCPI modules + headersMap["content-type"] = "application/json" + // If the request body is a String, we assume that it is already JSON + jsonBody = if (requestVariables.body is String) requestVariables.body else mapper.writeValueAsString(requestVariables.body) } return makeOcpiRequest( @@ -85,7 +87,7 @@ class HttpService { url = url, headers = headersMap, params = requestVariables.queryParams, - json = jsonBody) + data = jsonBody) } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt index 04fd6fc..1b839bf 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt @@ -30,6 +30,7 @@ import snc.openchargingnetwork.node.repositories.* import snc.openchargingnetwork.node.tools.extractToken import snc.openchargingnetwork.node.tools.generateUUIDv4Token import snc.openchargingnetwork.node.tools.urlJoin +import java.lang.IllegalArgumentException @Service class RoutingService(private val platformRepo: PlatformRepository, diff --git a/src/main/kotlin/snc/openchargingnetwork/node/tools/Extensions.kt b/src/main/kotlin/snc/openchargingnetwork/node/tools/Extensions.kt index 628ab5d..c5d1304 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/tools/Extensions.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/tools/Extensions.kt @@ -17,9 +17,13 @@ package snc.openchargingnetwork.node.tools import org.web3j.crypto.Keys +import java.nio.charset.Charset fun String.extractToken() = split(" ").last() +fun String.toBs64String(): String = bs64Encoder.encode(toByteArray()).toString(Charset.defaultCharset()) +fun String.fromBs64String(): String = bs64Decoder.decode(toByteArray()).toString(Charset.defaultCharset()) + fun String.extractNextLink(): String? { val next = split(", ").find { it.contains("; rel=\"next\"") } return next?.let { diff --git a/src/main/kotlin/snc/openchargingnetwork/node/tools/Tools.kt b/src/main/kotlin/snc/openchargingnetwork/node/tools/Tools.kt index 157cd2c..b7e087a 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/tools/Tools.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/tools/Tools.kt @@ -19,7 +19,10 @@ package snc.openchargingnetwork.node.tools import org.web3j.crypto.Keys import java.time.Instant import java.time.format.DateTimeFormatter -import java.util.UUID +import java.util.* + +val bs64Encoder: Base64.Encoder = Base64.getEncoder() +val bs64Decoder: Base64.Decoder = Base64.getDecoder() fun generateUUIDv4Token(): String { return UUID.randomUUID().toString() diff --git a/src/main/resources/application.dev.properties b/src/main/resources/application.dev.properties index 348646b..ce93c20 100644 --- a/src/main/resources/application.dev.properties +++ b/src/main/resources/application.dev.properties @@ -1,5 +1,6 @@ # spring JPA spring.jpa.open-in-view = false +#spring.jpa.hibernate.ddl-auto=update # http request handling server.error.include-stacktrace = never diff --git a/src/main/resources/application.prod.properties b/src/main/resources/application.prod.properties index 8b4c6e5..4dab5ee 100644 --- a/src/main/resources/application.prod.properties +++ b/src/main/resources/application.prod.properties @@ -33,3 +33,4 @@ ocn.node.signatures = true ocn.node.url = http://localhost:8080 ocn.node.web3.provider = https://rpc.energyweb.org/ ocn.node.web3.contracts.registry = 0x184aeD70F2aaB0Cd1FC62261C1170560cBfd0776 +ocn.node.web3.contracts.permissions = 0x9a59513F0C9Aa56CB4D3f3bAe0513868907BB978 diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsControllerTest.kt index 3921d8e..be27f15 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsControllerTest.kt @@ -21,6 +21,7 @@ import snc.openchargingnetwork.node.models.ocpi.Role import snc.openchargingnetwork.node.repositories.* import snc.openchargingnetwork.node.services.HttpService import snc.openchargingnetwork.node.services.RegistryService +import snc.openchargingnetwork.node.tools.bs64Encoder @WebMvcTest(CredentialsController::class) class CredentialsControllerTest(@Autowired val mockMvc: MockMvc) { @@ -49,9 +50,12 @@ class CredentialsControllerTest(@Autowired val mockMvc: MockMvc) { @MockkBean lateinit var httpService: HttpService + private fun enc(token: String): String = bs64Encoder.encodeToString(token.toByteArray()) + @Test fun `When GET credentials then return broker credentials`() { - val platform = PlatformEntity(auth = Auth(tokenC = "0987654321")) + val tokenC = "0987654321" + val platform = PlatformEntity(auth = Auth(tokenC = enc(tokenC))) every { platformRepo.findByAuth_TokenC(platform.auth.tokenC) } returns platform every { properties.url } returns "http://localhost:8001" mockMvc.perform(get("/ocpi/2.2/credentials") @@ -61,7 +65,7 @@ class CredentialsControllerTest(@Autowired val mockMvc: MockMvc) { .andExpect(jsonPath("\$.status_code").value(OcpiStatus.SUCCESS.code)) .andExpect(jsonPath("\$.status_message").doesNotExist()) .andExpect(jsonPath("\$.timestamp").isString) - .andExpect(jsonPath("\$.data.token").value(platform.auth.tokenC!!)) + .andExpect(jsonPath("\$.data.token").value(tokenC)) .andExpect(jsonPath("\$.data.url").value("http://localhost:8001/ocpi/versions")) .andExpect(jsonPath("\$.data.roles", Matchers.hasSize>(1))) .andExpect(jsonPath("\$.data.roles[0].role").value("HUB")) @@ -74,7 +78,7 @@ class CredentialsControllerTest(@Autowired val mockMvc: MockMvc) { fun `When POST credentials then return broker credentials and TOKEN_C`() { val platform = PlatformEntity( id = 1L, - auth = Auth(tokenA = "12345"), + auth = Auth(tokenA = enc("12345")), rules = OcnRules(signatures = true, blacklist = false, whitelist = false)) val role1 = CredentialsRole( @@ -93,10 +97,10 @@ class CredentialsControllerTest(@Autowired val mockMvc: MockMvc) { val tokenB = "67890" every { platformRepo.findByAuth_TokenA(platform.auth.tokenA) } returns platform - every { httpService.getVersions(versionsUrl, tokenB) } returns listOf(Version( + every { httpService.getVersions(versionsUrl, enc(tokenB)) } returns listOf(Version( version = "2.2", url = versionDetailUrl)) - every { httpService.getVersionDetail(versionDetailUrl, tokenB) } returns VersionDetail( + every { httpService.getVersionDetail(versionDetailUrl, enc(tokenB)) } returns VersionDetail( version = "2.2", endpoints = listOf( Endpoint("credentials", InterfaceRole.SENDER, "https://org.charging.net/credentials"), @@ -117,7 +121,7 @@ class CredentialsControllerTest(@Autowired val mockMvc: MockMvc) { every { roleRepo.saveAll(any>())} returns mockk() mockMvc.perform(post("/ocpi/2.2/credentials") - .header("Authorization", "Token ${platform.auth.tokenA}") + .header("Authorization", "Token ${platform.auth.tokenA!!}") .contentType(MediaType.APPLICATION_JSON) .content(jacksonObjectMapper().writeValueAsString(Credentials( token = tokenB, @@ -139,9 +143,10 @@ class CredentialsControllerTest(@Autowired val mockMvc: MockMvc) { @Test fun `When PUT credentials then return broker credentials and new TOKEN_C`() { + val tokenB = "67890" val platform = PlatformEntity( id = 1L, - auth = Auth(tokenA = null, tokenB = "67890", tokenC = "0102030405"), + auth = Auth(tokenA = null, tokenB = enc(tokenB), tokenC = enc("0102030405")), rules = OcnRules(signatures = false, whitelist = true, blacklist = false)) val role1 = CredentialsRole( @@ -180,7 +185,7 @@ class CredentialsControllerTest(@Autowired val mockMvc: MockMvc) { .header("Authorization", "Token ${platform.auth.tokenC}") .contentType(MediaType.APPLICATION_JSON) .content(jacksonObjectMapper().writeValueAsString(Credentials( - token = platform.auth.tokenB!!, + token = tokenB!!, url = versionsUrl, roles = listOf(role1, role2))))) .andExpect(status().isOk) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/CustomModulesIntegrationTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/CustomModulesIntegrationTest.kt index 7786333..f2fe837 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/CustomModulesIntegrationTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/CustomModulesIntegrationTest.kt @@ -2,8 +2,11 @@ package snc.openchargingnetwork.node.integration import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.* +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource import snc.openchargingnetwork.node.integration.utils.* import snc.openchargingnetwork.node.models.OcnRulesListType +import java.util.stream.Stream class CustomModulesIntegrationTest { @@ -25,6 +28,23 @@ class CustomModulesIntegrationTest { stopPartyServers(networkComponents) } + private fun testMessagePostWithBody(cpo: TestCpo, requestBody: String) { + val response = msp.server.postCustomModuleRequestWithBody(cpo.party, requestBody) + val json = response.jsonObject + assertThat(response.statusCode).isEqualTo(200) + assertThat(json.get("status_code")).isEqualTo(1000) + + // Expecting that request body is echoed back + assertThat(json.get("data")).isEqualTo(requestBody) + } + + private fun testMessagePostWithoutBody(cpo: TestCpo) { + val response = msp.server.postCustomModuleRequestWithoutBody(cpo.party) + val json = response.jsonObject + assertThat(response.statusCode).isEqualTo(200) + assertThat(json.get("status_code")).isEqualTo(1000) + } + private fun testMessageReceived(cpo: TestCpo) { val response = msp.server.sendCustomModuleRequest(cpo.party) val json = response.jsonObject @@ -51,6 +71,14 @@ class CustomModulesIntegrationTest { assertThat(json.get("status_message")).isEqualTo("Sender not whitelisted to request lite-locations from receiver.") } + private fun requestBodies(): Stream { + return Stream.of( + "simple string", + """[{"hello":"world"},{"beautiful":"day"}]""", + """{"key1":"value1"}""", + """{"key1":["value1","value2"]}""") + } + @Test fun `can send and receive custom module messages locally`() { testMessageReceived(cpo1) @@ -61,6 +89,20 @@ class CustomModulesIntegrationTest { testMessageReceived(cpo2) } + @ParameterizedTest + @MethodSource("requestBodies") + fun `can post custom module messages locally`(body: String) { + testMessagePostWithBody(cpo1, body) + testMessagePostWithoutBody(cpo1) + } + + @ParameterizedTest + @MethodSource("requestBodies") + fun `can post custom module messages remotely`(body: String) { + testMessagePostWithBody(cpo2, body) + testMessagePostWithoutBody(cpo2) + } + @Test fun `can be blocked locally`() { testBlocked(cpo1) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/IntegrationTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/IntegrationTest.kt index c9d422b..721b559 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/IntegrationTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/IntegrationTest.kt @@ -233,4 +233,5 @@ class IntegrationTest { sleep(1000) } } + } \ No newline at end of file diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/ServiceInterfaceTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/ServiceInterfaceTest.kt new file mode 100644 index 0000000..d9ca29b --- /dev/null +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/ServiceInterfaceTest.kt @@ -0,0 +1,65 @@ +package snc.openchargingnetwork.node.integration + +import org.awaitility.kotlin.await +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.springframework.http.HttpMethod +import snc.openchargingnetwork.node.integration.utils.* +import snc.openchargingnetwork.node.models.OcnServicePermission +import snc.openchargingnetwork.node.models.ocpi.InterfaceRole +import snc.openchargingnetwork.node.models.ocpi.ModuleID +import java.util.concurrent.TimeUnit + +class ServiceInterfaceTest { + + // TODO: integration test setup could be in an inheritable class + private lateinit var networkComponents: NetworkComponents + private lateinit var cpo1: TestCpo + private lateinit var cpo2: TestCpo + private lateinit var msp: TestMsp + + + @BeforeEach + fun bootStrap() { + networkComponents = setupNetwork(HubClientInfoParams()) + cpo1 = networkComponents.cpos[0] + cpo2 = networkComponents.cpos[1] + msp = networkComponents.msps.first() + } + + @AfterEach + fun stopTestParties() { + stopPartyServers(networkComponents) + } + + private fun seenByBothCpos(): Boolean { + val message = ReceivedMessage( + module = ModuleID.LOCATIONS, + interfaceRole = InterfaceRole.SENDER, + method = HttpMethod.GET, + sender = msp.party) + val cpo1Seen = cpo1.server.messageStore.contains(message) + val cpo2Seen = cpo2.server.messageStore.contains(message) + return cpo1Seen && cpo2Seen + } + + private fun testForwarding(recipient: TestCpo, service: TestCpo) { + service.server.setServicePermissions(listOf(OcnServicePermission.FORWARD_ALL)) + msp.server.agreeToServicePermissions(service.address) + msp.server.getLocation(recipient.party) + await.atMost(2L, TimeUnit.SECONDS).until { seenByBothCpos() } + } + + @Test + fun fowardsRequestToService_Local() { + testForwarding(recipient = cpo2, service = cpo1) + + } + + @Test + fun fowardsRequestToService_Remote() { + testForwarding(recipient = cpo1, service = cpo2) + } + +} \ No newline at end of file diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/TokenEncodingIntegrationTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/TokenEncodingIntegrationTest.kt new file mode 100644 index 0000000..1d46193 --- /dev/null +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/TokenEncodingIntegrationTest.kt @@ -0,0 +1,173 @@ +package snc.openchargingnetwork.node.integration + +import io.javalin.Javalin +import khttp.responses.Response +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import org.web3j.crypto.Credentials +import org.web3j.tx.ClientTransactionManager +import shareandcharge.openchargingnetwork.notary.Notary +import shareandcharge.openchargingnetwork.notary.SignableHeaders +import shareandcharge.openchargingnetwork.notary.ValuesToSign +import snc.openchargingnetwork.contracts.Registry +import snc.openchargingnetwork.node.integration.parties.CpoServer +import snc.openchargingnetwork.node.integration.utils.* +import snc.openchargingnetwork.node.models.entities.* +import snc.openchargingnetwork.node.models.ocpi.* +import snc.openchargingnetwork.node.repositories.EndpointRepository +import snc.openchargingnetwork.node.repositories.PlatformRepository +import snc.openchargingnetwork.node.repositories.RoleRepository +import snc.openchargingnetwork.node.tools.generateUUIDv4Token +import java.math.BigInteger + +class TokenEncodingIntegrationTest { + + private lateinit var networkComponents: NetworkComponents + private lateinit var node: OcnNode + private lateinit var cpo: CpoServer + + private lateinit var platformRepo: PlatformRepository + private lateinit var roleRepo: RoleRepository + private lateinit var endpointRepo: EndpointRepository + + object LegacyMsp { + private const val port = 8050 + private val credentials = Credentials.create( + "0x82d052c865f5763aad42add438569276c00d3d88a2d062d36b2bae914d58b8c8" + ) + + val platform = PlatformEntity( + // auth tokens stored in ascii, not bs64 encoded + auth = Auth(tokenB = generateUUIDv4Token(), tokenC = generateUUIDv4Token()), + status = ConnectionStatus.CONNECTED, + versionsUrl = "http://localhost:$port") + var role = RoleEntity( + platformID = 0L, + countryCode = "TR", + partyID = "ABC", + role = Role.EMSP, + businessDetails = BusinessDetails(name = "abc industries")) + var endpoint = EndpointEntity( + platformID = 0L, + url = "http://localhost:$port/ocpi/cdrs", + identifier = "cdrs", + role = InterfaceRole.RECEIVER) + + private val app = Javalin.create().start(port) + + init { + app.exception(JavalinException::class.java) { e, ctx -> + ctx.status(e.httpCode).json(OcpiResponse(statusCode = e.ocpiCode, statusMessage = e.message)) + } + app.before { + if (it.header("Authorization") != "Token ${platform.auth.tokenB!!}") { + throw JavalinException(message = "Unauthorized") + } + } + app.get("/ocpi/versions") { + it.json(OcpiResponse( + statusCode = 1000, + data = listOf(Version(version = "2.2", url = "http://localhost:$port/ocpi/versions/2.2")) + )) + } + app.get("/ocpi/versions/2.2") { + it.json(OcpiResponse( + statusCode = 1000, + data = VersionDetail( + version = "2.2", + endpoints = listOf(Endpoint(endpoint.identifier, endpoint.role, endpoint.url))) + )) + } + app.post("/ocpi/cdrs") { + val body = OcpiResponse(statusCode = 1000) + val notary = Notary().sign(ValuesToSign(body = body), credentials.privateKey()) + it.json(body.copy(signature = notary.serialize())) + } + } + + fun register(contracts: OcnContracts, node: NodeDefinition) { + // register in smart contract; already "registered" on ocn node + val registry = Registry.load( + contracts.registry.contractAddress, + web3, + ClientTransactionManager(web3, credentials.address), + gasProvider) + + val call = registry.setParty( + role.countryCode.toByteArray(), + role.partyID.toByteArray(), + listOf(BigInteger.ZERO), + node.credentials.address + ) + call.sendAsync().get() + } + + fun sendLocationsRequest(node: NodeDefinition, to: BasicRole): Response { + val headers = SignableHeaders( + correlationId = "0", + fromCountryCode = role.countryCode, + fromPartyId = role.partyID, + toCountryCode = to.country, + toPartyId = to.id + ) + val notary = Notary().sign(ValuesToSign(headers = headers), credentials.privateKey()) + return khttp.get( + url = "http://localhost:${node.port}/ocpi/sender/2.2/locations", + headers = headers.toMap(platform.auth.tokenC!!, notary.serialize()) + ) + } + + fun stop(): Javalin = app.stop() + } + + @BeforeAll + fun beforeAll() { + networkComponents = setupNetwork(HubClientInfoParams()) + cpo = networkComponents.cpos[0].server + node = networkComponents.nodes[0] + + platformRepo = node.appContext.getBean(PlatformRepository::class.java) + roleRepo = node.appContext.getBean(RoleRepository::class.java) + endpointRepo = node.appContext.getBean(EndpointRepository::class.java) + + // enter platform/party/endpoints in respective repositories + val platform = platformRepo.save(LegacyMsp.platform) + + LegacyMsp.role.platformID = platform.id!! + LegacyMsp.endpoint.platformID = platform.id!! + roleRepo.save(LegacyMsp.role) + endpointRepo.save(LegacyMsp.endpoint) + + // enter in ocn registry + LegacyMsp.register(networkComponents.contracts, node.definition) + } + + @AfterAll + fun afterAll() { + stopPartyServers(networkComponents) + LegacyMsp.stop() + } + + @Test + fun legacy_auth_incoming() { + // tests that a previously registered party can still use their ascii token + // (no encoding) to make requests to the OCN + val response = LegacyMsp.sendLocationsRequest(node.definition, cpo.config.party) + assertThat(response.statusCode).isEqualTo(200) + assertThat(response.jsonObject.get("status_code")).isEqualTo(1000) + } + + @Test + fun legacy_auth_outgoing() { + // tests that a previously registered party can still receive requests from the + // OCN (no encoding) + val response = cpo.sendCdr(to = BasicRole( + id = LegacyMsp.role.partyID, + country = LegacyMsp.role.countryCode)) + assertThat(response.statusCode).isEqualTo(200) + assertThat(response.jsonObject.get("status_code")).isEqualTo(1000) + } + +} \ No newline at end of file diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/CpoServer.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/CpoServer.kt index 5567d35..76d6621 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/CpoServer.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/CpoServer.kt @@ -100,6 +100,12 @@ class CpoServer(config: PartyDefinition, contracts: OcnContracts): PartyServer(c it.json(body) } + app.post("/ocpi/cpo/2.2/lite-locations") { + val body = OcpiResponse(statusCode = 1000, data = it.body()) + body.signature = sign(body = body) + it.json(body) + } + } fun sendCdr(to: BasicRole): Response { diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/MspServer.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/MspServer.kt index 0ca032c..b59bd1f 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/MspServer.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/MspServer.kt @@ -111,4 +111,23 @@ class MspServer(config: PartyDefinition, contracts: OcnContracts): PartyServer(c return khttp.get("$node/ocpi/custom/sender/lite-locations", headers = headers.toMap(tokenC, signature)) } + fun postCustomModuleRequestWithBody(to: BasicRole, requestBody: String): Response { + val headers = getSignableHeaders(to) + val signature = sign(headers = headers, body = requestBody) + val headersMap = headers.toMap(tokenC, signature) + headersMap["content-type"] = "application/json" + return khttp.post( + url = "$node/ocpi/custom/sender/lite-locations", + headers = headersMap, + data = requestBody) + } + + fun postCustomModuleRequestWithoutBody(to: BasicRole): Response { + val headers = getSignableHeaders(to) + val signature = sign(headers = headers) + return khttp.post( + url = "$node/ocpi/custom/sender/lite-locations", + headers = headers.toMap(tokenC, signature)) + } + } \ No newline at end of file diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/PartyServer.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/PartyServer.kt index 5a5de77..c5693ea 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/PartyServer.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/PartyServer.kt @@ -12,6 +12,7 @@ import snc.openchargingnetwork.node.models.OcnServicePermission import snc.openchargingnetwork.node.models.OcnRulesListType import snc.openchargingnetwork.node.models.ocpi.* import snc.openchargingnetwork.node.tools.generateUUIDv4Token +import snc.openchargingnetwork.node.tools.toBs64String open class PartyServer(val config: PartyDefinition, deployedContracts: OcnContracts) { @@ -36,7 +37,7 @@ open class PartyServer(val config: PartyDefinition, deployedContracts: OcnContra } app.before { - if (it.header("Authorization") != "Token $tokenB") { + if (it.header("Authorization") != "Token ${tokenB.toBs64String()}") { throw JavalinException(message = "Unauthorized") } } @@ -81,7 +82,8 @@ open class PartyServer(val config: PartyDefinition, deployedContracts: OcnContra fun registerCredentials() { // TODO: could also request versions and store endpoints in memory - val tokenA = getTokenA(node, listOf(config.party)) + val tokenA = getTokenA(node, listOf(config.party)).toBs64String() + val response = khttp.post("$node/ocpi/2.2/credentials", headers = mapOf("Authorization" to "Token $tokenA"), json = coerceToJson(Credentials( @@ -92,7 +94,12 @@ open class PartyServer(val config: PartyDefinition, deployedContracts: OcnContra businessDetails = BusinessDetails(name = "Some CPO"), countryCode = config.party.country, partyID = config.party.id))))) - tokenC = response.jsonObject.getJSONObject("data").getString("token") + + tokenC = response + .jsonObject + .getJSONObject("data") + .getString("token") + .let { it.toBs64String() } } fun deleteCredentials() { diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/utils/Helpers.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/utils/Helpers.kt index ff6b010..d81ed76 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/utils/Helpers.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/utils/Helpers.kt @@ -24,7 +24,7 @@ fun Credentials.privateKey(): String { return ecKeyPair.privateKey.toString(16) } -fun SignableHeaders.toMap(tokenC: String, signature: String): Map { +fun SignableHeaders.toMap(tokenC: String, signature: String): MutableMap { val map = mutableMapOf() map["Authorization"] = "Token $tokenC" map["OCN-Signature"] = signature @@ -35,4 +35,6 @@ fun SignableHeaders.toMap(tokenC: String, signature: String): Map = listOf( PartyDefinition( nodeNumber = 1, diff --git a/src/test/kotlin/snc/openchargingnetwork/node/scheduledTasks/PlannedPartySearchTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/scheduledTasks/PlannedPartySearchTest.kt new file mode 100644 index 0000000..21a8e5e --- /dev/null +++ b/src/test/kotlin/snc/openchargingnetwork/node/scheduledTasks/PlannedPartySearchTest.kt @@ -0,0 +1,52 @@ +package snc.openchargingnetwork.node.scheduledTasks + +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import org.junit.jupiter.api.Test +import org.web3j.tuples.generated.Tuple7 +import snc.openchargingnetwork.contracts.Registry +import snc.openchargingnetwork.node.config.NodeProperties +import snc.openchargingnetwork.node.models.entities.NetworkClientInfoEntity +import snc.openchargingnetwork.node.repositories.NetworkClientInfoRepository +import snc.openchargingnetwork.node.repositories.RoleRepository + +class PlannedPartySearchTest { + + private val registry: Registry = mockk() + private val roleRepo: RoleRepository = mockk() + private val networkClientInfoRepo: NetworkClientInfoRepository = mockk() + private val properties: NodeProperties = mockk() + + private val plannedPartySearch: PlannedPartySearch + + init { + plannedPartySearch = PlannedPartySearch( + registry, + roleRepo, + networkClientInfoRepo, + properties + ) + } + + @Test + fun deletedParty_should_notBePlanned() { + every { properties.privateKey } returns "c6cbd7d76bc5baca530c875663711b947efa6a86a900a9e8645ce32e5821484e" + val address = "0xc0ffee254729296a45a3885639AC7E10F9d54979" + every { registry.parties.sendAsync().get() } returns listOf(address) + every { registry.getPartyDetailsByAddress(address).sendAsync().get() } returns + Tuple7( + ByteArray(2), + ByteArray(3), + emptyList(), + emptyList(), + emptyList(), + "0x0000000000000000000000000000000000000000", + "" + ) + + plannedPartySearch.run() + verify(exactly = 0) { roleRepo.existsByCountryCodeAndPartyIDAllIgnoreCase(any(), any()) } + verify(exactly = 0) { networkClientInfoRepo.save(any()) } + } +}