1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

chore: extract api token service composition root; place it in /features (#7519)

This is a refactoring task, creating an ApiTokenService composition root
in /features.
This commit is contained in:
Thomas Heartman 2024-07-03 11:49:11 +02:00 committed by GitHub
parent 57c1a6edd5
commit 6d913809ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 182 additions and 120 deletions

View File

@ -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,
};
};

View File

@ -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(() => {

View File

@ -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({

View File

@ -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 {

View File

@ -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 () => {

View File

@ -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,

View File

@ -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);

View File

@ -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: '',

View File

@ -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 () => {

View File

@ -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 () => {

View File

@ -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 () => {

View File

@ -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);
});