Skip to content

Commit

Permalink
refactor: feature oriented architecture for project-environment (#5510)
Browse files Browse the repository at this point in the history
  • Loading branch information
kwasniew committed Dec 1, 2023
1 parent 63f6af0 commit 26f9cf9
Show file tree
Hide file tree
Showing 32 changed files with 217 additions and 97 deletions.
2 changes: 1 addition & 1 deletion src/lib/db/index.ts
Expand Up @@ -20,7 +20,7 @@ import { ResetTokenStore } from './reset-token-store';
import UserFeedbackStore from './user-feedback-store';
import FeatureStrategyStore from '../features/feature-toggle/feature-toggle-strategies-store';
import FeatureToggleClientStore from '../features/client-feature-toggles/client-feature-toggle-store';
import EnvironmentStore from './environment-store';
import EnvironmentStore from '../features/project-environments/environment-store';
import FeatureTagStore from './feature-tag-store';
import { FeatureEnvironmentStore } from './feature-environment-store';
import { ClientMetricsStoreV2 } from './client-metrics-store-v2';
Expand Down
4 changes: 2 additions & 2 deletions src/lib/features/access/createAccessService.ts
Expand Up @@ -3,14 +3,14 @@ import EventStore from '../../db/event-store';
import GroupStore from '../../db/group-store';
import { AccountStore } from '../../db/account-store';
import RoleStore from '../../db/role-store';
import EnvironmentStore from '../../db/environment-store';
import EnvironmentStore from '../project-environments/environment-store';
import { AccessStore } from '../../db/access-store';
import { AccessService, EventService, GroupService } from '../../services';
import FakeGroupStore from '../../../test/fixtures/fake-group-store';
import FakeEventStore from '../../../test/fixtures/fake-event-store';
import { FakeAccountStore } from '../../../test/fixtures/fake-account-store';
import FakeRoleStore from '../../../test/fixtures/fake-role-store';
import FakeEnvironmentStore from '../../../test/fixtures/fake-environment-store';
import FakeEnvironmentStore from '../project-environments/fake-environment-store';
import FakeAccessStore from '../../../test/fixtures/fake-access-store';
import FeatureTagStore from '../../db/feature-tag-store';
import FakeFeatureTagStore from '../../../test/fixtures/fake-feature-tag-store';
Expand Down
4 changes: 2 additions & 2 deletions src/lib/features/feature-toggle/createFeatureToggleService.ts
Expand Up @@ -14,7 +14,7 @@ import GroupStore from '../../db/group-store';
import { AccountStore } from '../../db/account-store';
import { AccessStore } from '../../db/access-store';
import RoleStore from '../../db/role-store';
import EnvironmentStore from '../../db/environment-store';
import EnvironmentStore from '../project-environments/environment-store';
import { Db } from '../../db/db';
import { IUnleashConfig } from '../../types';
import FakeEventStore from '../../../test/fixtures/fake-event-store';
Expand All @@ -28,7 +28,7 @@ import FakeGroupStore from '../../../test/fixtures/fake-group-store';
import { FakeAccountStore } from '../../../test/fixtures/fake-account-store';
import FakeAccessStore from '../../../test/fixtures/fake-access-store';
import FakeRoleStore from '../../../test/fixtures/fake-role-store';
import FakeEnvironmentStore from '../../../test/fixtures/fake-environment-store';
import FakeEnvironmentStore from '../project-environments/fake-environment-store';
import EventStore from '../../db/event-store';
import {
createChangeRequestAccessReadModel,
Expand Down
Expand Up @@ -10,7 +10,7 @@ import {
IVariant,
SKIP_CHANGE_REQUEST,
} from '../../../types';
import EnvironmentService from '../../../services/environment-service';
import EnvironmentService from '../../project-environments/environment-service';
import { ForbiddenError, PatternError, PermissionError } from '../../../error';
import { ISegmentService } from '../../../segments/segment-service-interface';
import { createFeatureToggleService, createSegmentService } from '../..';
Expand Down
1 change: 1 addition & 0 deletions src/lib/features/index.ts
Expand Up @@ -6,3 +6,4 @@ export * from './change-request-access-service/createChangeRequestAccessReadMode
export * from './segment/createSegmentService';
export * from './dependent-features/createDependentFeaturesService';
export * from './tag-type/createTagTypeService';
export * from './project-environments/createEnvironmentService';
2 changes: 1 addition & 1 deletion src/lib/features/instance-stats/instance-stats-service.ts
Expand Up @@ -7,7 +7,7 @@ import {
IUnleashStores,
} from '../../types/stores';
import { IContextFieldStore } from '../../types/stores/context-field-store';
import { IEnvironmentStore } from '../../types/stores/environment-store';
import { IEnvironmentStore } from '../project-environments/environment-store-type';
import { IFeatureToggleStore } from '../feature-toggle/types/feature-toggle-store-type';
import { IGroupStore } from '../../types/stores/group-store';
import { IProjectStore } from '../../types/stores/project-store';
Expand Down
88 changes: 88 additions & 0 deletions src/lib/features/project-environments/createEnvironmentService.ts
@@ -0,0 +1,88 @@
import { Db } from '../../db/db';
import EventStore from '../../db/event-store';
import { IUnleashConfig } from '../../types';
import { EventService } from '../../services';
import FeatureTagStore from '../../db/feature-tag-store';
import FakeEventStore from '../../../test/fixtures/fake-event-store';
import FakeFeatureTagStore from '../../../test/fixtures/fake-feature-tag-store';
import EnvironmentService from './environment-service';
import EnvironmentStore from './environment-store';
import FeatureStrategiesStore from '../feature-toggle/feature-toggle-strategies-store';
import { FeatureEnvironmentStore } from '../../db/feature-environment-store';
import ProjectStore from '../../db/project-store';
import FakeFeatureEnvironmentStore from '../../../test/fixtures/fake-feature-environment-store';
import FakeProjectStore from '../../../test/fixtures/fake-project-store';
import FakeFeatureStrategiesStore from '../feature-toggle/fakes/fake-feature-strategies-store';
import FakeEnvironmentStore from './fake-environment-store';

export const createEnvironmentService =
(config: IUnleashConfig) =>
(db: Db): EnvironmentService => {
const { getLogger, eventBus, flagResolver } = config;
const eventStore = new EventStore(db, getLogger);
const featureTagStore = new FeatureTagStore(db, eventBus, getLogger);
const featureEnvironmentStore = new FeatureEnvironmentStore(
db,
eventBus,
getLogger,
);
const projectStore = new ProjectStore(
db,
eventBus,
getLogger,
flagResolver,
);
const featureStrategiesStore = new FeatureStrategiesStore(
db,
eventBus,
getLogger,
flagResolver,
);
const environmentStore = new EnvironmentStore(db, eventBus, getLogger);
const eventService = new EventService(
{
eventStore,
featureTagStore,
},
config,
);
return new EnvironmentService(
{
environmentStore,
featureStrategiesStore,
featureEnvironmentStore,
projectStore,
},
config,
eventService,
);
};

export const createFakeEnvironmentService = (
config: IUnleashConfig,
): EnvironmentService => {
const eventStore = new FakeEventStore();
const featureTagStore = new FakeFeatureTagStore();
const featureEnvironmentStore = new FakeFeatureEnvironmentStore();
const projectStore = new FakeProjectStore();
const featureStrategiesStore = new FakeFeatureStrategiesStore();
const environmentStore = new FakeEnvironmentStore();
const eventService = new EventService(
{
eventStore,
featureTagStore,
},
config,
);

return new EnvironmentService(
{
environmentStore,
featureStrategiesStore,
featureEnvironmentStore,
projectStore,
},
config,
eventService,
);
};
@@ -1,10 +1,10 @@
import EnvironmentService from '../../../lib/services/environment-service';
import { createTestConfig } from '../../config/test-config';
import dbInit from '../helpers/database-init';
import NotFoundError from '../../../lib/error/notfound-error';
import { IUnleashStores } from '../../../lib/types';
import NameExistsError from '../../../lib/error/name-exists-error';
import { EventService } from '../../../lib/services';
import EnvironmentService from './environment-service';
import { createTestConfig } from '../../../test/config/test-config';
import dbInit from '../../../test/e2e/helpers/database-init';
import NotFoundError from '../../error/notfound-error';
import { IUnleashStores } from '../../types';
import NameExistsError from '../../error/name-exists-error';
import { EventService } from '../../services';

let stores: IUnleashStores;
let db;
Expand Down
Expand Up @@ -10,17 +10,17 @@ import {
IUnleashStores,
PROJECT_ENVIRONMENT_ADDED,
PROJECT_ENVIRONMENT_REMOVED,
} from '../types';
import { Logger } from '../logger';
import { BadDataError, UNIQUE_CONSTRAINT_VIOLATION } from '../error';
import NameExistsError from '../error/name-exists-error';
import { sortOrderSchema } from './state-schema';
import NotFoundError from '../error/notfound-error';
} from '../../types';
import { Logger } from '../../logger';
import { BadDataError, UNIQUE_CONSTRAINT_VIOLATION } from '../../error';
import NameExistsError from '../../error/name-exists-error';
import { sortOrderSchema } from '../../services/state-schema';
import NotFoundError from '../../error/notfound-error';
import { IProjectStore } from 'lib/types/stores/project-store';
import MinimumOneEnvironmentError from '../error/minimum-one-environment-error';
import MinimumOneEnvironmentError from '../../error/minimum-one-environment-error';
import { IFlagResolver } from 'lib/types/experimental';
import { CreateFeatureStrategySchema } from '../openapi';
import EventService from './event-service';
import { CreateFeatureStrategySchema } from '../../openapi';
import EventService from '../../services/event-service';

export default class EnvironmentService {
private logger: Logger;
Expand Down
Expand Up @@ -2,8 +2,8 @@ import {
IEnvironment,
IEnvironmentCreate,
IProjectEnvironment,
} from '../model';
import { Store } from './store';
} from '../../types/model';
import { Store } from '../../types/stores/store';

export interface IEnvironmentStore extends Store<IEnvironment, string> {
exists(name: string): Promise<boolean>;
Expand Down
@@ -1,17 +1,17 @@
import EventEmitter from 'events';
import { Db } from './db';
import { Logger, LogProvider } from '../logger';
import metricsHelper from '../util/metrics-helper';
import { DB_TIME } from '../metric-events';
import { Db } from '../../db/db';
import { Logger, LogProvider } from '../../logger';
import metricsHelper from '../../util/metrics-helper';
import { DB_TIME } from '../../metric-events';
import {
IEnvironment,
IEnvironmentCreate,
IProjectEnvironment,
} from '../types/model';
import NotFoundError from '../error/notfound-error';
import { IEnvironmentStore } from '../types/stores/environment-store';
import { snakeCaseKeys } from '../util/snakeCase';
import { CreateFeatureStrategySchema } from '../openapi';
} from '../../types/model';
import NotFoundError from '../../error/notfound-error';
import { IEnvironmentStore } from './environment-store-type';
import { snakeCaseKeys } from '../../util/snakeCase';
import { CreateFeatureStrategySchema } from '../../openapi';

interface IEnvironmentsTable {
name: string;
Expand Down
@@ -1,10 +1,10 @@
import dbInit, { ITestDb } from '../../../helpers/database-init';
import dbInit, { ITestDb } from '../../../test/e2e/helpers/database-init';
import {
IUnleashTest,
setupAppWithCustomConfig,
} from '../../../helpers/test-helper';
import getLogger from '../../../../fixtures/no-logger';
import { DEFAULT_ENV } from '../../../../../lib/util';
} from '../../../test/e2e/helpers/test-helper';
import getLogger from '../../../test/fixtures/no-logger';
import { DEFAULT_ENV } from '../../util';

let app: IUnleashTest;
let db: ITestDb;
Expand Down
@@ -1,13 +1,13 @@
import { Request, Response } from 'express';
import Controller from '../../controller';
import Controller from '../../routes/controller';
import {
IUnleashConfig,
IUnleashServices,
serializeDates,
UPDATE_PROJECT,
} from '../../../types';
import { Logger } from '../../../logger';
import EnvironmentService from '../../../services/environment-service';
} from '../../types';
import { Logger } from '../../logger';
import EnvironmentService from './environment-service';
import {
createFeatureStrategySchema,
CreateFeatureStrategySchema,
Expand All @@ -16,10 +16,11 @@ import {
emptyResponse,
getStandardResponses,
ProjectEnvironmentSchema,
} from '../../../openapi';
import { OpenApiService, ProjectService } from '../../../services';
import { extractUsername } from '../../../util';
import { IAuthRequest } from '../../unleash-types';
} from '../../openapi';
import { OpenApiService, ProjectService } from '../../services';
import { extractUsername } from '../../util';
import { IAuthRequest } from '../../routes/unleash-types';
import { WithTransactional } from '../../db/transaction';

const PREFIX = '/:projectId/environments';

Expand All @@ -31,7 +32,7 @@ interface IProjectEnvironmentParams {
export default class EnvironmentsController extends Controller {
private logger: Logger;

private environmentService: EnvironmentService;
private environmentService: WithTransactional<EnvironmentService>;

private openApiService: OpenApiService;

Expand All @@ -40,18 +41,20 @@ export default class EnvironmentsController extends Controller {
constructor(
config: IUnleashConfig,
{
environmentService,
transactionalEnvironmentService,
openApiService,
projectService,
}: Pick<
IUnleashServices,
'environmentService' | 'openApiService' | 'projectService'
| 'transactionalEnvironmentService'
| 'openApiService'
| 'projectService'
>,
) {
super(config);

this.logger = config.getLogger('admin-api/project/environments.ts');
this.environmentService = environmentService;
this.environmentService = transactionalEnvironmentService;
this.openApiService = openApiService;
this.projectService = projectService;

Expand Down Expand Up @@ -137,10 +140,12 @@ export default class EnvironmentsController extends Controller {
const { environment } = req.body;
await this.projectService.getProject(projectId); // Validates that the project exists

await this.environmentService.addEnvironmentToProject(
environment,
projectId,
extractUsername(req),
await this.environmentService.transactional((service) =>
service.addEnvironmentToProject(
environment,
projectId,
extractUsername(req),
),
);

res.status(200).end();
Expand All @@ -152,10 +157,12 @@ export default class EnvironmentsController extends Controller {
): Promise<void> {
const { projectId, environment } = req.params;

await this.environmentService.removeEnvironmentFromProject(
environment,
projectId,
extractUsername(req),
await this.environmentService.transactional((service) =>
service.removeEnvironmentFromProject(
environment,
projectId,
extractUsername(req),
),
);

res.status(200).end();
Expand All @@ -171,11 +178,13 @@ export default class EnvironmentsController extends Controller {
const { projectId, environment } = req.params;
const strategy = req.body;

const saved = await this.environmentService.updateDefaultStrategy(
environment,
projectId,
strategy,
extractUsername(req),
const saved = await this.environmentService.transactional((service) =>
service.updateDefaultStrategy(
environment,
projectId,
strategy,
extractUsername(req),
),
);

this.openApiService.respondWithValidation(
Expand Down
@@ -1,6 +1,6 @@
import { IEnvironment, IProjectEnvironment } from '../../lib/types/model';
import NotFoundError from '../../lib/error/notfound-error';
import { IEnvironmentStore } from '../../lib/types/stores/environment-store';
import { IEnvironment, IProjectEnvironment } from '../../types/model';
import NotFoundError from '../../error/notfound-error';
import { IEnvironmentStore } from './environment-store-type';

export default class FakeEnvironmentStore implements IEnvironmentStore {
importEnvironments(envs: IEnvironment[]): Promise<IEnvironment[]> {
Expand Down

0 comments on commit 26f9cf9

Please sign in to comment.