mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-26 13:48:33 +02:00
feat: implement createdByUserId for all features (#5725)
## About the changes Implements setting values on the created_by_user_id column on the features table in the db
This commit is contained in:
parent
9d8487ad6e
commit
9ac1070f43
@ -41,6 +41,7 @@ test('returns 0 if no custom strategies are in use', async () => {
|
||||
|
||||
featureToggleStore.create('default', {
|
||||
name: 'test-toggle-2',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
strategyStore.createStrategy({
|
||||
@ -68,6 +69,7 @@ test('counts custom strategies in use', async () => {
|
||||
|
||||
await featureToggleStore.create('default', {
|
||||
name: 'test-toggle',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await strategyStore.createStrategy({
|
||||
@ -112,6 +114,7 @@ test('increment sort order on each new insert', async () => {
|
||||
|
||||
await featureToggleStore.create('default', {
|
||||
name: 'test-toggle-increment',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
const { id: firstId } =
|
||||
|
@ -28,6 +28,7 @@ beforeAll(async () => {
|
||||
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: FLAG_NAME,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -661,6 +661,7 @@ test('should search features by project with operators', async () => {
|
||||
|
||||
await db.stores.featureToggleStore.create('project_b', {
|
||||
name: 'my_feature_b',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await db.stores.projectStore.create({
|
||||
@ -671,6 +672,7 @@ test('should search features by project with operators', async () => {
|
||||
|
||||
await db.stores.featureToggleStore.create('project_c', {
|
||||
name: 'my_feature_c',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
const { body } = await searchFeatures({
|
||||
|
@ -12,7 +12,10 @@ import {
|
||||
IVariant,
|
||||
} from 'lib/types/model';
|
||||
import { LastSeenInput } from '../../../services/client-metrics/last-seen/last-seen-service';
|
||||
import { EnvironmentFeatureNames } from '../feature-toggle-store';
|
||||
import {
|
||||
EnvironmentFeatureNames,
|
||||
FeatureToggleInsert,
|
||||
} from '../feature-toggle-store';
|
||||
import { FeatureConfigurationClient } from '../types/feature-toggle-strategies-store-type';
|
||||
import { IFeatureProjectUserParams } from '../feature-toggle-controller';
|
||||
|
||||
@ -104,7 +107,10 @@ export default class FakeFeatureToggleStore implements IFeatureToggleStore {
|
||||
};
|
||||
}
|
||||
|
||||
async create(project: string, data: FeatureToggle): Promise<FeatureToggle> {
|
||||
async create(
|
||||
project: string,
|
||||
data: FeatureToggleInsert,
|
||||
): Promise<FeatureToggle> {
|
||||
const inserted: FeatureToggle = { ...data, project };
|
||||
this.features.push(inserted);
|
||||
return inserted;
|
||||
|
@ -1150,9 +1150,14 @@ class FeatureToggleService {
|
||||
if (exists) {
|
||||
let featureData;
|
||||
if (isValidated) {
|
||||
featureData = value;
|
||||
featureData = { createdByUserId, ...value };
|
||||
} else {
|
||||
featureData = await featureMetadataSchema.validateAsync(value);
|
||||
const validated =
|
||||
await featureMetadataSchema.validateAsync(value);
|
||||
featureData = {
|
||||
createdByUserId,
|
||||
...validated,
|
||||
};
|
||||
}
|
||||
const featureName = featureData.name;
|
||||
const createdToggle = await this.featureToggleStore.create(
|
||||
|
@ -49,6 +49,12 @@ export interface FeaturesTable {
|
||||
impression_data: boolean;
|
||||
archived?: boolean;
|
||||
archived_at?: Date;
|
||||
created_by_user_id?: number;
|
||||
}
|
||||
|
||||
export interface FeatureToggleInsert
|
||||
extends Omit<FeatureToggleDTO, 'createdByUserId'> {
|
||||
createdByUserId: number;
|
||||
}
|
||||
|
||||
interface VariantDTO {
|
||||
@ -457,7 +463,7 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
|
||||
return sortedVariants;
|
||||
}
|
||||
|
||||
dtoToRow(project: string, data: FeatureToggleDTO): FeaturesTable {
|
||||
insertToRow(project: string, data: FeatureToggleInsert): FeaturesTable {
|
||||
const row = {
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
@ -467,20 +473,39 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
|
||||
stale: data.stale,
|
||||
created_at: data.createdAt,
|
||||
impression_data: data.impressionData,
|
||||
created_by_user_id: data.createdByUserId,
|
||||
};
|
||||
if (!row.created_at) {
|
||||
delete row.created_at;
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
dtoToUpdateRow(
|
||||
project: string,
|
||||
data: FeatureToggleDTO,
|
||||
): Omit<FeaturesTable, 'created_by_user_id'> {
|
||||
const row = {
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
type: data.type,
|
||||
project,
|
||||
archived_at: data.archived ? new Date() : null,
|
||||
stale: data.stale,
|
||||
impression_data: data.impressionData,
|
||||
};
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
async create(
|
||||
project: string,
|
||||
data: FeatureToggleDTO,
|
||||
data: FeatureToggleInsert,
|
||||
): Promise<FeatureToggle> {
|
||||
try {
|
||||
const row = await this.db(TABLE)
|
||||
.insert(this.dtoToRow(project, data))
|
||||
.insert(this.insertToRow(project, data))
|
||||
.returning(FEATURE_COLUMNS);
|
||||
|
||||
return this.rowToFeature(row[0]);
|
||||
@ -504,7 +529,7 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
|
||||
): Promise<FeatureToggle> {
|
||||
const row = await this.db(TABLE)
|
||||
.where({ name: data.name })
|
||||
.update(this.dtoToRow(project, data))
|
||||
.update(this.dtoToUpdateRow(project, data))
|
||||
.returning(FEATURE_COLUMNS);
|
||||
|
||||
return this.rowToFeature(row[0]);
|
||||
|
@ -100,14 +100,17 @@ test('Should get archived toggles via project', async () => {
|
||||
await db.stores.featureToggleStore.create('proj-1', {
|
||||
name: 'feat-proj-1',
|
||||
archived: true,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await db.stores.featureToggleStore.create('proj-2', {
|
||||
name: 'feat-proj-2',
|
||||
archived: true,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await db.stores.featureToggleStore.create('proj-2', {
|
||||
name: 'feat-proj-2-2',
|
||||
archived: true,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await app.request
|
||||
@ -151,6 +154,7 @@ test('Should disable all environments when reviving a toggle', async () => {
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: 'feat-proj-1',
|
||||
archived: true,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await db.stores.environmentStore.create({
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
IFeatureToggleStore,
|
||||
IProjectStore,
|
||||
} from '../../../types';
|
||||
import { FeatureToggleInsert } from '../feature-toggle-store';
|
||||
|
||||
let stores;
|
||||
let db;
|
||||
@ -49,10 +50,11 @@ describe('potentially_stale marking', () => {
|
||||
};
|
||||
|
||||
test('it returns an empty list if no toggles were updated', async () => {
|
||||
const features: FeatureToggleDTO[] = [
|
||||
const features: FeatureToggleInsert[] = [
|
||||
{
|
||||
name: 'feature1',
|
||||
type: 'release',
|
||||
createdByUserId: 9999,
|
||||
},
|
||||
];
|
||||
await Promise.all(
|
||||
@ -68,14 +70,16 @@ describe('potentially_stale marking', () => {
|
||||
});
|
||||
|
||||
test('it returns only updated toggles', async () => {
|
||||
const features: FeatureToggleDTO[] = [
|
||||
const features: FeatureToggleInsert[] = [
|
||||
{
|
||||
name: 'feature1',
|
||||
type: 'release',
|
||||
createdByUserId: 9999,
|
||||
},
|
||||
{
|
||||
name: 'feature2',
|
||||
type: 'kill-switch',
|
||||
createdByUserId: 9999,
|
||||
},
|
||||
];
|
||||
await Promise.all(
|
||||
@ -102,13 +106,13 @@ describe('potentially_stale marking', () => {
|
||||
])(
|
||||
'it marks toggles based on their type (days elapsed: %s)',
|
||||
async (daysElapsed, expectedMarkedFeatures) => {
|
||||
const features: FeatureToggleDTO[] = [
|
||||
const features: FeatureToggleInsert[] = [
|
||||
'release',
|
||||
'experiment',
|
||||
'operational',
|
||||
'kill-switch',
|
||||
'permission',
|
||||
].map((type) => ({ name: type, type }));
|
||||
].map((type) => ({ name: type, type, createdByUserId: 9999 }));
|
||||
await Promise.all(
|
||||
features.map((feature) =>
|
||||
featureToggleStore.create('default', feature),
|
||||
@ -143,11 +147,12 @@ describe('potentially_stale marking', () => {
|
||||
},
|
||||
);
|
||||
test('it does not mark toggles already flagged as stale', async () => {
|
||||
const features: FeatureToggleDTO[] = [
|
||||
const features: FeatureToggleInsert[] = [
|
||||
{
|
||||
name: 'feature1',
|
||||
type: 'release',
|
||||
stale: true,
|
||||
createdByUserId: 9999,
|
||||
},
|
||||
];
|
||||
await Promise.all(
|
||||
@ -163,10 +168,11 @@ describe('potentially_stale marking', () => {
|
||||
});
|
||||
|
||||
test('it does not return toggles previously marked as potentially_stale', async () => {
|
||||
const features: FeatureToggleDTO[] = [
|
||||
const features: FeatureToggleInsert[] = [
|
||||
{
|
||||
name: 'feature1',
|
||||
type: 'release',
|
||||
createdByUserId: 9999,
|
||||
},
|
||||
];
|
||||
await Promise.all(
|
||||
@ -197,10 +203,11 @@ describe('potentially_stale marking', () => {
|
||||
|
||||
describe('changing feature types', () => {
|
||||
test("if a potentially stale feature changes to a type that shouldn't be stale, it's 'potentially_stale' marker is removed.", async () => {
|
||||
const features: FeatureToggleDTO[] = [
|
||||
const features: FeatureToggleInsert[] = [
|
||||
{
|
||||
name: 'feature1',
|
||||
type: 'release',
|
||||
createdByUserId: 9999,
|
||||
},
|
||||
];
|
||||
await Promise.all(
|
||||
@ -247,10 +254,11 @@ describe('potentially_stale marking', () => {
|
||||
});
|
||||
|
||||
test('if a fresh feature changes to a type that should be stale, it gets marked as potentially stale', async () => {
|
||||
const features: FeatureToggleDTO[] = [
|
||||
const features: FeatureToggleInsert[] = [
|
||||
{
|
||||
name: 'feature1',
|
||||
type: 'kill-switch',
|
||||
createdByUserId: 9999,
|
||||
},
|
||||
];
|
||||
await Promise.all(
|
||||
@ -280,11 +288,12 @@ describe('potentially_stale marking', () => {
|
||||
});
|
||||
|
||||
test('if a stale feature changes to a type that should be stale, it does not get marked as potentially stale', async () => {
|
||||
const features: FeatureToggleDTO[] = [
|
||||
const features: FeatureToggleInsert[] = [
|
||||
{
|
||||
name: 'feature1',
|
||||
type: 'kill-switch',
|
||||
stale: true,
|
||||
createdByUserId: 9999,
|
||||
},
|
||||
];
|
||||
await Promise.all(
|
||||
@ -314,9 +323,15 @@ describe('potentially_stale marking', () => {
|
||||
name: 'MyProject',
|
||||
description: 'MyProject',
|
||||
});
|
||||
await featureToggleStore.create('default', { name: 'featureA' });
|
||||
await featureToggleStore.create('default', {
|
||||
name: 'featureA',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await featureToggleStore.create('MyProject', { name: 'featureB' });
|
||||
await featureToggleStore.create('MyProject', {
|
||||
name: 'featureB',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
const playgroundFeatures =
|
||||
await featureToggleStore.getPlaygroundFeatures({
|
||||
|
@ -15,7 +15,10 @@ beforeAll(async () => {
|
||||
stores = db.stores;
|
||||
featureStrategiesStore = stores.featureStrategiesStore;
|
||||
featureToggleStore = stores.featureToggleStore;
|
||||
await featureToggleStore.create('default', { name: featureName });
|
||||
await featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
@ -74,8 +77,14 @@ test('Can successfully update project for all strategies belonging to feature',
|
||||
test('Can query for features with tags', async () => {
|
||||
const tag = { type: 'simple', value: 'hello-tags' };
|
||||
await stores.tagStore.createTag(tag);
|
||||
await featureToggleStore.create('default', { name: 'to-be-tagged' });
|
||||
await featureToggleStore.create('default', { name: 'not-tagged' });
|
||||
await featureToggleStore.create('default', {
|
||||
name: 'to-be-tagged',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await featureToggleStore.create('default', {
|
||||
name: 'not-tagged',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await stores.featureTagStore.tagFeature('to-be-tagged', tag);
|
||||
const features = await featureStrategiesStore.getFeatureOverview({
|
||||
projectId: 'default',
|
||||
@ -87,9 +96,11 @@ test('Can query for features with tags', async () => {
|
||||
test('Can query for features with namePrefix', async () => {
|
||||
await featureToggleStore.create('default', {
|
||||
name: 'nameprefix-to-be-hit',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await featureToggleStore.create('default', {
|
||||
name: 'nameprefix-not-be-hit',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
const features = await featureStrategiesStore.getFeatureOverview({
|
||||
projectId: 'default',
|
||||
@ -103,12 +114,15 @@ test('Can query for features with namePrefix and tags', async () => {
|
||||
await stores.tagStore.createTag(tag);
|
||||
await featureToggleStore.create('default', {
|
||||
name: 'to-be-tagged-nameprefix-and-tags',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await featureToggleStore.create('default', {
|
||||
name: 'not-tagged-nameprefix-and-tags',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await featureToggleStore.create('default', {
|
||||
name: 'tagged-but-not-hit-nameprefix-and-tags',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await stores.featureTagStore.tagFeature(
|
||||
'to-be-tagged-nameprefix-and-tags',
|
||||
|
@ -40,7 +40,10 @@ test('Should not be possible to update feature toggle without permission', async
|
||||
const url = '/api/admin/projects/default/features';
|
||||
const name = 'auth.toggle.update';
|
||||
|
||||
await db.stores.featureToggleStore.create('default', { name });
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await app.services.userService.createUser({
|
||||
email,
|
||||
@ -62,7 +65,10 @@ test('Should be possible to update feature toggle with permission', async () =>
|
||||
const url = '/api/admin/projects/default/features';
|
||||
const name = 'auth.toggle.update2';
|
||||
|
||||
await db.stores.featureToggleStore.create('default', { name });
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await app.services.userService.createUser({
|
||||
email,
|
||||
|
@ -2574,6 +2574,7 @@ test('should reject invalid constraint values for multi-valued constraints', asy
|
||||
const toggle = await db.stores.featureToggleStore.create(project.id, {
|
||||
name: uuidv4(),
|
||||
impressionData: true,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
const mockStrategy = (values: string[]) => ({
|
||||
@ -2621,6 +2622,7 @@ test('should add default constraint values for single-valued constraints', async
|
||||
const toggle = await db.stores.featureToggleStore.create(project.id, {
|
||||
name: uuidv4(),
|
||||
impressionData: true,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
const constraintValue = {
|
||||
@ -2680,6 +2682,7 @@ test('should allow long parameter values', async () => {
|
||||
|
||||
const toggle = await db.stores.featureToggleStore.create(project.id, {
|
||||
name: uuidv4(),
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
const strategy = {
|
||||
@ -2988,9 +2991,11 @@ test('Can filter based on tags', async () => {
|
||||
await db.stores.tagStore.createTag(tag);
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: 'to-be-tagged',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: 'not-tagged',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await db.stores.featureTagStore.tagFeature('to-be-tagged', tag, TESTUSERID);
|
||||
await app.request
|
||||
@ -3003,9 +3008,11 @@ test('Can filter based on tags', async () => {
|
||||
test('Can query for features with namePrefix', async () => {
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: 'nameprefix-to-be-hit',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: 'nameprefix-not-be-hit',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await app.request
|
||||
.get('/api/admin/projects/default/features?namePrefix=nameprefix-to')
|
||||
@ -3019,12 +3026,15 @@ test('Can query for features with namePrefix and tags', async () => {
|
||||
await db.stores.tagStore.createTag(tag);
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: 'to-be-tagged-nameprefix-and-tags',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: 'not-tagged-nameprefix-and-tags',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: 'tagged-but-not-hit-nameprefix-and-tags',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await db.stores.featureTagStore.tagFeature(
|
||||
'to-be-tagged-nameprefix-and-tags',
|
||||
@ -3054,18 +3064,21 @@ test('Can query for two tags at the same time. Tags are ORed together', async ()
|
||||
'default',
|
||||
{
|
||||
name: 'tagged-with-first-tag',
|
||||
createdByUserId: 9999,
|
||||
},
|
||||
);
|
||||
const taggedWithSecond = await db.stores.featureToggleStore.create(
|
||||
'default',
|
||||
{
|
||||
name: 'tagged-with-second-tag',
|
||||
createdByUserId: 9999,
|
||||
},
|
||||
);
|
||||
const taggedWithBoth = await db.stores.featureToggleStore.create(
|
||||
'default',
|
||||
{
|
||||
name: 'tagged-with-both-tags',
|
||||
createdByUserId: 9999,
|
||||
},
|
||||
);
|
||||
await db.stores.featureTagStore.tagFeature(
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
IFeatureTypeCount,
|
||||
IVariant,
|
||||
} from '../../../types/model';
|
||||
import { FeatureToggleInsert } from '../feature-toggle-store';
|
||||
import { Store } from '../../../types/stores/store';
|
||||
import { LastSeenInput } from '../../../services/client-metrics/last-seen/last-seen-service';
|
||||
import { FeatureConfigurationClient } from './feature-toggle-strategies-store-type';
|
||||
@ -24,7 +25,7 @@ export interface IFeatureToggleStore extends Store<FeatureToggle, string> {
|
||||
|
||||
getProjectId(name: string): Promise<string | undefined>;
|
||||
|
||||
create(project: string, data: FeatureToggleDTO): Promise<FeatureToggle>;
|
||||
create(project: string, data: FeatureToggleInsert): Promise<FeatureToggle>;
|
||||
|
||||
update(project: string, data: FeatureToggleDTO): Promise<FeatureToggle>;
|
||||
|
||||
|
@ -52,6 +52,7 @@ test('Can connect environment to project', async () => {
|
||||
type: 'release',
|
||||
description: '',
|
||||
stale: false,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await service.addEnvironmentToProject(
|
||||
'test-connection',
|
||||
@ -93,6 +94,7 @@ test('Can remove environment from project', async () => {
|
||||
});
|
||||
await stores.featureToggleStore.create('default', {
|
||||
name: 'removal-test',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await service.removeEnvironmentFromProject(
|
||||
'test-connection',
|
||||
@ -285,6 +287,7 @@ test('When given overrides should remap projects to override environments', asyn
|
||||
type: 'release',
|
||||
description: '',
|
||||
stale: false,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await service.addEnvironmentToProject(
|
||||
|
@ -188,6 +188,7 @@ test('schema allow yes=<string nbr>', () => {
|
||||
test('should return a 400 when required fields are missing', async () => {
|
||||
stores.featureToggleStore.create('default', {
|
||||
name: 'toggleLastSeen',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await request
|
||||
.post('/api/client/metrics')
|
||||
@ -209,6 +210,7 @@ test('should return a 400 when required fields are missing', async () => {
|
||||
test('should return a 200 if required fields are there', async () => {
|
||||
stores.featureToggleStore.create('default', {
|
||||
name: 'theOtherToggleLastSeen',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await request
|
||||
.post('/api/client/metrics')
|
||||
|
@ -104,6 +104,7 @@ export const featureMetadataSchema = joi
|
||||
.unique((a, b) => a.name === b.name)
|
||||
.optional()
|
||||
.items(variantsSchema),
|
||||
createdByUserId: joi.number(),
|
||||
})
|
||||
.options({ allowUnknown: false, stripUnknown: true, abortEarly: false });
|
||||
|
||||
|
@ -43,6 +43,7 @@ async function setupV3VariantsCompatibilityScenario(
|
||||
const stores = createStores();
|
||||
await stores.featureToggleStore.create('some-project', {
|
||||
name: 'Feature-with-variants',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
let sortOrder = 1;
|
||||
envs.forEach(async (env) => {
|
||||
@ -112,6 +113,7 @@ test('should not import an existing feature', async () => {
|
||||
name: 'new-feature',
|
||||
enabled: true,
|
||||
strategies: [{ name: 'default' }],
|
||||
createdByUserId: 9999,
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -137,6 +139,7 @@ test('should not keep existing feature if drop-before-import', async () => {
|
||||
name: 'new-feature',
|
||||
enabled: true,
|
||||
strategies: [{ name: 'default' }],
|
||||
createdByUserId: 9999,
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -289,6 +292,7 @@ test('should export featureToggles', async () => {
|
||||
|
||||
await stores.featureToggleStore.create('default', {
|
||||
name: 'a-feature',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
const data = await stateService.export({ includeFeatureToggles: true });
|
||||
@ -303,6 +307,7 @@ test('archived feature toggles should not be included', async () => {
|
||||
await stores.featureToggleStore.create('default', {
|
||||
name: 'a-feature',
|
||||
archived: true,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
const data = await stateService.export({ includeFeatureToggles: true });
|
||||
|
||||
@ -315,6 +320,7 @@ test('featureStrategy connected to an archived feature toggle should not be incl
|
||||
await stores.featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
archived: true,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await stores.featureStrategiesStore.createStrategyFeatureEnv({
|
||||
@ -334,6 +340,7 @@ test('featureStrategy connected to a feature should be included', async () => {
|
||||
const featureName = 'fstrat-feature';
|
||||
await stores.featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await stores.featureStrategiesStore.createStrategyFeatureEnv({
|
||||
@ -662,6 +669,7 @@ test('exporting to new format works', async () => {
|
||||
});
|
||||
await stores.featureToggleStore.create('fancy', {
|
||||
name: 'Some-feature',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await stores.strategyStore.createStrategy({
|
||||
name: 'format',
|
||||
@ -724,6 +732,7 @@ test('featureStrategies can keep existing', async () => {
|
||||
});
|
||||
await stores.featureToggleStore.create('fancy', {
|
||||
name: 'Some-feature',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await stores.strategyStore.createStrategy({
|
||||
name: 'format',
|
||||
@ -779,6 +788,7 @@ test('featureStrategies should not keep existing if dropBeforeImport', async ()
|
||||
});
|
||||
await stores.featureToggleStore.create('fancy', {
|
||||
name: 'Some-feature',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await stores.strategyStore.createStrategy({
|
||||
name: 'format',
|
||||
|
@ -396,7 +396,10 @@ export default class StateService {
|
||||
.filter(filterExisting(keepExisting, oldToggles))
|
||||
.filter(filterEqual(oldToggles))
|
||||
.map(async (feature) => {
|
||||
await this.toggleStore.create(feature.project, feature);
|
||||
await this.toggleStore.create(feature.project, {
|
||||
createdByUserId: userId,
|
||||
...feature,
|
||||
});
|
||||
await this.featureEnvironmentStore.connectFeatureToEnvironmentsForProject(
|
||||
feature.name,
|
||||
feature.project,
|
||||
|
@ -181,7 +181,10 @@ test('counts toggles', async () => {
|
||||
await stores.settingStore.insert('unleash.enterprise.auth.oidc', {
|
||||
enabled: true,
|
||||
});
|
||||
await stores.featureToggleStore.create('project', { name: uuidv4() });
|
||||
await stores.featureToggleStore.create('project', {
|
||||
name: uuidv4(),
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await stores.strategyStore.createStrategy({
|
||||
name: uuidv4(),
|
||||
editable: true,
|
||||
@ -237,7 +240,10 @@ test('counts custom strategies', async () => {
|
||||
await stores.settingStore.insert('unleash.enterprise.auth.oidc', {
|
||||
enabled: true,
|
||||
});
|
||||
await stores.featureToggleStore.create('project', { name: toggleName });
|
||||
await stores.featureToggleStore.create('project', {
|
||||
name: toggleName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await stores.strategyStore.createStrategy({
|
||||
name: strategyName,
|
||||
editable: true,
|
||||
|
@ -59,6 +59,7 @@ export interface FeatureToggleDTO {
|
||||
createdAt?: Date;
|
||||
impressionData?: boolean;
|
||||
variants?: IVariant[];
|
||||
createdByUserId?: number;
|
||||
}
|
||||
|
||||
export interface FeatureToggle extends FeatureToggleDTO {
|
||||
|
@ -20,7 +20,10 @@ afterAll(async () => {
|
||||
});
|
||||
|
||||
test('should return instance statistics', async () => {
|
||||
stores.featureToggleStore.create('default', { name: 'TestStats1' });
|
||||
stores.featureToggleStore.create('default', {
|
||||
name: 'TestStats1',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
return app.request
|
||||
.get('/api/admin/instance-admin/statistics')
|
||||
@ -62,8 +65,14 @@ test('should return signed instance statistics', async () => {
|
||||
});
|
||||
|
||||
test('should return instance statistics as CVS', async () => {
|
||||
stores.featureToggleStore.create('default', { name: 'TestStats2' });
|
||||
stores.featureToggleStore.create('default', { name: 'TestStats3' });
|
||||
stores.featureToggleStore.create('default', {
|
||||
name: 'TestStats2',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
stores.featureToggleStore.create('default', {
|
||||
name: 'TestStats3',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
const res = await app.request
|
||||
.get('/api/admin/instance-admin/statistics/csv')
|
||||
|
@ -37,7 +37,10 @@ afterAll(async () => {
|
||||
test('Can get variants for a feature', async () => {
|
||||
const featureName = 'feature-variants';
|
||||
const variantName = 'fancy-variant';
|
||||
await db.stores.featureToggleStore.create('default', { name: featureName });
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
||||
featureName,
|
||||
'default',
|
||||
@ -110,6 +113,7 @@ test('Can patch variants for a feature and get a response of new variant', async
|
||||
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
||||
featureName,
|
||||
@ -151,6 +155,7 @@ test('Can patch variants for a feature patches all environments independently',
|
||||
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
||||
featureName,
|
||||
@ -237,6 +242,7 @@ test('Can push variants to multiple environments', async () => {
|
||||
});
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
||||
featureName,
|
||||
@ -339,6 +345,7 @@ test('Can add variant for a feature', async () => {
|
||||
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
||||
@ -394,6 +401,7 @@ test('Can remove variant for a feature', async () => {
|
||||
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
||||
@ -438,6 +446,7 @@ test('PUT overwrites current variant on feature', async () => {
|
||||
];
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
||||
featureName,
|
||||
@ -492,6 +501,7 @@ test('PUTing an invalid variant throws 400 exception', async () => {
|
||||
const featureName = 'variants-validation-feature';
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
const invalidJson = [
|
||||
@ -518,6 +528,7 @@ test('Invalid variant in PATCH also throws 400 exception', async () => {
|
||||
const featureName = 'patch-validation-feature';
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
||||
featureName,
|
||||
@ -553,6 +564,7 @@ test('PATCHING with all variable weightTypes forces weights to sum to no less th
|
||||
const featureName = 'variants-validation-with-all-variable-weights';
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
||||
@ -647,6 +659,7 @@ test('PATCHING with no variable variants fails with 400', async () => {
|
||||
const featureName = 'variants-validation-with-no-variable-weights';
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
||||
featureName,
|
||||
@ -681,6 +694,7 @@ test('Patching with a fixed variant and variable variants splits remaining weigh
|
||||
const featureName = 'variants-fixed-and-variable';
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
||||
@ -778,6 +792,7 @@ test('Multiple fixed variants gets added together to decide how much weight vari
|
||||
const featureName = 'variants-multiple-fixed-and-variable';
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
||||
@ -829,6 +844,7 @@ test('If sum of fixed variant weight exceed 1000 fails with 400', async () => {
|
||||
const featureName = 'variants-fixed-weight-over-1000';
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
||||
@ -876,6 +892,7 @@ test('If sum of fixed variant weight equals 1000 variable variants gets weight 0
|
||||
const featureName = 'variants-fixed-weight-equals-1000-no-variable-weight';
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
||||
@ -944,6 +961,7 @@ test('PATCH endpoint validates uniqueness of variant names', async () => {
|
||||
];
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
||||
@ -989,6 +1007,7 @@ test('PUT endpoint validates uniqueness of variant names', async () => {
|
||||
const featureName = 'variants-put-uniqueness-names';
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
||||
@ -1025,6 +1044,7 @@ test('Variants should be sorted by their name when PUT', async () => {
|
||||
const featureName = 'variants-sort-by-name';
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
||||
@ -1074,6 +1094,7 @@ test('Variants should be sorted by name when PATCHed as well', async () => {
|
||||
const featureName = 'variants-patch-sort-by-name';
|
||||
await db.stores.featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
||||
|
@ -118,6 +118,7 @@ export const seedDatabaseForPlaygroundTest = async (
|
||||
...feature,
|
||||
createdAt: undefined,
|
||||
variants: null,
|
||||
createdByUserId: 9999,
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -52,11 +52,13 @@ test('Project with no stale toggles should have 100% health rating', async () =>
|
||||
name: 'health-rating-not-stale',
|
||||
description: 'new',
|
||||
stale: false,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await stores.featureToggleStore.create('health-rating', {
|
||||
name: 'health-rating-not-stale-2',
|
||||
description: 'new too',
|
||||
stale: false,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
const rating =
|
||||
await projectHealthService.calculateHealthRating(savedProject);
|
||||
@ -74,21 +76,25 @@ test('Project with two stale toggles and two non stale should have 50% health ra
|
||||
name: 'health-rating-2-not-stale',
|
||||
description: 'new',
|
||||
stale: false,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await stores.featureToggleStore.create('health-rating-2', {
|
||||
name: 'health-rating-2-not-stale-2',
|
||||
description: 'new too',
|
||||
stale: false,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await stores.featureToggleStore.create('health-rating-2', {
|
||||
name: 'health-rating-2-stale-1',
|
||||
description: 'stale',
|
||||
stale: true,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await stores.featureToggleStore.create('health-rating-2', {
|
||||
name: 'health-rating-2-stale-2',
|
||||
description: 'stale too',
|
||||
stale: true,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
const rating =
|
||||
await projectHealthService.calculateHealthRating(savedProject);
|
||||
@ -106,6 +112,7 @@ test('Project with one non-stale, one potentially stale and one stale should hav
|
||||
name: 'health-rating-3-not-stale',
|
||||
description: 'new',
|
||||
stale: false,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await stores.featureToggleStore.create('health-rating-3', {
|
||||
name: 'health-rating-3-potentially-stale',
|
||||
@ -113,11 +120,13 @@ test('Project with one non-stale, one potentially stale and one stale should hav
|
||||
type: 'release',
|
||||
stale: false,
|
||||
createdAt: new Date(Date.UTC(2020, 1, 1)),
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await stores.featureToggleStore.create('health-rating-3', {
|
||||
name: 'health-rating-3-stale',
|
||||
description: 'stale',
|
||||
stale: true,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
const rating =
|
||||
await projectHealthService.calculateHealthRating(savedProject);
|
||||
|
@ -177,6 +177,7 @@ test('should not be able to delete project with toggles', async () => {
|
||||
await projectService.createProject(project, user);
|
||||
await stores.featureToggleStore.create(project.id, {
|
||||
name: 'test-project-delete',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
try {
|
||||
@ -1461,9 +1462,11 @@ test('should only count active feature toggles for project', async () => {
|
||||
|
||||
await stores.featureToggleStore.create(project.id, {
|
||||
name: 'only-active-t1',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await stores.featureToggleStore.create(project.id, {
|
||||
name: 'only-active-t2',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await featureToggleService.archiveToggle('only-active-t2', user);
|
||||
@ -1486,6 +1489,7 @@ test('should list projects with all features archived', async () => {
|
||||
|
||||
await stores.featureToggleStore.create(project.id, {
|
||||
name: 'archived-toggle',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await featureToggleService.archiveToggle('archived-toggle', user);
|
||||
@ -2088,6 +2092,7 @@ test('deleting a project with archived toggles should result in any remaining ar
|
||||
|
||||
await stores.featureToggleStore.create(project.id, {
|
||||
name: toggleName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await stores.featureToggleStore.archive(toggleName);
|
||||
|
@ -31,7 +31,10 @@ test('Setting enabled to same as existing value returns 0', async () => {
|
||||
enabled: true,
|
||||
type: 'test',
|
||||
});
|
||||
await featureStore.create('default', { name: featureName });
|
||||
await featureStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await featureEnvironmentStore.connectProject(envName, 'default');
|
||||
await featureEnvironmentStore.connectFeatures(envName, 'default');
|
||||
const enabledStatus = await featureEnvironmentStore.isEnvironmentEnabled(
|
||||
@ -54,7 +57,10 @@ test('Setting enabled to not existing value returns 1', async () => {
|
||||
enabled: true,
|
||||
type: 'test',
|
||||
});
|
||||
await featureStore.create('default', { name: featureName });
|
||||
await featureStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await featureEnvironmentStore.connectProject(envName, 'default');
|
||||
await featureEnvironmentStore.connectFeatures(envName, 'default');
|
||||
const enabledStatus = await featureEnvironmentStore.isEnvironmentEnabled(
|
||||
|
@ -19,7 +19,10 @@ beforeAll(async () => {
|
||||
featureTagStore = stores.featureTagStore;
|
||||
featureToggleStore = stores.featureToggleStore;
|
||||
await stores.tagStore.createTag(tag);
|
||||
await featureToggleStore.create('default', { name: featureName });
|
||||
await featureToggleStore.create('default', {
|
||||
name: featureName,
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
@ -80,6 +83,7 @@ test('get all feature tags', async () => {
|
||||
await featureTagStore.tagFeature(featureName, tag, TESTUSERID);
|
||||
await featureToggleStore.create('default', {
|
||||
name: 'some-other-toggle',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await featureTagStore.tagFeature('some-other-toggle', tag, TESTUSERID);
|
||||
const all = await featureTagStore.getAll();
|
||||
@ -89,6 +93,7 @@ test('get all feature tags', async () => {
|
||||
test('should import feature tags', async () => {
|
||||
await featureToggleStore.create('default', {
|
||||
name: 'some-other-toggle-import',
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
await featureTagStore.tagFeatures([
|
||||
{
|
||||
@ -121,7 +126,7 @@ test('should throw not found error if feature does not exist', async () => {
|
||||
|
||||
test('Returns empty tag list for existing feature with no tags', async () => {
|
||||
const name = 'feature.with.no.tags';
|
||||
await featureToggleStore.create('default', { name });
|
||||
await featureToggleStore.create('default', { name, createdByUserId: 9999 });
|
||||
const tags = await featureTagStore.getAllTagsForFeature(name);
|
||||
expect(tags).toHaveLength(0);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user