2024-03-18 13:58:05 +01:00
|
|
|
import type { Db } from '../../db/db';
|
|
|
|
import type { IDependentFeaturesReadModel } from './dependent-features-read-model-type';
|
|
|
|
import type { IDependency, IFeatureDependency } from '../../types';
|
2023-09-27 14:33:51 +02:00
|
|
|
|
2024-03-28 16:53:30 +01:00
|
|
|
interface IVariantName {
|
|
|
|
variant_name: string;
|
|
|
|
}
|
|
|
|
|
2023-09-27 14:33:51 +02:00
|
|
|
export class DependentFeaturesReadModel implements IDependentFeaturesReadModel {
|
|
|
|
private db: Db;
|
|
|
|
|
|
|
|
constructor(db: Db) {
|
|
|
|
this.db = db;
|
|
|
|
}
|
|
|
|
|
2023-10-06 13:39:16 +02:00
|
|
|
async getOrphanParents(parentsAndChildren: string[]): Promise<string[]> {
|
|
|
|
const rows = await this.db('dependent_features')
|
|
|
|
.distinct('parent')
|
|
|
|
.whereIn('parent', parentsAndChildren)
|
|
|
|
.andWhere(function () {
|
|
|
|
this.whereIn('parent', function () {
|
|
|
|
this.select('parent')
|
|
|
|
.from('dependent_features')
|
|
|
|
.whereNotIn('child', parentsAndChildren);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return rows.map((row) => row.parent);
|
|
|
|
}
|
|
|
|
|
2023-10-04 09:27:53 +02:00
|
|
|
async getChildren(parents: string[]): Promise<string[]> {
|
|
|
|
const rows = await this.db('dependent_features').whereIn(
|
2023-09-27 14:33:51 +02:00
|
|
|
'parent',
|
2023-10-04 09:27:53 +02:00
|
|
|
parents,
|
2023-09-27 14:33:51 +02:00
|
|
|
);
|
|
|
|
|
2023-10-04 09:27:53 +02:00
|
|
|
return [...new Set(rows.map((row) => row.child))];
|
2023-09-27 14:33:51 +02:00
|
|
|
}
|
|
|
|
|
2023-09-27 15:07:20 +02:00
|
|
|
async getParents(child: string): Promise<IDependency[]> {
|
2023-09-27 14:33:51 +02:00
|
|
|
const rows = await this.db('dependent_features').where('child', child);
|
|
|
|
|
2023-09-27 15:07:20 +02:00
|
|
|
return rows.map((row) => ({
|
|
|
|
feature: row.parent,
|
|
|
|
enabled: row.enabled,
|
|
|
|
variants: row.variants,
|
|
|
|
}));
|
2023-09-27 14:33:51 +02:00
|
|
|
}
|
|
|
|
|
2023-10-12 11:56:10 +02:00
|
|
|
async getDependencies(children: string[]): Promise<IFeatureDependency[]> {
|
|
|
|
const rows = await this.db('dependent_features').whereIn(
|
|
|
|
'child',
|
|
|
|
children,
|
|
|
|
);
|
|
|
|
|
|
|
|
return rows.map((row) => ({
|
|
|
|
feature: row.child,
|
|
|
|
dependency: {
|
|
|
|
feature: row.parent,
|
|
|
|
enabled: row.enabled,
|
|
|
|
variants: row.variants,
|
|
|
|
},
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2024-03-28 16:53:30 +01:00
|
|
|
async getPossibleParentFeatures(child: string): Promise<string[]> {
|
2023-09-27 15:17:04 +02:00
|
|
|
const result = await this.db('features')
|
|
|
|
.where('features.name', child)
|
|
|
|
.select('features.project');
|
2023-09-27 14:33:51 +02:00
|
|
|
if (result.length === 0) {
|
|
|
|
return [];
|
|
|
|
}
|
2023-09-27 15:17:04 +02:00
|
|
|
const rows = await this.db('features')
|
|
|
|
.leftJoin(
|
|
|
|
'dependent_features',
|
|
|
|
'features.name',
|
|
|
|
'dependent_features.child',
|
|
|
|
)
|
|
|
|
.where('features.project', result[0].project)
|
|
|
|
.andWhere('features.name', '!=', child)
|
|
|
|
.andWhere('dependent_features.child', null)
|
2023-09-28 13:37:52 +02:00
|
|
|
.andWhere('features.archived_at', null)
|
2023-11-01 21:18:19 +01:00
|
|
|
.select('features.name')
|
|
|
|
.orderBy('features.name');
|
2023-09-27 14:33:51 +02:00
|
|
|
|
|
|
|
return rows.map((item) => item.name);
|
|
|
|
}
|
2023-10-04 12:16:52 +02:00
|
|
|
|
2024-03-28 16:53:30 +01:00
|
|
|
async getPossibleParentVariants(parent: string): Promise<string[]> {
|
|
|
|
const strategyVariantsQuery = this.db('feature_strategies')
|
|
|
|
.select(
|
|
|
|
this.db.raw(
|
|
|
|
"jsonb_array_elements(variants)->>'name' as variant_name",
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.where('feature_name', parent);
|
|
|
|
|
|
|
|
const featureEnvironmentVariantsQuery = this.db('feature_environments')
|
|
|
|
.select(
|
|
|
|
this.db.raw(
|
|
|
|
"jsonb_array_elements(variants)->>'name' as variant_name",
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.where('feature_name', parent);
|
|
|
|
|
|
|
|
const results = await Promise.all([
|
|
|
|
strategyVariantsQuery,
|
|
|
|
featureEnvironmentVariantsQuery,
|
|
|
|
]);
|
|
|
|
const flatResults = results
|
|
|
|
.flat()
|
|
|
|
.map((item) => (item as unknown as IVariantName).variant_name);
|
|
|
|
const uniqueResults = [...new Set(flatResults)];
|
|
|
|
|
|
|
|
return uniqueResults.sort();
|
|
|
|
}
|
|
|
|
|
2023-10-20 08:58:03 +02:00
|
|
|
async haveDependencies(features: string[]): Promise<boolean> {
|
2023-10-04 12:16:52 +02:00
|
|
|
const parents = await this.db('dependent_features')
|
2023-10-20 08:58:03 +02:00
|
|
|
.whereIn('parent', features)
|
|
|
|
.orWhereIn('child', features)
|
2023-10-04 12:16:52 +02:00
|
|
|
.limit(1);
|
|
|
|
|
|
|
|
return parents.length > 0;
|
|
|
|
}
|
2023-10-16 20:56:06 +02:00
|
|
|
|
|
|
|
async hasAnyDependencies(): Promise<boolean> {
|
|
|
|
const result = await this.db.raw(
|
|
|
|
`SELECT EXISTS (SELECT 1 FROM dependent_features) AS present`,
|
|
|
|
);
|
|
|
|
const { present } = result.rows[0];
|
|
|
|
return present;
|
|
|
|
}
|
2023-09-27 14:33:51 +02:00
|
|
|
}
|