This repository has been archived by the owner on Mar 14, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Sebastian <[email protected]>
- Loading branch information
1 parent
f71553a
commit 9e5f387
Showing
9 changed files
with
286 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,5 @@ | ||
SENTRY_DSN= | ||
PORT= | ||
DATABASE_URL=https://dev.aam-digital.net/db | ||
QUERY_URL=http://localhost:3002 | ||
SCHEMA_CONFIG_ID=_design/sqlite:config |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,16 @@ | ||
# Deployer Backend | ||
# Query Backend | ||
|
||
This server allows to automatically deploy applications on the server. | ||
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 | ||
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). | ||
|
||
The following variables might need to be configured in the `.env` file: | ||
- `DATABASE_URL` URL of the `CouchDB` or [replication backend](https://github.com/Aam-Digital/replication-backend) | ||
- `QUERY_URL` URL of the SQS | ||
- `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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,97 @@ | ||
import { Controller, } from '@nestjs/common'; | ||
import { | ||
BadRequestException, | ||
Body, | ||
Controller, | ||
Headers, | ||
HttpException, | ||
Param, | ||
Post, | ||
} from '@nestjs/common'; | ||
import { HttpService } from '@nestjs/axios'; | ||
import { ConfigService } from '@nestjs/config'; | ||
import { ApiHeader, ApiOperation, ApiParam } from '@nestjs/swagger'; | ||
import { catchError, concat, map, mergeMap, toArray } from 'rxjs'; | ||
import { SqlReport } from './sql-report'; | ||
import { QueryBody } from './query-body.dto'; | ||
|
||
@Controller() | ||
@Controller('report') | ||
export class AppController { | ||
|
||
private dbUrl = this.configService.get('DATABASE_URL'); | ||
private queryUrl = this.configService.get('QUERY_URL'); | ||
private schemaDocId = this.configService.get('SCHEMA_CONFIG_ID'); | ||
constructor( | ||
private http: HttpService, | ||
private configService: ConfigService, | ||
) {} | ||
|
||
// TODO also support cookie auth? Not really required with Keycloak | ||
@ApiOperation({ | ||
description: `Get the results for the report with the given ID. User needs 'read' access for the requested report entity.`, | ||
}) | ||
@ApiParam({ name: 'id', description: '(full) ID of the report entity' }) | ||
@ApiParam({ name: 'db', example: 'app', description: 'name of database' }) | ||
@ApiHeader({ | ||
name: 'Authorization', | ||
required: false, | ||
description: 'request needs to be authenticated', | ||
}) | ||
@Post(':db/:id') | ||
queryData( | ||
@Param('id') reportId: string, | ||
@Param('db') db: string, | ||
@Headers('Authorization') token: string, | ||
@Body() body?: QueryBody, | ||
) { | ||
return this.http | ||
.get<SqlReport>(`${this.dbUrl}/${db}/${reportId}`, { | ||
headers: { Authorization: token }, | ||
}) | ||
.pipe( | ||
mergeMap(({ data }) => this.executeReport(data, db, body)), | ||
catchError((err) => { | ||
throw err.response?.data | ||
? new HttpException(err.response.data, err.response.status) | ||
: err; | ||
}), | ||
); | ||
} | ||
|
||
private executeReport(report: SqlReport, db: string, args?: QueryBody) { | ||
if (report.mode !== 'sql') { | ||
throw new BadRequestException('Not an SQL report'); | ||
} | ||
if (!report.aggregationDefinitions) { | ||
throw new BadRequestException('Report query not configured'); | ||
} | ||
|
||
// execute all requests in sequence | ||
return concat( | ||
...report.aggregationDefinitions.map((query) => | ||
this.getQueryResult(query, args, db), | ||
), | ||
).pipe( | ||
// combine results of each request | ||
toArray(), | ||
map((res) => [].concat(...res)), | ||
); | ||
} | ||
|
||
private getQueryResult(query: string, args: QueryBody, db: string) { | ||
const data: SqsRequest = { query: query }; | ||
if (args?.from && args?.to) { | ||
data.args = [args.from, args.to]; | ||
} | ||
return this.http | ||
.post<any[]>(`${this.queryUrl}/${db}/${this.schemaDocId}`, data) | ||
.pipe(map(({ data }) => data)); | ||
} | ||
} | ||
|
||
/** | ||
* Request body as required by the SQS service. See SQS docs for more info. | ||
* {@link https://neighbourhood.ie/products-and-services/structured-query-server} | ||
*/ | ||
interface SqsRequest { | ||
query: string; | ||
args?: any[]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/** | ||
* The dates can be used in the SQL SELECT statements with a "?" | ||
* "from" will replace the first "?" | ||
* "to" will replace the second "?" | ||
*/ | ||
export class QueryBody { | ||
from: string; | ||
to: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
/** | ||
* The report entity needs to have the following format in order to work. | ||
* This aligns with the same interface in {@link https://github.com/Aam-Digital/ndb-core} | ||
*/ | ||
export interface SqlReport { | ||
mode: 'sql'; | ||
aggregationDefinitions: string[]; | ||
} |