Skip to content

Commit

Permalink
fix: refactor getProjectOverview store method (#4972)
Browse files Browse the repository at this point in the history
This PR cleans up and refactors the feature-strategy-store method
getFeatureOverview to join on the new table and attempts to make the
function more readable by extracting some of the logic into separate
functions. Keeping the LastSeenMapper for now in case there is a reason
to use it for the other endpoints.
  • Loading branch information
FredrikOseberg authored Oct 10, 2023
1 parent ab739eb commit 30d8444
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 64 deletions.
122 changes: 75 additions & 47 deletions src/lib/db/feature-strategy-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,6 @@ const COLUMNS = [
'created_at',
'disabled',
];
/*
const mapperToColumnNames = {
createdAt: 'created_at',
featureName: 'feature_name',
strategyName: 'strategy_name',
};
*/

const T = {
features: 'features',
Expand Down Expand Up @@ -111,6 +104,32 @@ function mapInput(input: IFeatureStrategy): IFeatureStrategiesTable {
};
}

const getUniqueRows = (rows: any[]) => {
const seen = {};
return rows.filter((row) => {
const key = `${row.environment}-${row.feature_name}`;
if (seen[key]) {
return false;
}
seen[key] = true;
return true;
});
};

const sortEnvironments = (overview: IFeatureOverview) => {
return Object.values(overview).map((data: IFeatureOverview) => ({
...data,
environments: data.environments
.filter((f) => f.name)
.sort((a, b) => {
if (a.sortOrder === b.sortOrder) {
return a.name.localeCompare(b.name);
}
return a.sortOrder - b.sortOrder;
}),
}));
};

interface StrategyUpdate {
strategy_name: string;
parameters: object;
Expand Down Expand Up @@ -514,6 +533,14 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
)
.leftJoin('feature_tag as ft', 'ft.feature_name', 'features.name');

if (this.flagResolver.isEnabled('useLastSeenRefactor')) {
query.leftJoin(
'last_seen_at_metrics',
'last_seen_at_metrics.environment',
'environments.name',
);
}

let selectColumns = [
'features.name as feature_name',
'features.description as description',
Expand All @@ -525,13 +552,22 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
'feature_environments.enabled as enabled',
'feature_environments.environment as environment',
'feature_environments.variants as variants',
'feature_environments.last_seen_at as env_last_seen_at',
'environments.type as environment_type',
'environments.sort_order as environment_sort_order',
'ft.tag_value as tag_value',
'ft.tag_type as tag_type',
] as (string | Raw<any>)[];

if (this.flagResolver.isEnabled('useLastSeenRefactor')) {
selectColumns.push(
'last_seen_at_metrics.last_seen_at as env_last_seen_at',
);
} else {
selectColumns.push(
'feature_environments.last_seen_at as env_last_seen_at',
);
}

if (userId) {
query = query.leftJoin(`favorite_features`, function () {
this.on('favorite_features.feature', 'features.name').andOnVal(
Expand All @@ -552,50 +588,42 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
const rows = await query;

if (rows.length > 0) {
const overview = rows.reduce((acc, row) => {
if (acc[row.feature_name] !== undefined) {
acc[row.feature_name].environments.push(
FeatureStrategiesStore.getEnvironment(row),
);
if (this.isNewTag(acc[row.feature_name], row)) {
this.addTag(acc[row.feature_name], row);
}
} else {
acc[row.feature_name] = {
type: row.type,
description: row.description,
favorite: row.favorite,
name: row.feature_name,
createdAt: row.created_at,
lastSeenAt: row.last_seen_at,
stale: row.stale,
impressionData: row.impression_data,
environments: [
FeatureStrategiesStore.getEnvironment(row),
],
};
if (this.isNewTag(acc[row.feature_name], row)) {
this.addTag(acc[row.feature_name], row);
}
}
return acc;
}, {});
const overview = this.getFeatureOverviewData(getUniqueRows(rows));

return Object.values(overview).map((o: IFeatureOverview) => ({
...o,
environments: o.environments
.filter((f) => f.name)
.sort((a, b) => {
if (a.sortOrder === b.sortOrder) {
return a.name.localeCompare(b.name);
}
return a.sortOrder - b.sortOrder;
}),
}));
return sortEnvironments(overview);
}
return [];
}

getFeatureOverviewData(rows): IFeatureOverview {
return rows.reduce((acc, row) => {
if (acc[row.feature_name] !== undefined) {
acc[row.feature_name].environments.push(
FeatureStrategiesStore.getEnvironment(row),
);
if (this.isNewTag(acc[row.feature_name], row)) {
this.addTag(acc[row.feature_name], row);
}
} else {
acc[row.feature_name] = {
type: row.type,
description: row.description,
favorite: row.favorite,
name: row.feature_name,
createdAt: row.created_at,
lastSeenAt: row.last_seen_at,
stale: row.stale,
impressionData: row.impression_data,
environments: [FeatureStrategiesStore.getEnvironment(row)],
};
if (this.isNewTag(acc[row.feature_name], row)) {
this.addTag(acc[row.feature_name], row);
}
}
return acc;
}, {});
}

async getStrategyById(id: string): Promise<IFeatureStrategy> {
const strat = await this.db(T.featureStrategies).where({ id }).first();
if (strat) {
Expand Down
18 changes: 1 addition & 17 deletions src/lib/services/project-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1077,22 +1077,6 @@ export default class ProjectService {
this.projectStatsStore.getProjectStats(projectId),
]);

let decoratedFeatures = features;

if (this.flagResolver.isEnabled('useLastSeenRefactor')) {
const mapper = new LastSeenMapper();

const featureNames = features.map((feature) => feature.name);
const lastSeenAtPerEnvironment =
await this.lastSeenReadModel.getForFeature(featureNames);

decoratedFeatures = mapper.mapToFeatures(
decoratedFeatures,
lastSeenAtPerEnvironment,
this.logger,
);
}

return {
stats: projectStats,
name: project.name,
Expand All @@ -1106,7 +1090,7 @@ export default class ProjectService {
updatedAt: project.updatedAt,
createdAt: project.createdAt,
environments,
features: decoratedFeatures,
features: features,
members,
version: 1,
};
Expand Down

0 comments on commit 30d8444

Please sign in to comment.