Skip to content

Commit

Permalink
feat: use Nest.js SDK in backend (#273)
Browse files Browse the repository at this point in the history
Signed-off-by: Lukas Reining <[email protected]>
  • Loading branch information
lukas-reining authored Jan 22, 2024
1 parent 1827862 commit d4b18d8
Show file tree
Hide file tree
Showing 32 changed files with 1,309 additions and 1,143 deletions.
10 changes: 9 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,15 @@
"depConstraints": [
{
"sourceTag": "*",
"onlyDependOnLibsWithTags": ["*"]
"onlyDependOnLibsWithTags": [
"*"
]
},
{
"sourceTag": "shared",
"allowedExternalImports": [
"@openfeature/core"
]
}
]
}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ jobs:
cache: 'npm'
- run: npm ci
- run: npx nx affected --target=lint --parallel=3
- run: npx nx affected --target=build --parallel=3 --ci
- run: npx nx affected --target=test --parallel=3 --ci --code-coverage
16 changes: 8 additions & 8 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
version: '3.8'
services:
demo:
image: ghcr.io/open-feature/playground-app:v0.13.4 # x-release-please-version
# build:
# dockerfile: ./packages/app/Dockerfile
# context: .
# image: ghcr.io/open-feature/playground-app:v0.13.4 # x-release-please-version
build:
dockerfile: ./packages/app/Dockerfile
context: .
ports:
- '30000:30000'
command:
Expand Down Expand Up @@ -46,10 +46,10 @@ services:
- FLAGD_TLS_WEB

fib-service:
image: ghcr.io/open-feature/playground-fib-service:v0.13.4 # x-release-please-version
# build:
# dockerfile: ./packages/fibonacci-service/Dockerfile
# context: .
# image: ghcr.io/open-feature/playground-fib-service:v0.13.4 # x-release-please-version
build:
dockerfile: ./packages/fibonacci-service/Dockerfile
context: .
expose:
- '30001'
environment:
Expand Down
2,109 changes: 1,223 additions & 886 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"ui": "nx run ui:serve",
"app": "nx run app:serve",
"fib-service": "nx run fibonacci-service:serve",
"temp": "nx build",
"build": "nx run-many --all --target=build",
"build:app": "nx build app && nx build ui",
"build:fib-service": "nx build fibonacci-service",
"lint": "nx run-many --all --target=lint",
Expand All @@ -24,12 +24,12 @@
"@nestjs/platform-express": "9.4.3",
"@nestjs/serve-static": "^3.0.1",
"@openfeature/env-var-provider": "^0.2.0",
"@openfeature/flagd-provider": "^0.9.0",
"@openfeature/flagd-provider": "^0.10.4",
"@openfeature/flagd-web-provider": "^0.4.1",
"@openfeature/go-feature-flag-provider": "^0.6.1",
"@openfeature/nestjs-sdk": "^0.0.4-experimental",
"@openfeature/open-telemetry-hooks": "^0.3.0",
"@openfeature/server-sdk": "^1.7.5",
"@openfeature/web-sdk": "0.4.7",
"@openfeature/web-sdk": "0.4.10",
"@opentelemetry/api": "^1.7.0",
"@opentelemetry/auto-instrumentations-node": "^0.40.2",
"@opentelemetry/core": "^1.18.1",
Expand Down
87 changes: 28 additions & 59 deletions packages/app/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,25 @@
import {HttpModule} from '@nestjs/axios';
import {MiddlewareConsumer, Module, NestModule, Scope} from '@nestjs/common';
import {REQUEST} from '@nestjs/core';
import {ExecutionContext, Module} from '@nestjs/common';
import {ServeStaticModule} from '@nestjs/serve-static';
import {AsyncLocalStorageTransactionContext, LoggingHook, OpenFeatureLogger} from '@openfeature/extra';
import { FlagMetadata, OpenFeature } from '@openfeature/server-sdk';
import {MetricsHook, TracingHook as SpanEventBasedTracingHook} from '@openfeature/open-telemetry-hooks';
import {LoggingHook, OpenFeatureLogger} from '@openfeature/extra';
import {TracingHook as SpanEventBasedTracingHook, MetricsHook} from '@openfeature/open-telemetry-hooks';
import {ProviderService} from '@openfeature/provider';
import {Request} from 'express';
import {Agent} from 'http';
import {LoggerModule} from 'nestjs-pino';
import {join} from 'path';
import {OPENFEATURE_CLIENT, REQUEST_DATA} from './constants';
import {FibonacciAsAServiceController} from './fibonacci-as-a-service.controller';
import {FibonacciService} from './fibonacci/fibonacci.service';
import {ProvidersController} from './providers.controller';
import {TransactionContextMiddleware} from './transaction-context.middleware';
import {RequestData} from './types';
import {UtilsController} from './utils.controller';

/**
* Set a global logger for OpenFeature. This is logger will available in hooks.
*/
OpenFeature.setLogger(new OpenFeatureLogger('OpenFeature'));
import {EvaluationContext, FlagMetadata, OpenFeatureModule} from "@openfeature/nestjs-sdk";

function attributeMapper(flagMetadata: FlagMetadata) {
return {
...('scope' in flagMetadata && { scope: flagMetadata.scope }),
...('scope' in flagMetadata && {scope: flagMetadata.scope}),
};
}

/**
* Adding hooks to at the global level will ensure they always run
* as part of a flag evaluation lifecycle.
*/
OpenFeature.addHooks(
new LoggingHook(),
new SpanEventBasedTracingHook({attributeMapper}),
new MetricsHook({attributeMapper}));

/**
* The transaction context propagator is an experimental feature
* that allows evaluation context to be set anywhere in a request
* and have it automatically available during a flag evaluation.
*/
OpenFeature.setTransactionContextPropagator(new AsyncLocalStorageTransactionContext());

@Module({
imports: [
LoggerModule.forRoot({
Expand All @@ -59,53 +34,47 @@ OpenFeature.setTransactionContextPropagator(new AsyncLocalStorageTransactionCont
transport:
process.env['NODE' + '_ENV'] !== 'production'
? {
target: 'pino-pretty',
options: {
hideObject: true,
},
}
target: 'pino-pretty',
options: {
hideObject: true,
},
}
: undefined,
},
}),
ServeStaticModule.forRoot({
rootPath: join(__dirname, '..', 'ui'),
}),
HttpModule.register({
httpAgent: new Agent({ keepAlive: true }),
httpAgent: new Agent({keepAlive: true}),
}),
],
controllers: [FibonacciAsAServiceController, UtilsController, ProvidersController],
providers: [
FibonacciService,
ProviderService,
{
provide: OPENFEATURE_CLIENT,
useFactory: () => {
const client = OpenFeature.getClient('app');
return client;
},
},
{
provide: REQUEST_DATA,
useFactory: (req: Request): RequestData => {
OpenFeatureModule.forRoot({
// Set a global logger for OpenFeature. This is logger will available in hooks.
logger: new OpenFeatureLogger('OpenFeature'),
//Adding hooks to at the global level will ensure they always run as part of a flag evaluation lifecycle.
hooks: [new LoggingHook(), new SpanEventBasedTracingHook({attributeMapper}), new MetricsHook({attributeMapper})],
// This context will be used for all flag evaluations in the callstack
contextFactory: async (context: ExecutionContext): Promise<EvaluationContext> => {
const req = await context.switchToHttp().getRequest<Request>()
const authHeaderValue = req.header('Authorization') || 'anonymous';
const userAgent = req.header('user-agent');
return {
ts: new Date().getTime(),
ip: (req.headers['x-forwarded-for'] as string) || (req.socket.remoteAddress as string),
email: authHeaderValue,
method: req.method,
path: req.path,
...(userAgent && { userAgent }),
...(userAgent && {userAgent}),
targetingKey: authHeaderValue,
};
},
scope: Scope.REQUEST,
inject: [REQUEST],
},
})
],
controllers: [FibonacciAsAServiceController, UtilsController, ProvidersController],
providers: [
FibonacciService,
ProviderService,
],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(TransactionContextMiddleware).forRoutes(FibonacciAsAServiceController);
}
export class AppModule {
}
2 changes: 0 additions & 2 deletions packages/app/src/app/constants.ts

This file was deleted.

16 changes: 8 additions & 8 deletions packages/app/src/app/fibonacci/fibonacci.service.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { HttpService } from '@nestjs/axios';
import { Inject, Injectable } from '@nestjs/common';
import { fibonacci } from '@openfeature/fibonacci';
import { Client } from '@openfeature/server-sdk';
import { OPENFEATURE_CLIENT } from '../constants';
import { lastValueFrom, map } from 'rxjs';
import {HttpService} from '@nestjs/axios';
import {Injectable} from '@nestjs/common';
import {fibonacci} from '@openfeature/fibonacci';
import {lastValueFrom, map} from 'rxjs';
import {Client, FeatureClient} from "@openfeature/nestjs-sdk";

@Injectable()
export class FibonacciService {
private readonly FIB_SERVICE_URL = process.env.FIB_SERVICE_URL || 'http://localhost:30001';

constructor(private readonly httpService: HttpService, @Inject(OPENFEATURE_CLIENT) private client: Client) {}
constructor(private readonly httpService: HttpService, @FeatureClient() private client: Client) {
}

async calculateFibonacci(num: number): Promise<{ result: number }> {
const useRemoteFibService = await this.client.getBooleanValue('use-remote-fib-service', false);
Expand All @@ -18,7 +18,7 @@ export class FibonacciService {
return lastValueFrom(
this.httpService
.get<{ result: number }>(`${this.FIB_SERVICE_URL}/calculate`, {
params: { num },
params: {num},
auth: {
username: process.env.FIB_SERVICE_USER || '',
password: process.env.FIB_SERVICE_PASS || '',
Expand Down
19 changes: 0 additions & 19 deletions packages/app/src/app/transaction-context.middleware.ts

This file was deleted.

13 changes: 0 additions & 13 deletions packages/app/src/app/types.ts

This file was deleted.

54 changes: 19 additions & 35 deletions packages/fibonacci-service/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,18 @@
import {MiddlewareConsumer, Module, NestModule} from '@nestjs/common';
import {AppController} from './app.controller';
import {LoggerModule} from 'nestjs-pino';
import { FlagMetadata, OpenFeature } from '@openfeature/server-sdk';
import {AsyncLocalStorageTransactionContext, LoggingHook, OpenFeatureLogger} from '@openfeature/extra';
import {MetricsHook, TracingHook as SpanEventBasedTracingHook} from '@openfeature/open-telemetry-hooks';
import {TransactionContextMiddleware} from './transaction-context.middleware';
import {ProviderService} from '@openfeature/provider';
import {ProvidersController} from './providers.controller';

/**
* Set a global logger for OpenFeature. This is logger will available in hooks.
*/
OpenFeature.setLogger(new OpenFeatureLogger('OpenFeature'));
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { LoggerModule } from 'nestjs-pino';
import { LoggingHook, OpenFeatureLogger } from '@openfeature/extra';
import { MetricsHook, TracingHook as SpanEventBasedTracingHook } from '@openfeature/open-telemetry-hooks';
import { ProviderService } from '@openfeature/provider';
import { ProvidersController } from './providers.controller';
import { OpenFeatureModule, FlagMetadata } from '@openfeature/nestjs-sdk';

function attributeMapper(flagMetadata: FlagMetadata) {
return {
...('scope' in flagMetadata && { scope: flagMetadata.scope }),
};
}

/**
* Adding hooks to at the global level will ensure they always run
* as part of a flag evaluation lifecycle.
*/
OpenFeature.addHooks(
new LoggingHook(),
new SpanEventBasedTracingHook({attributeMapper}),
new MetricsHook({ attributeMapper }));

/**
* The transaction context propagator is an experimental feature
* that allows evaluation context to be set anywhere in a request
* and have it automatically available during a flag evaluation.
*/
OpenFeature.setTransactionContextPropagator(new AsyncLocalStorageTransactionContext());

@Module({
imports: [
LoggerModule.forRoot({
Expand All @@ -57,12 +35,18 @@ OpenFeature.setTransactionContextPropagator(new AsyncLocalStorageTransactionCont
: undefined,
},
}),
OpenFeatureModule.forRoot({
// Set a global logger for OpenFeature. This is logger will available in hooks.
logger: new OpenFeatureLogger('OpenFeature'),
//Adding hooks to at the global level will ensure they always run as part of a flag evaluation lifecycle.
hooks: [
new LoggingHook(),
new SpanEventBasedTracingHook({ attributeMapper }),
new MetricsHook({ attributeMapper }),
],
}),
],
controllers: [AppController, ProvidersController],
providers: [ProviderService],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(TransactionContextMiddleware).forRoutes(AppController);
}
}
export class AppModule {}

This file was deleted.

2 changes: 1 addition & 1 deletion packages/fibonacci/src/lib/fibonacci.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { OpenFeature } from '@openfeature/server-sdk';
import { OpenFeature } from '@openfeature/nestjs-sdk';

const oFeatClient = OpenFeature.getClient('fibonacci');

Expand Down
1 change: 0 additions & 1 deletion packages/openfeature-extra/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
export * from './lib/hooks';
export * from './lib/transaction-context';
export * from './lib/logger';
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EvaluationDetails, Hook, HookContext, JsonObject } from '@openfeature/server-sdk';
import { EvaluationDetails, Hook, HookContext, JsonObject } from '@openfeature/nestjs-sdk';
import { validateSync } from 'class-validator';

/* eslint-disable @typescript-eslint/no-explicit-any */
Expand Down
2 changes: 1 addition & 1 deletion packages/openfeature-extra/src/lib/hooks/logging-hook.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EvaluationDetails, FlagValue, Hook, HookContext, HookHints } from '@openfeature/server-sdk';
import { EvaluationDetails, FlagValue, Hook, HookContext, HookHints } from '@openfeature/nestjs-sdk';

/**
* A hook that simply logs at every life-cycle stage.
Expand Down
Loading

0 comments on commit d4b18d8

Please sign in to comment.