diff --git a/src/lib/features/api-tokens/createApiTokenService.ts b/src/lib/features/api-tokens/createApiTokenService.ts new file mode 100644 index 000000000000..5a834509a6d2 --- /dev/null +++ b/src/lib/features/api-tokens/createApiTokenService.ts @@ -0,0 +1,53 @@ +import type { Db, IUnleashConfig } from '../../server-impl'; +import EnvironmentStore from '../project-environments/environment-store'; +import { ApiTokenService, type EventService } from '../../services'; +import FakeEnvironmentStore from '../project-environments/fake-environment-store'; +import type { IEnvironmentStore } from '../../types'; +import { + createEventsService, + createFakeEventsService, +} from '../events/createEventsService'; +import FakeApiTokenStore from '../../../test/fixtures/fake-api-token-store'; +import { ApiTokenStore } from '../../db/api-token-store'; + +export const createApiTokenService = ( + db: Db, + config: IUnleashConfig, +): ApiTokenService => { + const { eventBus, getLogger } = config; + const apiTokenStore = new ApiTokenStore(db, eventBus, getLogger); + const environmentStore = new EnvironmentStore(db, eventBus, getLogger); + const eventService = createEventsService(db, config); + + return new ApiTokenService( + { apiTokenStore, environmentStore }, + config, + eventService, + ); +}; + +export const createFakeApiTokenService = ( + config: IUnleashConfig, +): { + apiTokenService: ApiTokenService; + eventService: EventService; + apiTokenStore: FakeApiTokenStore; + environmentStore: IEnvironmentStore; +} => { + const apiTokenStore = new FakeApiTokenStore(); + const environmentStore = new FakeEnvironmentStore(); + const eventService = createFakeEventsService(config); + + const apiTokenService = new ApiTokenService( + { apiTokenStore, environmentStore }, + config, + eventService, + ); + + return { + apiTokenService, + apiTokenStore, + eventService, + environmentStore, + }; +}; 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 6e010ee1eeb5..44aa4ddf333a 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 @@ -21,10 +21,14 @@ beforeAll(async () => { baseLogger.error(msg, ...args); }, }; - app = await setupAppWithoutSupertest(db.stores, { - frontendApiOrigins: ['https://example.com'], - getLogger: () => appLogger, - }); + app = await setupAppWithoutSupertest( + db.stores, + { + frontendApiOrigins: ['https://example.com'], + getLogger: () => appLogger, + }, + db.rawDatabase, + ); }); afterEach(() => { 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 5fac9c307cca..b7af53753a2a 100644 --- a/src/lib/features/frontend-api/frontend-api.e2e.test.ts +++ b/src/lib/features/frontend-api/frontend-api.e2e.test.ts @@ -1151,14 +1151,18 @@ test('should NOT evaluate disabled strategies when returning toggles', async () }); test('should return 204 if metrics are disabled', async () => { - const localApp = await setupAppWithAuth(db.stores, { - frontendApiOrigins: ['https://example.com'], - experimental: { - flags: { - disableMetrics: true, + const localApp = await setupAppWithAuth( + db.stores, + { + frontendApiOrigins: ['https://example.com'], + experimental: { + flags: { + disableMetrics: true, + }, }, }, - }); + db.rawDatabase, + ); const frontendToken = await localApp.services.apiTokenService.createApiTokenWithProjects({ diff --git a/src/lib/routes/admin-api/api-token.test.ts b/src/lib/routes/admin-api/api-token.test.ts index e6edc76ef2a8..d187da9a1016 100644 --- a/src/lib/routes/admin-api/api-token.test.ts +++ b/src/lib/routes/admin-api/api-token.test.ts @@ -20,12 +20,18 @@ async function getSetup(adminTokenKillSwitchEnabled: boolean) { //@ts-ignore - Just testing, so only need the isEnabled call here }); const stores = createStores(); - await stores.environmentStore.create({ + const services = createServices(stores, config); + + //@ts-expect-error: we're accessing a private field, but we need + //to set up an environment to test the functionality. Because we + //don't have a db to use, we need to access the service's store + //directly. + await services.apiTokenService.environmentStore.create({ name: 'development', type: 'development', enabled: true, }); - const services = createServices(stores, config); + const app = await getApp(config, stores, services); return { diff --git a/src/lib/services/api-token-service.limit.test.ts b/src/lib/services/api-token-service.limit.test.ts index 78a2802a1244..7d83cae18e2c 100644 --- a/src/lib/services/api-token-service.limit.test.ts +++ b/src/lib/services/api-token-service.limit.test.ts @@ -1,11 +1,8 @@ -import { ApiTokenService } from './api-token-service'; import { createTestConfig } from '../../test/config/test-config'; import type { IUnleashConfig } from '../server-impl'; import { ApiTokenType } from '../types/models/api-token'; -import FakeApiTokenStore from '../../test/fixtures/fake-api-token-store'; -import FakeEnvironmentStore from '../features/project-environments/fake-environment-store'; -import { createFakeEventsService } from '../../lib/features'; import { ExceedsLimitError } from '../error/exceeds-limit-error'; +import { createFakeApiTokenService } from '../features/api-tokens/createApiTokenService'; const createServiceWithLimit = (limit: number) => { const config: IUnleashConfig = createTestConfig({ @@ -15,28 +12,18 @@ const createServiceWithLimit = (limit: number) => { }, }, }); + config.resourceLimits.apiTokens = limit; + + const { apiTokenService, environmentStore } = + createFakeApiTokenService(config); - const apiTokenStore = new FakeApiTokenStore(); - const environmentStore = new FakeEnvironmentStore(); environmentStore.create({ name: 'production', type: 'production', enabled: true, - protected: true, sortOrder: 1, }); - - const eventService = createFakeEventsService(config); - - config.resourceLimits.apiTokens = limit; - - const service = new ApiTokenService( - { apiTokenStore, environmentStore }, - config, - eventService, - ); - - return service; + return apiTokenService; }; test('Should allow you to create tokens up to and including the limit', async () => { diff --git a/src/lib/services/api-token-service.test.ts b/src/lib/services/api-token-service.test.ts index 116eb811153b..34bb1fb72e03 100644 --- a/src/lib/services/api-token-service.test.ts +++ b/src/lib/services/api-token-service.test.ts @@ -1,10 +1,6 @@ -import { ApiTokenService } from './api-token-service'; import { createTestConfig } from '../../test/config/test-config'; import type { IUnleashConfig, IUnleashOptions, IUser } from '../server-impl'; import { ApiTokenType, type IApiTokenCreate } from '../types/models/api-token'; -import FakeApiTokenStore from '../../test/fixtures/fake-api-token-store'; -import FakeEnvironmentStore from '../features/project-environments/fake-environment-store'; -import FakeEventStore from '../../test/fixtures/fake-event-store'; import { ADMIN_TOKEN_USER, API_TOKEN_CREATED, @@ -13,10 +9,8 @@ import { TEST_AUDIT_USER, } from '../types'; import { addDays, minutesToMilliseconds, subDays } from 'date-fns'; -import EventService from '../features/events/event-service'; -import FakeFeatureTagStore from '../../test/fixtures/fake-feature-tag-store'; -import { createFakeEventsService } from '../../lib/features'; import { extractAuditInfoFromUser } from '../util'; +import { createFakeApiTokenService } from '../features/api-tokens/createApiTokenService'; test('Should init api token', async () => { const token = { @@ -37,20 +31,11 @@ test('Should init api token', async () => { }, }, }); - const apiTokenStore = new FakeApiTokenStore(); - const environmentStore = new FakeEnvironmentStore(); + const { apiTokenStore } = createFakeApiTokenService(config); const insertCalled = new Promise((resolve) => { apiTokenStore.on('insert', resolve); }); - const eventService = createFakeEventsService(config); - - new ApiTokenService( - { apiTokenStore, environmentStore }, - config, - eventService, - ); - await insertCalled; const tokens = await apiTokenStore.getAll(); @@ -69,31 +54,15 @@ test("Shouldn't return frontend token when secret is undefined", async () => { }; const config: IUnleashConfig = createTestConfig({}); - const apiTokenStore = new FakeApiTokenStore(); - const environmentStore = new FakeEnvironmentStore(); - - const eventService = new EventService( - { - eventStore: new FakeEventStore(), - featureTagStore: new FakeFeatureTagStore(), - }, - config, - ); - + const { environmentStore, apiTokenService } = + createFakeApiTokenService(config); await environmentStore.create({ name: 'default', enabled: true, - protected: true, type: 'test', sortOrder: 1, }); - const apiTokenService = new ApiTokenService( - { apiTokenStore, environmentStore }, - config, - eventService, - ); - await apiTokenService.createApiTokenWithProjects(token); await apiTokenService.fetchActiveTokens(); @@ -111,30 +80,16 @@ test('Api token operations should all have events attached', async () => { }; const config: IUnleashConfig = createTestConfig({}); - const apiTokenStore = new FakeApiTokenStore(); - const environmentStore = new FakeEnvironmentStore(); - - const eventService = new EventService( - { - eventStore: new FakeEventStore(), - featureTagStore: new FakeFeatureTagStore(), - }, - config, - ); + const { environmentStore, apiTokenService, eventService } = + createFakeApiTokenService(config); await environmentStore.create({ name: 'default', enabled: true, - protected: true, type: 'test', sortOrder: 1, }); - const apiTokenService = new ApiTokenService( - { apiTokenStore, environmentStore }, - config, - eventService, - ); const saved = await apiTokenService.createApiTokenWithProjects(token); const newExpiry = addDays(new Date(), 30); await apiTokenService.updateExpiry( @@ -171,17 +126,8 @@ test('Api token operations should all have events attached', async () => { test('getUserForToken should get a user with admin token user id and token name', async () => { const config = createTestConfig(); - const apiTokenStore = new FakeApiTokenStore(); - const environmentStore = new FakeEnvironmentStore(); - - const eventService = createFakeEventsService(config); - - const tokenService = new ApiTokenService( - { apiTokenStore, environmentStore }, - config, - eventService, - ); - const token = await tokenService.createApiTokenWithProjects( + const { apiTokenService } = createFakeApiTokenService(config); + const token = await apiTokenService.createApiTokenWithProjects( { environment: '*', projects: ['*'], @@ -191,7 +137,7 @@ test('getUserForToken should get a user with admin token user id and token name' extractAuditInfoFromUser(ADMIN_TOKEN_USER as IUser), ); - const user = await tokenService.getUserForToken(token.secret); + const user = await apiTokenService.getUserForToken(token.secret); expect(user).toBeDefined(); expect(user!.username).toBe(token.tokenName); expect(user!.internalAdminTokenUserId).toBe(ADMIN_TOKEN_USER.id); @@ -209,14 +155,8 @@ describe('API token getTokenWithCache', () => { const setup = (options?: IUnleashOptions) => { const config: IUnleashConfig = createTestConfig(options); - const apiTokenStore = new FakeApiTokenStore(); - const environmentStore = new FakeEnvironmentStore(); - - const apiTokenService = new ApiTokenService( - { apiTokenStore, environmentStore }, - config, - createFakeEventsService(config), - ); + const { apiTokenService, apiTokenStore } = + createFakeApiTokenService(config); return { apiTokenService, apiTokenStore, diff --git a/src/lib/services/index.ts b/src/lib/services/index.ts index ce69c31d8798..58a0328fc68f 100644 --- a/src/lib/services/index.ts +++ b/src/lib/services/index.ts @@ -130,6 +130,10 @@ import { FeatureLifecycleService } from '../features/feature-lifecycle/feature-l import { createFakeFeatureLifecycleService } from '../features/feature-lifecycle/createFeatureLifecycle'; import { FeatureLifecycleReadModel } from '../features/feature-lifecycle/feature-lifecycle-read-model'; import { FakeFeatureLifecycleReadModel } from '../features/feature-lifecycle/fake-feature-lifecycle-read-model'; +import { + createApiTokenService, + createFakeApiTokenService, +} from '../features/api-tokens/createApiTokenService'; export const createServices = ( stores: IUnleashStores, @@ -144,7 +148,9 @@ export const createServices = ( groupService, eventService, ); - const apiTokenService = new ApiTokenService(stores, config, eventService); + const apiTokenService = db + ? createApiTokenService(db, config) + : createFakeApiTokenService(config).apiTokenService; const lastSeenService = db ? createLastSeenService(db, config) : createFakeLastSeenService(config); diff --git a/src/test/e2e/api/admin/api-token.auth.e2e.test.ts b/src/test/e2e/api/admin/api-token.auth.e2e.test.ts index 8d640330fa66..67404d10da36 100644 --- a/src/test/e2e/api/admin/api-token.auth.e2e.test.ts +++ b/src/test/e2e/api/admin/api-token.auth.e2e.test.ts @@ -67,7 +67,12 @@ test('editor users should only get client or frontend tokens', async () => { }); }; - const { request, destroy } = await setupAppWithCustomAuth(stores, preHook); + const { request, destroy } = await setupAppWithCustomAuth( + stores, + preHook, + undefined, + db.rawDatabase, + ); await stores.apiTokenStore.insert({ environment: '', @@ -124,7 +129,12 @@ test('viewer users should not be allowed to fetch tokens', async () => { }); }; - const { request, destroy } = await setupAppWithCustomAuth(stores, preHook); + const { request, destroy } = await setupAppWithCustomAuth( + stores, + preHook, + undefined, + db.rawDatabase, + ); await stores.apiTokenStore.insert({ environment: '', @@ -166,7 +176,12 @@ test('Only token-admins should be allowed to create token', async () => { }); }; - const { request, destroy } = await setupAppWithCustomAuth(stores, preHook); + const { request, destroy } = await setupAppWithCustomAuth( + stores, + preHook, + undefined, + db.rawDatabase, + ); await request .post('/api/admin/api-tokens') @@ -194,7 +209,12 @@ test('Token-admin should be allowed to create token', async () => { }); }; - const { request, destroy } = await setupAppWithCustomAuth(stores, preHook); + const { request, destroy } = await setupAppWithCustomAuth( + stores, + preHook, + undefined, + db.rawDatabase, + ); await request .post('/api/admin/api-tokens') @@ -211,7 +231,11 @@ test('Token-admin should be allowed to create token', async () => { test('An admin token should be allowed to create a token', async () => { expect.assertions(2); - const { request, destroy, services } = await setupAppWithAuth(stores); + const { request, destroy, services } = await setupAppWithAuth( + stores, + undefined, + db.rawDatabase, + ); const { secret } = await services.apiTokenService.createApiTokenWithProjects( @@ -348,6 +372,8 @@ describe('Fine grained API token permissions', () => { const { request, destroy } = await setupAppWithCustomAuth( stores, preHook, + undefined, + db.rawDatabase, ); await request .post('/api/admin/api-tokens') @@ -403,6 +429,8 @@ describe('Fine grained API token permissions', () => { const { request, destroy } = await setupAppWithCustomAuth( stores, preHook, + undefined, + db.rawDatabase, ); await request .post('/api/admin/api-tokens') @@ -458,6 +486,8 @@ describe('Fine grained API token permissions', () => { const { request, destroy } = await setupAppWithCustomAuth( stores, preHook, + undefined, + db.rawDatabase, ); await request .post('/api/admin/api-tokens') @@ -514,6 +544,8 @@ describe('Fine grained API token permissions', () => { const { request, destroy } = await setupAppWithCustomAuth( stores, preHook, + undefined, + db.rawDatabase, ); await stores.apiTokenStore.insert({ environment: '', @@ -597,6 +629,8 @@ describe('Fine grained API token permissions', () => { const { request, destroy } = await setupAppWithCustomAuth( stores, preHook, + undefined, + db.rawDatabase, ); await stores.apiTokenStore.insert({ environment: '', @@ -657,6 +691,8 @@ describe('Fine grained API token permissions', () => { const { request, destroy } = await setupAppWithCustomAuth( stores, preHook, + undefined, + db.rawDatabase, ); await stores.apiTokenStore.insert({ environment: '', @@ -716,6 +752,8 @@ describe('Fine grained API token permissions', () => { const { request, destroy } = await setupAppWithCustomAuth( stores, preHook, + undefined, + db.rawDatabase, ); await stores.apiTokenStore.insert({ environment: '', @@ -802,6 +840,8 @@ describe('Fine grained API token permissions', () => { const { request, destroy } = await setupAppWithCustomAuth( stores, preHook, + undefined, + db.rawDatabase, ); const token = await stores.apiTokenStore.insert({ environment: '', @@ -862,6 +902,8 @@ describe('Fine grained API token permissions', () => { const { request, destroy } = await setupAppWithCustomAuth( stores, preHook, + undefined, + db.rawDatabase, ); const token = await stores.apiTokenStore.insert({ environment: '', @@ -922,6 +964,8 @@ describe('Fine grained API token permissions', () => { const { request, destroy } = await setupAppWithCustomAuth( stores, preHook, + undefined, + db.rawDatabase, ); const token = await stores.apiTokenStore.insert({ environment: '', @@ -986,6 +1030,8 @@ describe('Fine grained API token permissions', () => { const { request, destroy } = await setupAppWithCustomAuth( stores, preHook, + undefined, + db.rawDatabase, ); const token = await stores.apiTokenStore.insert({ environment: '', @@ -1046,6 +1092,8 @@ describe('Fine grained API token permissions', () => { const { request, destroy } = await setupAppWithCustomAuth( stores, preHook, + undefined, + db.rawDatabase, ); const token = await stores.apiTokenStore.insert({ environment: '', @@ -1105,6 +1153,8 @@ describe('Fine grained API token permissions', () => { const { request, destroy } = await setupAppWithCustomAuth( stores, preHook, + undefined, + db.rawDatabase, ); const token = await stores.apiTokenStore.insert({ environment: '', diff --git a/src/test/e2e/api/auth/leading-slashes-are-stripped.e2e.test.ts b/src/test/e2e/api/auth/leading-slashes-are-stripped.e2e.test.ts index c68f3b20f38d..7569073d4d56 100644 --- a/src/test/e2e/api/auth/leading-slashes-are-stripped.e2e.test.ts +++ b/src/test/e2e/api/auth/leading-slashes-are-stripped.e2e.test.ts @@ -15,13 +15,24 @@ beforeAll(async () => { getLogger, ); stores = db.stores; - app = await setupAppWithAuth(stores, { - authentication: { enableApiToken: true, type: IAuthType.DEMO }, - }); - appWithBaseUrl = await setupAppWithAuth(stores, { - server: { unleashUrl: 'http://localhost:4242', basePathUri: '/demo' }, - authentication: { enableApiToken: true, type: IAuthType.DEMO }, - }); + app = await setupAppWithAuth( + stores, + { + authentication: { enableApiToken: true, type: IAuthType.DEMO }, + }, + db.rawDatabase, + ); + appWithBaseUrl = await setupAppWithAuth( + stores, + { + server: { + unleashUrl: 'http://localhost:4242', + basePathUri: '/demo', + }, + authentication: { enableApiToken: true, type: IAuthType.DEMO }, + }, + db.rawDatabase, + ); }); afterAll(async () => { diff --git a/src/test/e2e/api/client/metrics.e2e.access.e2e.test.ts b/src/test/e2e/api/client/metrics.access.e2e.test.ts similarity index 94% rename from src/test/e2e/api/client/metrics.e2e.access.e2e.test.ts rename to src/test/e2e/api/client/metrics.access.e2e.test.ts index 012afbbaf6ea..971a2541134c 100644 --- a/src/test/e2e/api/client/metrics.e2e.access.e2e.test.ts +++ b/src/test/e2e/api/client/metrics.access.e2e.test.ts @@ -9,7 +9,7 @@ let db: ITestDb; beforeAll(async () => { db = await dbInit('metrics_api_e2e_access_client', getLogger); - app = await setupAppWithAuth(db.stores); + app = await setupAppWithAuth(db.stores, undefined, db.rawDatabase); }); afterAll(async () => { diff --git a/src/test/e2e/services/api-token-service.e2e.test.ts b/src/test/e2e/services/api-token-service.e2e.test.ts index 02be7666f9e5..879561d7370d 100644 --- a/src/test/e2e/services/api-token-service.e2e.test.ts +++ b/src/test/e2e/services/api-token-service.e2e.test.ts @@ -1,6 +1,6 @@ import dbInit, { type ITestDb } from '../helpers/database-init'; import getLogger from '../../fixtures/no-logger'; -import { ApiTokenService } from '../../../lib/services/api-token-service'; +import type { ApiTokenService } from '../../../lib/services/api-token-service'; import { createTestConfig } from '../../config/test-config'; import { ApiTokenType, @@ -12,6 +12,7 @@ import type ProjectService from '../../../lib/features/project/project-service'; import { createProjectService } from '../../../lib/features'; import { EventService } from '../../../lib/services'; import { type IUnleashStores, TEST_AUDIT_USER } from '../../../lib/types'; +import { createApiTokenService } from '../../../lib/features/api-tokens/createApiTokenService'; let db: ITestDb; let stores: IUnleashStores; @@ -45,7 +46,7 @@ beforeAll(async () => { await projectService.createProject(project, user, TEST_AUDIT_USER); - apiTokenService = new ApiTokenService(stores, config, eventService); + apiTokenService = createApiTokenService(db.rawDatabase, config); }); afterAll(async () => { diff --git a/src/test/e2e/services/edge-service.e2e.test.ts b/src/test/e2e/services/edge-service.e2e.test.ts index 8c8734281a06..30f2c0c9d554 100644 --- a/src/test/e2e/services/edge-service.e2e.test.ts +++ b/src/test/e2e/services/edge-service.e2e.test.ts @@ -1,6 +1,5 @@ import dbInit, { type ITestDb } from '../helpers/database-init'; import getLogger from '../../fixtures/no-logger'; -import { ApiTokenService } from '../../../lib/services/api-token-service'; import { createTestConfig } from '../../config/test-config'; import { ApiTokenType, @@ -12,6 +11,7 @@ import type ProjectService from '../../../lib/features/project/project-service'; import { createProjectService } from '../../../lib/features'; import { EdgeService, EventService } from '../../../lib/services'; import { type IUnleashStores, TEST_AUDIT_USER } from '../../../lib/types'; +import { createApiTokenService } from '../../../lib/features/api-tokens/createApiTokenService'; let db: ITestDb; let stores: IUnleashStores; @@ -45,7 +45,7 @@ beforeAll(async () => { await projectService.createProject(project, user, TEST_AUDIT_USER); - const apiTokenService = new ApiTokenService(stores, config, eventService); + const apiTokenService = createApiTokenService(db.rawDatabase, config); edgeService = new EdgeService({ apiTokenService }, config); });