mirror of
https://github.com/Unleash/unleash.git
synced 2025-09-01 13:47:27 +02:00
feat: start trackign db calls for feature environments (#10585)
This commit is contained in:
parent
96c74578b5
commit
829c2c5bc3
@ -57,14 +57,18 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
featureName,
|
featureName,
|
||||||
environment,
|
environment,
|
||||||
}: FeatureEnvironmentKey): Promise<void> {
|
}: FeatureEnvironmentKey): Promise<void> {
|
||||||
|
const stopTimer = this.timer('delete');
|
||||||
await this.db(T.featureEnvs)
|
await this.db(T.featureEnvs)
|
||||||
.where('feature_name', featureName)
|
.where('feature_name', featureName)
|
||||||
.andWhere('environment', environment)
|
.andWhere('environment', environment)
|
||||||
.del();
|
.del();
|
||||||
|
stopTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteAll(): Promise<void> {
|
async deleteAll(): Promise<void> {
|
||||||
|
const stopTimer = this.timer('deleteAll');
|
||||||
await this.db(T.featureEnvs).del();
|
await this.db(T.featureEnvs).del();
|
||||||
|
stopTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {}
|
destroy(): void {}
|
||||||
@ -73,10 +77,12 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
featureName,
|
featureName,
|
||||||
environment,
|
environment,
|
||||||
}: FeatureEnvironmentKey): Promise<boolean> {
|
}: FeatureEnvironmentKey): Promise<boolean> {
|
||||||
|
const stopTimer = this.timer('exists');
|
||||||
const result = await this.db.raw(
|
const result = await this.db.raw(
|
||||||
`SELECT EXISTS (SELECT 1 FROM ${T.featureEnvs} WHERE feature_name = ? AND environment = ?) AS present`,
|
`SELECT EXISTS (SELECT 1 FROM ${T.featureEnvs} WHERE feature_name = ? AND environment = ?) AS present`,
|
||||||
[featureName, environment],
|
[featureName, environment],
|
||||||
);
|
);
|
||||||
|
stopTimer();
|
||||||
const { present } = result.rows[0];
|
const { present } = result.rows[0];
|
||||||
return present;
|
return present;
|
||||||
}
|
}
|
||||||
@ -85,10 +91,12 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
featureName,
|
featureName,
|
||||||
environment,
|
environment,
|
||||||
}: FeatureEnvironmentKey): Promise<IFeatureEnvironment> {
|
}: FeatureEnvironmentKey): Promise<IFeatureEnvironment> {
|
||||||
|
const stopTimer = this.timer('get');
|
||||||
const md = await this.db(T.featureEnvs)
|
const md = await this.db(T.featureEnvs)
|
||||||
.where('feature_name', featureName)
|
.where('feature_name', featureName)
|
||||||
.andWhere('environment', environment)
|
.andWhere('environment', environment)
|
||||||
.first();
|
.first();
|
||||||
|
stopTimer();
|
||||||
if (md) {
|
if (md) {
|
||||||
return {
|
return {
|
||||||
enabled: md.enabled,
|
enabled: md.enabled,
|
||||||
@ -122,12 +130,15 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getAll(query?: Object): Promise<IFeatureEnvironment[]> {
|
async getAll(query?: Object): Promise<IFeatureEnvironment[]> {
|
||||||
|
const stopTimer = this.timer('getAll');
|
||||||
let rows = this.db(T.featureEnvs);
|
let rows = this.db(T.featureEnvs);
|
||||||
if (query) {
|
if (query) {
|
||||||
rows = rows.where(query);
|
rows = rows.where(query);
|
||||||
}
|
}
|
||||||
this.addOssFilterIfNeeded(rows);
|
this.addOssFilterIfNeeded(rows);
|
||||||
return (await rows).map((r) => ({
|
const result = await rows;
|
||||||
|
stopTimer();
|
||||||
|
return result.map((r) => ({
|
||||||
enabled: r.enabled,
|
enabled: r.enabled,
|
||||||
featureName: r.feature_name,
|
featureName: r.feature_name,
|
||||||
environment: r.environment,
|
environment: r.environment,
|
||||||
@ -139,6 +150,7 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
features: string[],
|
features: string[],
|
||||||
environment?: string,
|
environment?: string,
|
||||||
): Promise<IFeatureEnvironment[]> {
|
): Promise<IFeatureEnvironment[]> {
|
||||||
|
const stopTimer = this.timer('getAllByFeatures');
|
||||||
let rows = this.db(T.featureEnvs)
|
let rows = this.db(T.featureEnvs)
|
||||||
.whereIn('feature_name', features)
|
.whereIn('feature_name', features)
|
||||||
.orderBy('feature_name', 'asc');
|
.orderBy('feature_name', 'asc');
|
||||||
@ -146,7 +158,9 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
rows = rows.where({ environment });
|
rows = rows.where({ environment });
|
||||||
}
|
}
|
||||||
this.addOssFilterIfNeeded(rows);
|
this.addOssFilterIfNeeded(rows);
|
||||||
return (await rows).map((r) => ({
|
const result = await rows;
|
||||||
|
stopTimer();
|
||||||
|
return result.map((r) => ({
|
||||||
enabled: r.enabled,
|
enabled: r.enabled,
|
||||||
featureName: r.feature_name,
|
featureName: r.feature_name,
|
||||||
environment: r.environment,
|
environment: r.environment,
|
||||||
@ -159,6 +173,7 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
featureName: string,
|
featureName: string,
|
||||||
environment: string,
|
environment: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const stopTimer = this.timer('disableEnvironmentIfNoStrategies');
|
||||||
const result = await this.db.raw(
|
const result = await this.db.raw(
|
||||||
`SELECT EXISTS (SELECT 1 FROM ${T.featureStrategies} WHERE feature_name = ? AND environment = ?) AS enabled`,
|
`SELECT EXISTS (SELECT 1 FROM ${T.featureStrategies} WHERE feature_name = ? AND environment = ?) AS enabled`,
|
||||||
[featureName, environment],
|
[featureName, environment],
|
||||||
@ -169,6 +184,7 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
.update({ enabled: false })
|
.update({ enabled: false })
|
||||||
.where({ feature_name: featureName, environment });
|
.where({ feature_name: featureName, environment });
|
||||||
}
|
}
|
||||||
|
stopTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
async addEnvironmentToFeature(
|
async addEnvironmentToFeature(
|
||||||
@ -176,10 +192,12 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
environment: string,
|
environment: string,
|
||||||
enabled: boolean = false,
|
enabled: boolean = false,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const stopTimer = this.timer('addEnvironmentToFeature');
|
||||||
await this.db('feature_environments')
|
await this.db('feature_environments')
|
||||||
.insert({ feature_name: featureName, environment, enabled })
|
.insert({ feature_name: featureName, environment, enabled })
|
||||||
.onConflict(['environment', 'feature_name'])
|
.onConflict(['environment', 'feature_name'])
|
||||||
.merge(['enabled']);
|
.merge(['enabled']);
|
||||||
|
stopTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: move to project store.
|
// TODO: move to project store.
|
||||||
@ -187,6 +205,7 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
environment: string,
|
environment: string,
|
||||||
project: string,
|
project: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const stopTimer = this.timer('disconnectFeatures');
|
||||||
const featureSelector = this.db('features')
|
const featureSelector = this.db('features')
|
||||||
.where({ project })
|
.where({ project })
|
||||||
.select('name');
|
.select('name');
|
||||||
@ -198,16 +217,19 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
environment,
|
environment,
|
||||||
project_name: project,
|
project_name: project,
|
||||||
});
|
});
|
||||||
|
stopTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
async featureHasEnvironment(
|
async featureHasEnvironment(
|
||||||
environment: string,
|
environment: string,
|
||||||
featureName: string,
|
featureName: string,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
|
const stopTimer = this.timer('featureHasEnvironment');
|
||||||
const result = await this.db.raw(
|
const result = await this.db.raw(
|
||||||
`SELECT EXISTS (SELECT 1 FROM ${T.featureEnvs} WHERE feature_name = ? AND environment = ?) AS present`,
|
`SELECT EXISTS (SELECT 1 FROM ${T.featureEnvs} WHERE feature_name = ? AND environment = ?) AS present`,
|
||||||
[featureName, environment],
|
[featureName, environment],
|
||||||
);
|
);
|
||||||
|
stopTimer();
|
||||||
const { present } = result.rows[0];
|
const { present } = result.rows[0];
|
||||||
return present;
|
return present;
|
||||||
}
|
}
|
||||||
@ -215,10 +237,12 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
async getEnvironmentsForFeature(
|
async getEnvironmentsForFeature(
|
||||||
featureName: string,
|
featureName: string,
|
||||||
): Promise<IFeatureEnvironment[]> {
|
): Promise<IFeatureEnvironment[]> {
|
||||||
|
const stopTimer = this.timer('getEnvironmentsForFeature');
|
||||||
const envs = await this.db(T.featureEnvs).where(
|
const envs = await this.db(T.featureEnvs).where(
|
||||||
'feature_name',
|
'feature_name',
|
||||||
featureName,
|
featureName,
|
||||||
);
|
);
|
||||||
|
stopTimer();
|
||||||
if (envs) {
|
if (envs) {
|
||||||
return envs.map((r) => ({
|
return envs.map((r) => ({
|
||||||
featureName: r.feature_name,
|
featureName: r.feature_name,
|
||||||
@ -235,10 +259,12 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
environment: string,
|
environment: string,
|
||||||
featureName: string,
|
featureName: string,
|
||||||
): Promise<IFeatureEnvironment> {
|
): Promise<IFeatureEnvironment> {
|
||||||
|
const stopTimer = this.timer('getEnvironmentMetaData');
|
||||||
const md = await this.db(T.featureEnvs)
|
const md = await this.db(T.featureEnvs)
|
||||||
.where('feature_name', featureName)
|
.where('feature_name', featureName)
|
||||||
.andWhere('environment', environment)
|
.andWhere('environment', environment)
|
||||||
.first();
|
.first();
|
||||||
|
stopTimer();
|
||||||
if (md) {
|
if (md) {
|
||||||
return {
|
return {
|
||||||
enabled: md.enabled,
|
enabled: md.enabled,
|
||||||
@ -255,10 +281,12 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
featureName: string,
|
featureName: string,
|
||||||
environment: string,
|
environment: string,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
|
const stopTimer = this.timer('isEnvironmentEnabled');
|
||||||
const row = await this.db(T.featureEnvs)
|
const row = await this.db(T.featureEnvs)
|
||||||
.select('enabled')
|
.select('enabled')
|
||||||
.where({ feature_name: featureName, environment })
|
.where({ feature_name: featureName, environment })
|
||||||
.first();
|
.first();
|
||||||
|
stopTimer();
|
||||||
return row.enabled;
|
return row.enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,9 +294,11 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
featureName: string,
|
featureName: string,
|
||||||
environment: string,
|
environment: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const stopTimer = this.timer('removeEnvironmentForFeature');
|
||||||
await this.db(T.featureEnvs)
|
await this.db(T.featureEnvs)
|
||||||
.where({ feature_name: featureName, environment })
|
.where({ feature_name: featureName, environment })
|
||||||
.del();
|
.del();
|
||||||
|
stopTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
async setEnvironmentEnabledStatus(
|
async setEnvironmentEnabledStatus(
|
||||||
@ -276,11 +306,14 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
featureName: string,
|
featureName: string,
|
||||||
enabled: boolean,
|
enabled: boolean,
|
||||||
): Promise<number> {
|
): Promise<number> {
|
||||||
return this.db(T.featureEnvs).update({ enabled }).where({
|
const stopTimer = this.timer('setEnvironmentEnabledStatus');
|
||||||
|
const result = await this.db(T.featureEnvs).update({ enabled }).where({
|
||||||
environment,
|
environment,
|
||||||
feature_name: featureName,
|
feature_name: featureName,
|
||||||
enabled: !enabled,
|
enabled: !enabled,
|
||||||
});
|
});
|
||||||
|
stopTimer();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async connectProject(
|
async connectProject(
|
||||||
@ -288,6 +321,7 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
projectId: string,
|
projectId: string,
|
||||||
idempotent?: boolean, // default false to respect old behavior
|
idempotent?: boolean, // default false to respect old behavior
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const stopTimer = this.timer('connectProject');
|
||||||
const query = this.db('project_environments').insert({
|
const query = this.db('project_environments').insert({
|
||||||
environment_name: environment,
|
environment_name: environment,
|
||||||
project_id: projectId,
|
project_id: projectId,
|
||||||
@ -297,12 +331,14 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
} else {
|
} else {
|
||||||
await query;
|
await query;
|
||||||
}
|
}
|
||||||
|
stopTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
async connectFeatures(
|
async connectFeatures(
|
||||||
environment: string,
|
environment: string,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const stopTimer = this.timer('connectFeatures');
|
||||||
const featuresToEnable = await this.db('features')
|
const featuresToEnable = await this.db('features')
|
||||||
.select('name')
|
.select('name')
|
||||||
.where({
|
.where({
|
||||||
@ -319,15 +355,18 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
.onConflict(['environment', 'feature_name'])
|
.onConflict(['environment', 'feature_name'])
|
||||||
.ignore();
|
.ignore();
|
||||||
}
|
}
|
||||||
|
stopTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
async disconnectProject(
|
async disconnectProject(
|
||||||
environment: string,
|
environment: string,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const stopTimer = this.timer('disconnectProject');
|
||||||
await this.db('project_environments')
|
await this.db('project_environments')
|
||||||
.where({ environment_name: environment, project_id: projectId })
|
.where({ environment_name: environment, project_id: projectId })
|
||||||
.del();
|
.del();
|
||||||
|
stopTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
async connectFeatureToEnvironmentsForProject(
|
async connectFeatureToEnvironmentsForProject(
|
||||||
@ -335,6 +374,7 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
projectId: string,
|
projectId: string,
|
||||||
enabledIn: { [environment: string]: boolean } = {},
|
enabledIn: { [environment: string]: boolean } = {},
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const stopTimer = this.timer('connectFeatureToEnvironmentsForProject');
|
||||||
const environmentsToEnable = await this.db('project_environments')
|
const environmentsToEnable = await this.db('project_environments')
|
||||||
.select('environment_name')
|
.select('environment_name')
|
||||||
.where({ project_id: projectId });
|
.where({ project_id: projectId });
|
||||||
@ -350,6 +390,7 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
.ignore();
|
.ignore();
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
stopTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
async copyEnvironmentFeaturesByProjects(
|
async copyEnvironmentFeaturesByProjects(
|
||||||
@ -357,6 +398,7 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
destinationEnvironment: string,
|
destinationEnvironment: string,
|
||||||
projects: string[],
|
projects: string[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const stopTimer = this.timer('copyEnvironmentFeaturesByProjects');
|
||||||
await this.db.raw(
|
await this.db.raw(
|
||||||
`INSERT INTO ${T.featureEnvs} (environment, feature_name, enabled, variants)
|
`INSERT INTO ${T.featureEnvs} (environment, feature_name, enabled, variants)
|
||||||
SELECT DISTINCT ? AS environemnt, fe.feature_name, fe.enabled, fe.variants
|
SELECT DISTINCT ? AS environemnt, fe.feature_name, fe.enabled, fe.variants
|
||||||
@ -365,6 +407,7 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
WHERE fe.environment = ? AND f.project = ANY(?)`,
|
WHERE fe.environment = ? AND f.project = ANY(?)`,
|
||||||
[destinationEnvironment, sourceEnvironment, projects],
|
[destinationEnvironment, sourceEnvironment, projects],
|
||||||
);
|
);
|
||||||
|
stopTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
async addVariantsToFeatureEnvironment(
|
async addVariantsToFeatureEnvironment(
|
||||||
@ -372,11 +415,14 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
environment: string,
|
environment: string,
|
||||||
variants: IVariant[],
|
variants: IVariant[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
return this.setVariantsToFeatureEnvironments(
|
const stopTimer = this.timer('addVariantsToFeatureEnvironment');
|
||||||
|
const result = await this.setVariantsToFeatureEnvironments(
|
||||||
featureName,
|
featureName,
|
||||||
[environment],
|
[environment],
|
||||||
variants,
|
variants,
|
||||||
);
|
);
|
||||||
|
stopTimer();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async setVariantsToFeatureEnvironments(
|
async setVariantsToFeatureEnvironments(
|
||||||
@ -384,6 +430,7 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
environments: string[],
|
environments: string[],
|
||||||
variants: IVariant[],
|
variants: IVariant[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const stopTimer = this.timer('setVariantsToFeatureEnvironments');
|
||||||
const v = variants || [];
|
const v = variants || [];
|
||||||
v.sort((a, b) => a.name.localeCompare(b.name));
|
v.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
const variantsString = JSON.stringify(v);
|
const variantsString = JSON.stringify(v);
|
||||||
@ -397,11 +444,13 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
.insert(records)
|
.insert(records)
|
||||||
.onConflict(['feature_name', 'environment'])
|
.onConflict(['feature_name', 'environment'])
|
||||||
.merge(['variants']);
|
.merge(['variants']);
|
||||||
|
stopTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
async addFeatureEnvironment(
|
async addFeatureEnvironment(
|
||||||
featureEnvironment: IFeatureEnvironment,
|
featureEnvironment: IFeatureEnvironment,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const stopTimer = this.timer('addFeatureEnvironment');
|
||||||
const v = featureEnvironment.variants || [];
|
const v = featureEnvironment.variants || [];
|
||||||
v.sort((a, b) => a.name.localeCompare(b.name));
|
v.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
await this.db(T.featureEnvs)
|
await this.db(T.featureEnvs)
|
||||||
@ -413,12 +462,14 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
})
|
})
|
||||||
.onConflict(['feature_name', 'environment'])
|
.onConflict(['feature_name', 'environment'])
|
||||||
.merge(['variants', 'enabled']);
|
.merge(['variants', 'enabled']);
|
||||||
|
stopTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
async cloneStrategies(
|
async cloneStrategies(
|
||||||
sourceEnvironment: string,
|
sourceEnvironment: string,
|
||||||
destinationEnvironment: string,
|
destinationEnvironment: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const stopTimer = this.timer('cloneStrategies');
|
||||||
const sourceFeatureStrategies = await this.db(
|
const sourceFeatureStrategies = await this.db(
|
||||||
'feature_strategies',
|
'feature_strategies',
|
||||||
).where({
|
).where({
|
||||||
@ -442,6 +493,7 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (clonedStrategyRows.length === 0) {
|
if (clonedStrategyRows.length === 0) {
|
||||||
|
stopTimer();
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
await this.db('feature_strategies').insert(clonedStrategyRows);
|
await this.db('feature_strategies').insert(clonedStrategyRows);
|
||||||
@ -481,13 +533,16 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
|||||||
clonedSegmentIdRows,
|
clonedSegmentIdRows,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
stopTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
async variantExists(featureName: string): Promise<boolean> {
|
async variantExists(featureName: string): Promise<boolean> {
|
||||||
|
const stopTimer = this.timer('variantExists');
|
||||||
const result = await this.db.raw(
|
const result = await this.db.raw(
|
||||||
`SELECT EXISTS (SELECT 1 FROM ${T.featureEnvs} WHERE feature_name = ? AND variants <> '[]'::jsonb) AS present`,
|
`SELECT EXISTS (SELECT 1 FROM ${T.featureEnvs} WHERE feature_name = ? AND variants <> '[]'::jsonb) AS present`,
|
||||||
[featureName],
|
[featureName],
|
||||||
);
|
);
|
||||||
|
stopTimer();
|
||||||
const { present } = result.rows[0];
|
const { present } = result.rows[0];
|
||||||
return present;
|
return present;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user