Inteli OpenAPI service for interacting with the DSCP (Digital Supply-Chain Platform)
inteli-api is configured primarily using environment variables as follows:
| variable | required | default | description |
|---|---|---|---|
| SERVICE_TYPE | N | info |
Logging level. Valid values are [trace, debug, info, warn, error, fatal] |
| PORT | N | 80 |
The port for the API to listen on |
| EXTERNAL_ORIGIN | N | The origin from which the OpenAPI service is accessible. If not provided the value will default to http://localhost:${PORT} |
|
| EXTERNAL_PATH_PREFIX | N | A path prefix from which this service is served | |
| LOG_LEVEL | N | info |
Logging level. Valid values are [trace, debug, info, warn, error, fatal] |
| API_VERSION | N | package.json version |
API version |
| API_MAJOR_VERSION | N | v1 |
API major version |
| DSCP_API_HOST | Y | - | dscp-api host |
| DSCP_API_PORT | Y | - | dscp-api port |
| DB_HOST | Y | - | PostgreSQL database hostname |
| DB_PORT | N | 5432 |
PostgreSQL database port |
| DB_NAME | N | inteli |
PostgreSQL database name |
| DB_USERNAME | Y | - | PostgreSQL database username |
| DB_PASSWORD | Y | - | PostgreSQL database password |
| FILE_UPLOAD_SIZE_LIMIT_BYTES | N | 1024 * 1024 * 100 |
Maximum file size in bytes for upload |
| IDENTITY_SERVICE_HOST | Y | Hostname of the dscp-identity-service |
|
| IDENTITY_SERVICE_PORT | Y | Port of the dscp-identity-service |
|
| AUTH_TYPE | N | NONE |
Authentication type for routes on the service. Valid values: [NONE, JWT] |
The following environment variables are additionally used when AUTH_TYPE : 'JWT'
| variable | required | default | description |
|---|---|---|---|
| AUTH_JWKS_URI | N | https://inteli.eu.auth0.com/.well-known/jwks.json |
JSON Web Key Set containing public keys used by the Auth0 API |
| AUTH_AUDIENCE | N | inteli-dev |
Identifier of the Auth0 API |
| AUTH_ISSUER | N | https://inteli.eu.auth0.com/ |
Domain of the Auth0 API ` |
| AUTH_TOKEN_URL | N | https://inteli.eu.auth0.com/oauth/token |
Auth0 API endpoint that issues an Authorisation (Bearer) access token |
Start dependencies:
docker compose up -d
Install packages:
npm i
Run DB migrations:
npx knex migrate:latest --env test
Run the application in development mode:
npm run devAssuming devDefault environment variables, view OpenAPI documentation for all routes with Swagger:
localhost:3000/v1/swagger/
The Swagger route is constructed as localhost:{PORT}/{API_MAJOR_VERSION}/swagger. OpenAPI docs can also be viewed as JSON localhost:{PORT}/{API_MAJOR_VERSION}/api-docs.
If AUTH_TYPE env is set to JWT, the endpoints on inteli-api require Bearer Authentication using a JSON Web Token. Tokens are generated externally as an Auth0 Machine to Machine token. You will need to create your own Auth0 API, which can be done for free, and set the appropriate environment variables (those prefixed with AUTH). Follow the start of this tutorial to create an API. Go here and here to see where the environment variables are used.
Integration tests for AUTH_TYPE: 'JWT' use a preconfigured Auth0 test application and user to authenticate across multiple dscp services. Follow the tutorial here to create your own.
Once a test application and user is created, running integration tests locally requires a /test/test.env file containing the following environment variables:
| variable | required | default | description |
|---|---|---|---|
| AUTH_TEST_USERNAME | Y | - | Username of the auth0 user for testing |
| AUTH_TEST_PASSWORD | Y | - | Password of the auth0 user for testing |
| AUTH_TEST_CLIENT_ID | Y | - | Client ID of the auth0 application for testing |
| AUTH_TEST_CLIENT_SECRET | Y | - | Client secret of the auth0 application for testing |
Start dependencies with AUTH_TYPE: 'JWT':
AUTH_TYPE=JWT docker compose up -d
Run tests:
npm run test:jwt
inteli-api provides a RESTful OpenAPI-based interface for third parties and front-ends to interact with the DSCP system. The design prioritises:
- RESTful design principles:
- all endpoints describing discrete operations on path derived entities.
- use of HTTP verbs to describe whether state is modified, whether the action is idempotent etc.
- HTTP response codes indicating the correct status of the request.
- HTTP response bodies including the details of a query response or details about the entity being created/modified.
- Simplicity of structure. The API should be easily understood by a third party and traversable.
- Simplicity of usage:
- all APIs that take request bodies taking a JSON structured request with the exception of attachment upload (which is idiomatically represented as a multipart form).
- all APIs which return a body returning a JSON structured response (again with the exception of attachments.
- Abstraction of the underlying DLT components. This means no token Ids, no block numbers etc.
- Conflict free identifiers. All identifiers must be conflict free as updates can come from third party organisations.
These are the top level physical concepts in the system. They are the top level RESTful path segments. Note that different states of an entity will NOT be represented as different top level entities.
recipeordersbuildpart
Additionally, there is one more top level entity attachment which accepts a multipart/form-data payload for uploading a file. This returns an attachmentId that can then be used when preparing entity updates to attach files.
Entity queries allow the API user to list those entities (including a query) and to get a specific entity. For order for example:
GET /order- list ordersGET /order/{orderId}- get order
Allows the creation of an initial local state for an entity. Note this is essentially just to establish an internal identifier for the entity and the state is not shared across the blockchain network at this point.
POST /order
Allows different kind of updates to be prepared and applied to an entity. For example, an order must be submitted via a submission action. Each update can have specific files attached along with other specific metadata.
POST /order/{orderId}/submission- create an ordersubmissionaction and send it to the blockchain.GET /order/{orderId}/submission- list ordersubmissionsand their status.GET /order/{orderId}/submission/{submissionId}- get the details of an ordersubmission.
The last top level entity attachment, which accepts a multipart/form-data payload for uploading a file or application/json for uploading JSON as a file. This will return an attachmentId that can then be used when preparing entity updates to attach files.
POST /attachmentGET /attachment/{attachmentId}
Demoing the routes in inteli-api requires two personas: buyer and supplier. Each persona runs their own instance of inteli-api and its dependencies.
Before transacting, each persona sets aliases in dscp-identity-service for other parties' node addresses so they can refer to them by human-friendly names. The self alias should also be set for a persona's own node address.
buyerwants to create arecipe, which describes how a particularsupplierwill make apart. Arecipealways includes an imageattachment, so firstbuyermust upload an image to their local database withPOST /attachment.- They use the returned
imageattachmentIdin the request body toPOST /recipe, as well as settingsupplier: 'supplier'and providing an array ofrequiredCertsfor therecipe. Later on,supplierwill need to add a certificate file for eachrequiredCertfor thepartbuilt from therecipe. At this point, therecipeonly exists in thebuyerdatabase. - When
buyeris ready for therecipeto exist on chain theyPOST recipe/{id}/creation.suppliercan now see therecipeif their node is running and connected. buyercreates anorderof 1-10recipesin their local database withPOST /order, again settingsupplier: 'supplier'. The request will fail if any of the listed recipes are set with a different supplier.- When
buyeris ready for theorderto exist on chain theyPOST order/{id}/submission. suppliercanPOST order/{id}/rejectionorPOST order/{id}/acceptancethe order. For simplicity they will accept, signifying their intent to buildpartsto fulfil theorder.suppliercreates abuildof 1-10recipesin their local database. They must have thesupplierrole for eachrecipe.- When
supplieris ready for thebuildto exist on chain theyPOST build/{id}/schedule. This also creates a newparton chain for eachrecipe. suppliercan assign an individualpartto anorderwithPOST part/{id}/order-assignment.itemIndexin the request body matches the index on theorderfor the specificrecipethat was used to build thatpart.builds(andparts) can be started and completed before anorderis made and later assigned to one.- A required certificate (
attachment) for apartis added with/part/{id}/certificationbysupplier.certificationIndexin the request matches the index on therecipefor therequiredCertthat the uploaded certificate is fulfilling. suppliercan change the estimated date of completion or add general build files (attachments) to thebuildwithPOST build/{id}/progress-update.suppliercan add general, non-required files (attachments) to thepartwithPOST build/{id}/metadata-update.- Finally,
suppliercan complete thebuildwithPOST build/{id}/completion.