Skip to content

Commit

Permalink
[Gitar] Cleaning up stale flag: useProjectReadModel with value true (#…
Browse files Browse the repository at this point in the history
…8211)

Co-authored-by: Gitar <[email protected]>
Co-authored-by: Tymoteusz Czech <[email protected]>
  • Loading branch information
3 people authored Sep 23, 2024
1 parent 5dd0fb9 commit 338b5ce
Show file tree
Hide file tree
Showing 10 changed files with 11 additions and 177 deletions.
6 changes: 3 additions & 3 deletions src/lib/features/project/project-owners-read-model.type.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { TransitionalProjectData } from './project-read-model-type';
import type { ProjectForUi } from './project-read-model-type';

export type SystemOwner = { ownerType: 'system' };
export type UserProjectOwner = {
Expand All @@ -17,13 +17,13 @@ type ProjectOwners =

export type ProjectOwnersDictionary = Record<string, ProjectOwners>;

export type IProjectForUiWithOwners = TransitionalProjectData & {
export type IProjectForUiWithOwners = ProjectForUi & {
owners: ProjectOwners;
};

export interface IProjectOwnersReadModel {
addOwners(
projects: TransitionalProjectData[],
projects: ProjectForUi[],
anonymizeProjectOwners?: boolean,
): Promise<IProjectForUiWithOwners[]>;
}
5 changes: 1 addition & 4 deletions src/lib/features/project/project-read-model-type.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { IProjectWithCount, ProjectMode } from '../../types';
import type { ProjectMode } from '../../types';
import type { IProjectQuery } from './project-store-type';

export type ProjectForUi = {
Expand All @@ -16,9 +16,6 @@ export type ProjectForUi = {
lastUpdatedAt: Date | null;
};

// @todo remove with flag useProjectReadModel
export type TransitionalProjectData = ProjectForUi | IProjectWithCount;

export type ProjectForInsights = {
id: string;
health: number;
Expand Down
4 changes: 1 addition & 3 deletions src/lib/features/project/project-service.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,7 @@ beforeAll(async () => {
await stores.accessStore.addUserToRole(opsUser.id, 1, '');
const config = createTestConfig({
getLogger,
experimental: {
flags: { useProjectReadModel: true },
},
experimental: {},
});
eventService = createEventsService(db.rawDatabase, config);
accessService = createAccessService(db.rawDatabase, config);
Expand Down
13 changes: 6 additions & 7 deletions src/lib/features/project/project-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ import type { IProjectFlagCreatorsReadModel } from './project-flag-creators-read
import { throwExceedsLimitError } from '../../error/exceeds-limit-error';
import type EventEmitter from 'events';
import type { ApiTokenService } from '../../services/api-token-service';
import type { TransitionalProjectData } from './project-read-model-type';
import type { ProjectForUi } from './project-read-model-type';
import { canGrantProjectRole } from './can-grant-project-role';

type Days = number;
Expand Down Expand Up @@ -232,10 +232,9 @@ export default class ProjectService {
async getProjects(
query?: IProjectQuery,
userId?: number,
): Promise<TransitionalProjectData[]> {
const getProjects = this.flagResolver.isEnabled('useProjectReadModel')
? () => this.projectReadModel.getProjectsForAdminUi(query, userId)
: () => this.projectStore.getProjectsWithCounts(query, userId);
): Promise<ProjectForUi[]> {
const getProjects = () =>
this.projectReadModel.getProjectsForAdminUi(query, userId);

const projects = await getProjects();

Expand All @@ -257,8 +256,8 @@ export default class ProjectService {
}

async addOwnersToProjects(
projects: TransitionalProjectData[],
): Promise<TransitionalProjectData[]> {
projects: ProjectForUi[],
): Promise<ProjectForUi[]> {
const anonymizeProjectOwners = this.flagResolver.isEnabled(
'anonymizeProjectOwners',
);
Expand Down
9 changes: 0 additions & 9 deletions src/lib/features/project/project-store-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import type {
IFeatureNaming,
IProject,
IProjectApplications,
IProjectWithCount,
ProjectMode,
} from '../../types/model';
import type { Store } from '../../types/stores/store';
Expand Down Expand Up @@ -99,14 +98,6 @@ export interface IProjectStore extends Store<IProject, string> {

getProjectsByUser(userId: number): Promise<string[]>;

/**
* @deprecated Use the appropriate method in the project read model instead.
*/
getProjectsWithCounts(
query?: IProjectQuery,
userId?: number,
): Promise<IProjectWithCount[]>;

count(): Promise<number>;

getAll(query?: IProjectQuery): Promise<IProject[]>;
Expand Down
111 changes: 0 additions & 111 deletions src/lib/features/project/project-store.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Knex } from 'knex';
import type { Logger, LogProvider } from '../../logger';

import NotFoundError from '../../error/notfound-error';
Expand All @@ -9,7 +8,6 @@ import type {
IProjectApplication,
IProjectApplications,
IProjectUpdate,
IProjectWithCount,
ProjectMode,
} from '../../types';
import type {
Expand All @@ -27,7 +25,6 @@ import metricsHelper from '../../util/metrics-helper';
import { DB_TIME } from '../../metric-events';
import type EventEmitter from 'events';
import type { Db } from '../../db/db';
import Raw = Knex.Raw;
import type { CreateFeatureStrategySchema } from '../../openapi';
import { applySearchFilters } from '../feature-search/search-utils';

Expand Down Expand Up @@ -117,114 +114,6 @@ class ProjectStore implements IProjectStore {
return present;
}

async getProjectsWithCounts(
query?: IProjectQuery,
userId?: number,
): Promise<IProjectWithCount[]> {
const projectTimer = this.timer('getProjectsWithCount');
let projects = this.db(TABLE)
.leftJoin('features', 'features.project', 'projects.id')
.leftJoin(
'project_settings',
'project_settings.project',
'projects.id',
)
.leftJoin('project_stats', 'project_stats.project', 'projects.id')
.orderBy('projects.name', 'asc');

if (query?.archived === true) {
projects = projects.whereNot(`${TABLE}.archived_at`, null);
} else {
projects = projects.where(`${TABLE}.archived_at`, null);
}

if (query?.id) {
projects = projects.where(`${TABLE}.id`, query.id);
}

let selectColumns = [
this.db.raw(
'projects.id, projects.name, projects.description, projects.health, projects.updated_at, projects.created_at, ' +
'count(features.name) FILTER (WHERE features.archived_at is null) AS number_of_features, ' +
'count(features.name) FILTER (WHERE features.archived_at is null and features.stale IS TRUE) AS stale_feature_count, ' +
'count(features.name) FILTER (WHERE features.archived_at is null and features.potentially_stale IS TRUE) AS potentially_stale_feature_count',
),
'project_settings.default_stickiness',
'project_settings.project_mode',
'project_stats.avg_time_to_prod_current_window',
'projects.archived_at',
] as (string | Raw<any>)[];

let groupByColumns = [
'projects.id',
'project_settings.default_stickiness',
'project_settings.project_mode',
'project_stats.avg_time_to_prod_current_window',
];

if (userId) {
projects = projects.leftJoin(`favorite_projects`, function () {
this.on('favorite_projects.project', 'projects.id').andOnVal(
'favorite_projects.user_id',
'=',
userId,
);
});
selectColumns = [
...selectColumns,
this.db.raw(
'favorite_projects.project is not null as favorite',
),
];
groupByColumns = [...groupByColumns, 'favorite_projects.project'];
}

const projectAndFeatureCount = await projects
.select(selectColumns)
.groupBy(groupByColumns);

const projectsWithFeatureCount = projectAndFeatureCount.map(
this.mapProjectWithCountRow,
);
projectTimer();
const memberTimer = this.timer('getMemberCount');

const memberCount = await this.getMembersCount();
memberTimer();
const memberMap = new Map<string, number>(
memberCount.map((c) => [c.project, Number(c.count)]),
);

return projectsWithFeatureCount.map((projectWithCount) => {
return {
...projectWithCount,
memberCount: memberMap.get(projectWithCount.id) || 0,
};
});
}

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
mapProjectWithCountRow(row): IProjectWithCount {
return {
name: row.name,
id: row.id,
description: row.description,
health: row.health,
favorite: row.favorite,
featureCount: Number(row.number_of_features) || 0,
staleFeatureCount: Number(row.stale_feature_count) || 0,
potentiallyStaleFeatureCount:
Number(row.potentially_stale_feature_count) || 0,
memberCount: Number(row.number_of_users) || 0,
updatedAt: row.updated_at,
createdAt: row.created_at,
archivedAt: row.archived_at,
mode: row.project_mode || 'open',
defaultStickiness: row.default_stickiness || 'default',
avgTimeToProduction: row.avg_time_to_prod_current_window || 0,
};
}

async getAll(query: IProjectQuery = {}): Promise<IProject[]> {
let projects = this.db
.select(COLUMNS)
Expand Down
5 changes: 0 additions & 5 deletions src/lib/types/experimental.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ export type IFlagKey =
| 'extendedMetrics'
| 'removeUnsafeInlineStyleSrc'
| 'originMiddleware'
| 'useProjectReadModel'
| 'addonUsageMetrics'
| 'onboardingMetrics'
| 'onboardingUI'
Expand Down Expand Up @@ -279,10 +278,6 @@ const flags: IFlags = {
process.env.UNLEASH_EXPERIMENTAL_ORIGIN_MIDDLEWARE,
false,
),
useProjectReadModel: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_USE_PROJECT_READ_MODEL,
false,
),
addonUsageMetrics: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_ADDON_USAGE_METRICS,
false,
Expand Down
11 changes: 0 additions & 11 deletions src/lib/types/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -574,17 +574,6 @@ export interface ICustomRole extends IRole {
description: string;
}

// @deprecated Remove with flag useProjectReadModel
export interface IProjectWithCount extends IProject {
featureCount: number;
staleFeatureCount: number;
potentiallyStaleFeatureCount: number;
memberCount: number;
favorite?: boolean;
avgTimeToProduction: number;
archivedAt?: Date;
}

export interface IClientSegment {
id: number;
constraints: IConstraint[];
Expand Down
1 change: 0 additions & 1 deletion src/server-dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ process.nextTick(async () => {
enableLegacyVariants: false,
extendedMetrics: true,
originMiddleware: true,
useProjectReadModel: true,
addonUsageMetrics: true,
onboardingMetrics: true,
onboardingUI: true,
Expand Down
23 changes: 0 additions & 23 deletions src/test/fixtures/fake-project-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import type {
IProject,
IProjectApplications,
IProjectStore,
IProjectWithCount,
} from '../../lib/types';
import NotFoundError from '../../lib/error/notfound-error';
import type {
Expand All @@ -15,7 +14,6 @@ import type {
IProjectApplicationsSearchParams,
IProjectHealthUpdate,
IProjectInsert,
IProjectQuery,
ProjectEnvironment,
} from '../../lib/features/project/project-store-type';

Expand Down Expand Up @@ -48,27 +46,6 @@ export default class FakeProjectStore implements IProjectStore {
this.projectEnvironment.set(id, environments);
}

async getProjectsWithCounts(
query?: IProjectQuery,
): Promise<IProjectWithCount[]> {
return this.projects
.filter((project) =>
query?.archived
? project.archivedAt !== null
: project.archivedAt === null,
)
.map((project) => {
return {
...project,
memberCount: 0,
featureCount: 0,
staleFeatureCount: 0,
potentiallyStaleFeatureCount: 0,
avgTimeToProduction: 0,
};
});
}

private createInternal(project: IProjectInsert): IProject {
const newProj: ArchivableProject = {
...project,
Expand Down

0 comments on commit 338b5ce

Please sign in to comment.