diff --git a/src/lib/features/frontend-api/frontend-api-controller.ts b/src/lib/features/frontend-api/frontend-api-controller.ts index 86d8b834e8..671351bd23 100644 --- a/src/lib/features/frontend-api/frontend-api-controller.ts +++ b/src/lib/features/frontend-api/frontend-api-controller.ts @@ -9,10 +9,10 @@ import { createResponseSchema, emptyResponse, getStandardResponses, - ProxyClientSchema, - ProxyFeatureSchema, - proxyFeaturesSchema, - ProxyFeaturesSchema, + FrontendApiClientSchema, + FrontendApiFeatureSchema, + frontendApiFeaturesSchema, + FrontendApiFeaturesSchema, } from '../../openapi'; import { Context } from 'unleash-client'; import { enrichContextWithIp } from './index'; @@ -61,13 +61,13 @@ export default class FrontendAPIController extends Controller { tags: ['Frontend API'], operationId: 'getFrontendFeatures', responses: { - 200: createResponseSchema('proxyFeaturesSchema'), + 200: createResponseSchema('frontendApiFeaturesSchema'), ...getStandardResponses(401, 404), }, summary: 'Retrieve enabled feature toggles for the provided context.', description: - 'This endpoint returns the list of feature toggles that the proxy evaluates to enabled for the given context. Context values are provided as query parameters. If the Frontend API is disabled 404 is returned.', + 'This endpoint returns the list of feature toggles that the frontend API evaluates to enabled for the given context. Context values are provided as query parameters. If the Frontend API is disabled 404 is returned.', }), ], }); @@ -126,7 +126,7 @@ export default class FrontendAPIController extends Controller { description: 'This is for future use. Currently Frontend client registration is not supported. Returning 200 for clients that expect this status code. If the Frontend API is disabled 404 is returned.', operationId: 'registerFrontendClient', - requestBody: createRequestSchema('proxyClientSchema'), + requestBody: createRequestSchema('frontendApiClientSchema'), responses: { 200: emptyResponse, ...getStandardResponses(400, 401, 404), @@ -170,13 +170,13 @@ export default class FrontendAPIController extends Controller { private async getFrontendApiFeatures( req: ApiUserRequest, - res: Response, + res: Response, ) { if (!this.config.flagResolver.isEnabled('embedProxy')) { throw new NotFoundError(); } - let toggles: ProxyFeatureSchema[]; - let newToggles: ProxyFeatureSchema[] = []; + let toggles: FrontendApiFeatureSchema[]; + let newToggles: FrontendApiFeatureSchema[] = []; if (this.config.flagResolver.isEnabled('globalFrontendApiCache')) { [toggles, newToggles] = await Promise.all([ this.services.frontendApiService.getFrontendApiFeatures( @@ -217,7 +217,7 @@ export default class FrontendAPIController extends Controller { this.services.openApiService.respondWithValidation( 200, res, - proxyFeaturesSchema.$id, + frontendApiFeaturesSchema.$id, { toggles: returnedToggles }, ); } @@ -244,7 +244,7 @@ export default class FrontendAPIController extends Controller { } private async registerFrontendApiClient( - req: ApiUserRequest, + req: ApiUserRequest, res: Response, ) { if (!this.config.flagResolver.isEnabled('embedProxy')) { diff --git a/src/lib/features/frontend-api/proxy-service.test.ts b/src/lib/features/frontend-api/frontend-api-service.test.ts similarity index 83% rename from src/lib/features/frontend-api/proxy-service.test.ts rename to src/lib/features/frontend-api/frontend-api-service.test.ts index fd068ecc6e..043d478611 100644 --- a/src/lib/features/frontend-api/proxy-service.test.ts +++ b/src/lib/features/frontend-api/frontend-api-service.test.ts @@ -4,6 +4,8 @@ import { IApiUser } from '../../types'; import { FeatureInterface } from 'unleash-client/lib/feature'; import noLogger from '../../../test/fixtures/no-logger'; import { ApiTokenType } from '../../types/models/api-token'; +import EventEmitter from 'events'; +import { FRONTEND_API_REPOSITORY_CREATED } from '../../metric-events'; test('frontend api service fetching features from global cache', async () => { const irrelevant = {} as any; @@ -38,8 +40,13 @@ test('frontend api service fetching features from global cache', async () => { ) as FeatureInterface; }, } as GlobalFrontendApiCache; + const eventBus = new EventEmitter(); + let createdFrontendRepositoriesCount = 0; + eventBus.on(FRONTEND_API_REPOSITORY_CREATED, () => { + createdFrontendRepositoriesCount++; + }); const frontendApiService = new FrontendApiService( - { getLogger: noLogger } as unknown as Config, + { getLogger: noLogger, eventBus } as unknown as Config, irrelevant, irrelevant, globalFrontendApiCache, @@ -56,4 +63,5 @@ test('frontend api service fetching features from global cache', async () => { expect(features).toMatchObject([{ name: 'toggleA' }]); expect(features).toHaveLength(1); + expect(createdFrontendRepositoriesCount).toBe(1); }); diff --git a/src/lib/features/frontend-api/frontend-api-service.ts b/src/lib/features/frontend-api/frontend-api-service.ts index ac43d09057..05ed873950 100644 --- a/src/lib/features/frontend-api/frontend-api-service.ts +++ b/src/lib/features/frontend-api/frontend-api-service.ts @@ -1,6 +1,6 @@ import { IUnleashConfig, IUnleashServices, IUnleashStores } from '../../types'; import { Logger } from '../../logger'; -import { ClientMetricsSchema, ProxyFeatureSchema } from '../../openapi'; +import { ClientMetricsSchema, FrontendApiFeatureSchema } from '../../openapi'; import ApiUser, { IApiUser } from '../../types/api-user'; import { Context, @@ -15,7 +15,10 @@ import { } from '../../types/settings/frontend-settings'; import { validateOrigins } from '../../util'; import { BadDataError, InvalidTokenError } from '../../error'; -import { PROXY_REPOSITORY_CREATED } from '../../metric-events'; +import { + FRONTEND_API_REPOSITORY_CREATED, + PROXY_REPOSITORY_CREATED, +} from '../../metric-events'; import { FrontendApiRepository } from './frontend-api-repository'; import { GlobalFrontendApiCache } from './global-frontend-api-cache'; import { ProxyRepository } from './proxy-repository'; @@ -74,7 +77,7 @@ export class FrontendApiService { async getFrontendApiFeatures( token: IApiUser, context: Context, - ): Promise { + ): Promise { const client = await this.clientForFrontendApiToken(token); const definitions = client.getFeatureToggleDefinitions() || []; const sessionId = context.sessionId || String(Math.random()); @@ -100,7 +103,7 @@ export class FrontendApiService { async getNewFrontendApiFeatures( token: IApiUser, context: Context, - ): Promise { + ): Promise { const client = await this.newClientForFrontendApiToken(token); const definitions = client.getFeatureToggleDefinitions() || []; const sessionId = context.sessionId || String(Math.random()); @@ -168,8 +171,7 @@ export class FrontendApiService { if (!newClient) { newClient = this.createNewClientForFrontendApiToken(token); this.newClients.set(token.secret, newClient); - // TODO: do we need this twice? - // this.config.eventBus.emit(PROXY_REPOSITORY_CREATED); + this.config.eventBus.emit(FRONTEND_API_REPOSITORY_CREATED); } return newClient; diff --git a/src/lib/features/frontend-api/frontend-api.concurrency.e2e.test.ts b/src/lib/features/frontend-api/frontend-api.concurrency.e2e.test.ts index f053f6ce36..e0a9bff29b 100644 --- a/src/lib/features/frontend-api/frontend-api.concurrency.e2e.test.ts +++ b/src/lib/features/frontend-api/frontend-api.concurrency.e2e.test.ts @@ -12,7 +12,7 @@ let db: ITestDb; let appErrorLogs: string[] = []; beforeAll(async () => { - db = await dbInit('proxy_concurrency', getLogger); + db = await dbInit('frontend_api_concurrency', getLogger); const baseLogger = getLogger(); const appLogger = { ...baseLogger, diff --git a/src/lib/features/frontend-api/frontend-api.e2e.test.ts b/src/lib/features/frontend-api/frontend-api.e2e.test.ts index 7696b3fb2b..59e092e9b9 100644 --- a/src/lib/features/frontend-api/frontend-api.e2e.test.ts +++ b/src/lib/features/frontend-api/frontend-api.e2e.test.ts @@ -24,7 +24,7 @@ let app: IUnleashTest; let db: ITestDb; const TEST_USER_ID = -9999; beforeAll(async () => { - db = await dbInit('proxy', getLogger); + db = await dbInit('frontend_api', getLogger); app = await setupAppWithAuth( db.stores, { @@ -347,7 +347,7 @@ test('should accept client registration requests', async () => { .expect((res) => expect(res.text).toEqual('OK')); }); -test('should store proxy client metrics', async () => { +test('should store frontend api client metrics', async () => { const now = new Date(); const appName = randomId(); const instanceId = randomId(); diff --git a/src/lib/features/frontend-api/global-frontend-api-cache.ts b/src/lib/features/frontend-api/global-frontend-api-cache.ts index 6551edc107..08c94015c9 100644 --- a/src/lib/features/frontend-api/global-frontend-api-cache.ts +++ b/src/lib/features/frontend-api/global-frontend-api-cache.ts @@ -47,7 +47,7 @@ export class GlobalFrontendApiCache extends EventEmitter { ) { super(); this.config = config; - this.logger = config.getLogger('proxy-repository.ts'); + this.logger = config.getLogger('global-frontend-api-cache.ts'); this.clientFeatureToggleReadModel = clientFeatureToggleReadModel; this.configurationRevisionService = configurationRevisionService; this.segmentReadModel = segmentReadModel; diff --git a/src/lib/metric-events.ts b/src/lib/metric-events.ts index fa8f9da344..cc8e2352bb 100644 --- a/src/lib/metric-events.ts +++ b/src/lib/metric-events.ts @@ -3,6 +3,7 @@ const DB_TIME = 'db_time'; const SCHEDULER_JOB_TIME = 'scheduler_job_time'; const FEATURES_CREATED_BY_PROCESSED = 'features_created_by_processed'; const EVENTS_CREATED_BY_PROCESSED = 'events_created_by_processed'; +const FRONTEND_API_REPOSITORY_CREATED = 'frontend_api_repository_created'; const PROXY_REPOSITORY_CREATED = 'proxy_repository_created'; const PROXY_FEATURES_FOR_TOKEN_TIME = 'proxy_features_for_token_time'; @@ -12,6 +13,7 @@ export { SCHEDULER_JOB_TIME, FEATURES_CREATED_BY_PROCESSED, EVENTS_CREATED_BY_PROCESSED, + FRONTEND_API_REPOSITORY_CREATED, PROXY_REPOSITORY_CREATED, PROXY_FEATURES_FOR_TOKEN_TIME, }; diff --git a/src/lib/metrics.ts b/src/lib/metrics.ts index fcf154c1f6..5be086282a 100644 --- a/src/lib/metrics.ts +++ b/src/lib/metrics.ts @@ -236,6 +236,10 @@ export default class MetricsMonitor { name: 'proxy_repositories_created', help: 'Proxy repositories created', }); + const frontendApiRepositoriesCreated = createCounter({ + name: 'frontend_api_repositories_created', + help: 'Frontend API repositories created', + }); const mapFeaturesForClientDuration = createHistogram({ name: 'map_features_for_client_duration', help: 'Duration of mapFeaturesForClient function', @@ -417,6 +421,10 @@ export default class MetricsMonitor { proxyRepositoriesCreated.inc(); }); + eventBus.on(events.FRONTEND_API_REPOSITORY_CREATED, () => { + frontendApiRepositoriesCreated.inc(); + }); + eventBus.on(events.PROXY_FEATURES_FOR_TOKEN_TIME, ({ duration }) => { mapFeaturesForClientDuration.observe(duration); }); diff --git a/src/lib/openapi/spec/proxy-client-schema.ts b/src/lib/openapi/spec/frontend-api-client-schema.ts similarity index 89% rename from src/lib/openapi/spec/proxy-client-schema.ts rename to src/lib/openapi/spec/frontend-api-client-schema.ts index 59082cac75..3036d03673 100644 --- a/src/lib/openapi/spec/proxy-client-schema.ts +++ b/src/lib/openapi/spec/frontend-api-client-schema.ts @@ -1,7 +1,7 @@ import { FromSchema } from 'json-schema-to-ts'; -export const proxyClientSchema = { - $id: '#/components/schemas/proxyClientSchema', +export const frontendApiClientSchema = { + $id: '#/components/schemas/frontendApiClientSchema', type: 'object', required: ['appName', 'interval', 'started', 'strategies'], description: 'Frontend SDK client registration information', @@ -50,4 +50,6 @@ export const proxyClientSchema = { components: {}, } as const; -export type ProxyClientSchema = FromSchema; +export type FrontendApiClientSchema = FromSchema< + typeof frontendApiClientSchema +>; diff --git a/src/lib/openapi/spec/proxy-feature-schema.ts b/src/lib/openapi/spec/frontend-api-feature-schema.ts similarity index 93% rename from src/lib/openapi/spec/proxy-feature-schema.ts rename to src/lib/openapi/spec/frontend-api-feature-schema.ts index f9202a8b40..98a1a6eff8 100644 --- a/src/lib/openapi/spec/proxy-feature-schema.ts +++ b/src/lib/openapi/spec/frontend-api-feature-schema.ts @@ -1,7 +1,7 @@ import { FromSchema } from 'json-schema-to-ts'; -export const proxyFeatureSchema = { - $id: '#/components/schemas/proxyFeatureSchema', +export const frontendApiFeatureSchema = { + $id: '#/components/schemas/frontendApiFeatureSchema', type: 'object', required: ['name', 'enabled', 'impressionData'], additionalProperties: false, @@ -75,4 +75,6 @@ export const proxyFeatureSchema = { components: {}, } as const; -export type ProxyFeatureSchema = FromSchema; +export type FrontendApiFeatureSchema = FromSchema< + typeof frontendApiFeatureSchema +>; diff --git a/src/lib/openapi/spec/proxy-features-schema.ts b/src/lib/openapi/spec/frontend-api-features-schema.ts similarity index 55% rename from src/lib/openapi/spec/proxy-features-schema.ts rename to src/lib/openapi/spec/frontend-api-features-schema.ts index abaddaaadd..56d173d111 100644 --- a/src/lib/openapi/spec/proxy-features-schema.ts +++ b/src/lib/openapi/spec/frontend-api-features-schema.ts @@ -1,8 +1,8 @@ import { FromSchema } from 'json-schema-to-ts'; -import { proxyFeatureSchema } from './proxy-feature-schema'; +import { frontendApiFeatureSchema } from './frontend-api-feature-schema'; -export const proxyFeaturesSchema = { - $id: '#/components/schemas/proxyFeaturesSchema', +export const frontendApiFeaturesSchema = { + $id: '#/components/schemas/frontendApiFeaturesSchema', type: 'object', required: ['toggles'], additionalProperties: false, @@ -12,15 +12,17 @@ export const proxyFeaturesSchema = { description: 'The actual features returned to the Frontend SDK', type: 'array', items: { - $ref: proxyFeatureSchema.$id, + $ref: frontendApiFeatureSchema.$id, }, }, }, components: { schemas: { - proxyFeatureSchema, + frontendApiFeatureSchema, }, }, } as const; -export type ProxyFeaturesSchema = FromSchema; +export type FrontendApiFeaturesSchema = FromSchema< + typeof frontendApiFeaturesSchema +>; diff --git a/src/lib/openapi/spec/index.ts b/src/lib/openapi/spec/index.ts index ccb31b68c8..df5ddd5a61 100644 --- a/src/lib/openapi/spec/index.ts +++ b/src/lib/openapi/spec/index.ts @@ -90,6 +90,9 @@ export * from './features-schema'; export * from './feedback-create-schema'; export * from './feedback-response-schema'; export * from './feedback-update-schema'; +export * from './frontend-api-client-schema'; +export * from './frontend-api-feature-schema'; +export * from './frontend-api-features-schema'; export * from './group-schema'; export * from './group-user-model-schema'; export * from './groups-schema'; @@ -134,9 +137,6 @@ export * from './project-overview-schema'; export * from './project-schema'; export * from './project-stats-schema'; export * from './projects-schema'; -export * from './proxy-client-schema'; -export * from './proxy-feature-schema'; -export * from './proxy-features-schema'; export * from './public-signup-token-create-schema'; export * from './public-signup-token-schema'; export * from './public-signup-token-update-schema';