diff --git a/README.md b/README.md index 97f3567..96d9152 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ This service allows to run SQL queries on the database. In particular, this service allows users with limited permissions to see reports of aggregated statistics across all data (e.g. a supervisor could analyse reports without having access to possibly confidential details of participants or notes). -## Usage +## Deployment See the [ndb-setup repo](https://github.com/Aam-Digital/ndb-setup) for full deployment instructions. To use this you need a running [CouchDB](https://docs.couchdb.org/en/stable/) and [structured query server (SQS)](https://neighbourhood.ie/products-and-services/structured-query-server). @@ -14,3 +14,44 @@ The following variables might need to be configured in the `.env` file: - `SCHEMA_CONFIG_ID` database ID of the document which holds the SQS schema (default `_design/sqlite:config`) - `PORT` where the app should listen (default 3000) - `SENTRY_DSN` for remote logging + +----- +# API access to reports +Reports and their results are available for external services through the given API endpoints ([see OpenAPI specs](./docs/api-specs/reporting-api-v1.yaml)). Endpoints require a valid JWT access token, which can be fetched via OAuth2 client credential flow. + +## Initial setup of an API integration +1. Request client_id and client_secret from server administrator (--> admin has to [create new client grant in Keycloak](https://www.keycloak.org/docs/latest/server_admin/#_oidc_clients)) + +![Keycloak Client Setup](docs/assets/keycloak-client-setup.png) + +2. Get the realm of your instance (e.g. https://[your_realm].aam-digital.com). This is both the subdomain of systems hosted on aam-digital.com and the Keycloak Realm for authentication (case sensitive!). + +## Access a report via API (after setup) + +1. Get valid access token using your client secret: + +```bash +curl -X "POST" "https://keycloak.aam-digital.net/realms/<your_realm>/protocol/openid-connect/token" \ + -H 'Content-Type: application/x-www-form-urlencoded; charset=utf-8' \ + --data-urlencode "client_id=<your_client_id>" \ + --data-urlencode "client_secret=<your_client_secret>" \ + --data-urlencode "grant_type=client_credentials" \ + --data-urlencode "scopes=openid reports_read reports_write" +``` +Check API docs for the required "scopes". +This returns a JWT access token required to provided as Bearer Token for any request to the API endpoints. Sample token: +```json +{ + "access_token": "eyJhbGciOiJSUzI...", + "expires_in": 300, + "refresh_expires_in": 0, + "token_type": "Bearer", + "not-before-policy": 0, + "scope": "openid reports_read reports_write" +} +``` + +2. Request the all available reports: `GET /reports` (see OpenAPI specs for details) +3. Trigger the calculation of a reports data: `POST /report-calculation/<report-id>` +4. Get status of the report calculation: `GET /report-calculation/<calculation-id>` +5. Once the status shows the calculation is completed, get the actual result data: `GET /report-calculation/<calculation-id>/data` \ No newline at end of file diff --git a/docs/api-specs/reporting-api-v1.yaml b/docs/api-specs/reporting-api-v1.yaml new file mode 100644 index 0000000..e72b67e --- /dev/null +++ b/docs/api-specs/reporting-api-v1.yaml @@ -0,0 +1,252 @@ +openapi: 3.0.3 +info: + title: Aam Digital - Reporting API + description: |- + API to manage reports that provide data calculated based on any entities of the Aam Digital system. + version: 1.0.0-alpha.1 +servers: + - url: https://{customerId}.aam-digital.net/api/v1/reporting + description: Developer Instance for testing + variables: + customerId: + default: dev + description: Customer ID assigned by the service provider +tags: + - name: report + +paths: + /report: + get: + security: + - development: + - reports_read + tags: + - report + summary: Return list of available Reports + responses: + 200: + description: List of all available Reports the requester has access permissions to, empty array if no reports are available + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Report' + 401: + description: If no valid access token + + /report/{reportId}: + get: + security: + - development: + - reports_read + tags: + - report + summary: Return report metadata by ID + parameters: + - in: path + name: reportId + schema: + type: string + required: true + responses: + 200: + description: Get details of a report, including details of the data structure (schema) this specific report's data has + content: + application/json: + schema: + $ref: '#/components/schemas/Report' + 404: + description: If the Report does not exist + 401: + description: If the access token does not grant permission to this Report + + /report-calculation/report/{reportId}: + get: + security: + - development: + - reports_read + tags: + - report + summary: Return all report calculations for a report + parameters: + - in: path + name: reportId + schema: + type: string + required: true + responses: + 200: + description: List of metadata of all calculations triggered by any user (pending and completed) + content: + application/json: + schema: + $ref: '#/components/schemas/ReportCalculation' + 401: + description: If the access token does not grant permission to this Report + 404: + description: If report does not exist + + post: + security: + - development: + - reports_write + tags: + - report + summary: Trigger a new report calculation run. + description: Trigger a new report calculation run. Check status of the asynchronous calculation via the /report-calculation endpoint + (CURRENTLY UNDER DEVELOPMENT) optionally receive status updates via a webhook if that has been set up for the authorized client + parameters: + - in: path + name: reportId + schema: + type: string + required: true + - in: query + name: from + description: start date for report period + schema: + type: string + format: date + - in: query + name: to + description: end date for report period + schema: + type: string + format: date + responses: + 200: + description: Return calculation identifier, to be used to check status and result data + content: + application/json: + schema: + type: object + properties: + id: + type: string + format: uuid + 401: + description: If the access token does not grant permission to this Report + + /report-calculation/{calculationId}: + get: + security: + - development: + - reports_read + tags: + - report + summary: Return metadata for a report calculation + parameters: + - in: path + name: calculationId + schema: + type: string + required: true + responses: + 200: + description: Status and details of the given report calculation run + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ReportCalculation' + 401: + description: If the access token does not grant permission to this Report + 404: + description: If the calculation identifier does not exist + + /report-calculation/{calculationId}/data: + get: + security: + - development: + - reports_read + tags: + - report + summary: Fetch actual report data for a specific calculation + parameters: + - in: path + name: calculationId + schema: + type: string + required: true + responses: + 200: + description: The actual data that has been calculated by the calculation run. This matches the schema defined in the /report/id + content: + application/json: + schema: + $ref: '#/components/schemas/ReportData' + application/xml: + schema: + $ref: '#/components/schemas/ReportData' + 404: + description: report data is not available yet (when either the calculation id is not valid or the calculation is still running) + 401: + description: If the access token does not grant permission to this Report + +components: + schemas: + Report: + type: object + properties: + id: + type: string + format: uuid + name: + type: string + example: report_all_course_members + calculationPending: + type: boolean + schema: + $ref: '#/components/schemas/ReportSchema' + + ReportSchema: + type: object + properties: + fields: + type: array + items: + type: object + + ReportCalculation: + type: object + properties: + id: + type: string + format: uuid + start_date: + type: string + example: date + end_date: + type: string + example: date + nullable: true + status: + type: string + description: Current status of the run + enum: + - PENDING + - RUNNING + - FINISHED_SUCCESS + - FINISHED_ERROR + + ReportData: + type: object + properties: + reportId: + type: string + format: uuid + data: + type: object + + securitySchemes: + development: + type: oauth2 + description: This API uses OAuth2 with the Client Credentials Flow + flows: + clientCredentials: + tokenUrl: https://keycloak.aam-digital.net/realms/TolaData/protocol/openid-connect/token + scopes: + reports_read: Read Report and ReportCalculation + reports_write: Trigger new ReportCalculation diff --git a/docs/assets/keycloak-client-setup.png b/docs/assets/keycloak-client-setup.png new file mode 100644 index 0000000..454bb59 Binary files /dev/null and b/docs/assets/keycloak-client-setup.png differ