Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ PORT=3000
LOG_LEVEL=mute
AC_JWKS_URL=http://auth.example.com
ASSERT_CONFIGS_ON_START=false
START_AUX_HTTP_SERVER_ON_START=false
2 changes: 1 addition & 1 deletion docs/application.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,6 @@ Note: scope simply refers to and individual `Mesh` instance. For example, when r

When application is processing HTTP requests, a number of request-scoped components can be bound to Router classes:

- `AcAuth` (Automation Cloud identity and authorisation data)
- `AuthContext<AcAuth>` (Automation Cloud identity and authorisation data)
- `KoaContext` (bound by string `"KoaContext"` service identifier) — [Koa](https://koajs.org) context object
- `Logger` (rebound to `RequestLogger`) which includes request-specific data
42 changes: 30 additions & 12 deletions docs/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,28 @@ Automation Cloud infrastructure features "highly sophisticated" request authenti

Node Framework does its best to abstract away all the complexity, allowing apps to focus on what they are supposed to be doing.

`AcAuth` object exposes identity information of current request, as well as convenience methods for request authorisation.
The framework exports a default implementation of auth provider for Automation Cloud. Although, (starting from version 16.x) this service is optional and needs explicity installation (including its dependencies, i.e. a JWT service).

```ts
class App extends Application {

override createGlobalScope() {
// starting from version 16.x, the following lines are required to use AC auth (default) service implementation
const mesh = super.createGlobalScope();
mesh.service(AuthProvider, AcAuthProvider);
mesh.service(JwtService, AutomationCloudJwtService);
}
}
```

`AuthContext<AuthToken>` object exposes identity information of current request, as well as convenience methods for request authorisation.

`AuthToken` object type is based on the implementation of the `AuthProvider` registered. You may specify any auth token object type, i.e `AuthContext<AcAuth>`. In order to retrieve specific data from a auth token type, you need to retrieve it from the auth context object with `authContenxt.getToken()`.

```ts
export class MyRouter extends Router {

@dep() auth!: AcAuth;
@dep() auth!: AuthContext<AcAuth>;

@Middleware()
async authorise() {
Expand All @@ -25,7 +41,7 @@ export class MyRouter extends Router {
})
async helloOrg() {
// throws 403 when organisationId cannot be extracted from request details
const organisationId = this.auth.requireOrganisationId();
const organisationId = this.auth.getAuthToken().requireOrganisationId();
return { message: '👋 Hello ' + organisationId };
}

Expand All @@ -35,28 +51,30 @@ export class MyRouter extends Router {
})
async helloServiceAccount() {
// throws 403 when serviceAccount info cannot be extracted from request details
const serviceAccountId = this.auth.requireServiceAccountId();
const serviceAccountId = this.auth.getAuthToken().requireServiceAccountId();
return { message: '👋 Hello ' + serviceAccountId };
}
}
```

## Mocking auth in tests

In integration tests it is useful to mock `AcAuth` by providing a custom implementation of `AcAuthProvider`:
In integration tests it is useful to mock `AuthContext` by providing a custom implementation of `AcAuthProvider`:

```ts
class App extends Application {

override createHttpRequestScope() {
const mesh = super.createHttpRequestScope();
override createGlobalScope() {
const mesh = super.createGlobalScope();
mesh.constant(AcAuthProvider, {
async provide() {
return new AcAuth({
authenticated: true,
organisationId: 'my-fake-org-id',
serviceAccountId: 'my-face-service-account-id',
});
return new AuthContext(
new AcAuth({
authenticated: true,
organisationId: 'my-fake-org-id',
serviceAccountId: 'my-face-service-account-id',
})
);
}
});
return mesh;
Expand Down
24 changes: 15 additions & 9 deletions docs/metrics.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## Metrics

Framework provides a standard facility to collect and expose Prometheus metrics.
Framework provides (global) standard metrics and that can be extended with `@nodescript/metrics`, to collect and expose Prometheus metrics.

Following main use cases are supported:

Expand All @@ -12,15 +12,15 @@ Following main use cases are supported:
Create a `Metrics` class in `src/main/metrics.ts` and define your counters, histograms and gauges.

```ts
import { MetricsRegistry } from '@ubio/framework';
import { metric, CounterMetric } from '@nodescript/metrics';

export class Metrics extends MetricsRegistry {
export class Metrics {

dataCacheKeysRequested = this.counter('api_dataset_cache_keys_requested_total',
@metric() dataCacheKeysRequested = new CounterMetric('api_dataset_cache_keys_requested_total',
'Data Cache: Keys Requested');
dataCacheKeysRetrieved = this.counter('api_dataset_cache_keys_retrieved_total',
@metric() dataCacheKeysRetrieved = new CounterMetric('api_dataset_cache_keys_retrieved_total',
'Data Cache: Keys Retrieved');
dataCacheKeysStored = this.counter('api_dataset_cache_keys_stored_total',
@metric() dataCacheKeysStored = new CounterMetric('api_dataset_cache_keys_stored_total',
'Data Cache: Keys Stored');

}
Expand All @@ -43,13 +43,13 @@ export class App extends Application {
This will do two things:

1. bind `Metrics` to self in singleton scope
2. bind that instance to `MetricsRegistry` which is used to aggregate mutliple registries (including the global registry)
2. make it available to `@nodescript/metrics` so the app can expose the reports over http.

Finally, for observing the metrics in your classes, simply `@dep() metrics: Metrics` and start using the counters, gauges and histograms you have defined.

### Global metrics

Global metrics are collected in `GlobalMetricsRegistry` which is automatically bound to all applications (you don't have to specify anything).
Global metrics are collected in `GlobalMetrics` which is automatically bound to all applications (you don't have to specify anything).

Global metrics include `@MeasureAsync` decorator which can be used to annotate any Promise-returning method so that its performance can be reported and analyse.

Expand All @@ -69,4 +69,10 @@ In this example, `app_method_latencies_seconds{class="MyClass",method="doWork"}`

### Metrics endpoint

MetricsRouter is automatically bound to all applications (you don't have to do anything). It serves `GET /metrics` endpoint and reports metrics from all registered registries.
By default, the application will run a http server (in a different port) that handles requests to `GET /metrics` endpoint, unless configured otherwise (i.e. `START_AUX_HTTP_SERVER_ON_START=false` is set). So, you don't have to do anything to expose the metrics reports.

This endpoint reports metrics from all registered ones, which are:

- class member decorated with `@metric()`;
- metric type instantiated (i.e. `new CounterMetric()`);
- and its class is bound to mesh container as a service (i.e `mesh.service(Metrics)`).
2 changes: 1 addition & 1 deletion docs/structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export class App extends Application {
await this.mongoDb.client.connect();
// Add other code to execute on application startup
await this.httpServer.startServer();
});
};

override async afterStop() {
await this.httpServer.stopServer();
Expand Down
Loading