diff --git a/src/lib/db/feature-toggle-client-store.ts b/src/lib/db/feature-toggle-client-store.ts index d6115f5186..889a26c22e 100644 --- a/src/lib/db/feature-toggle-client-store.ts +++ b/src/lib/db/feature-toggle-client-store.ts @@ -9,6 +9,7 @@ import { IStrategyConfig, } from '../types/model'; import { IFeatureToggleClientStore } from '../types/stores/feature-toggle-client-store'; +import { DEFAULT_ENV } from '../util/constants'; export interface FeaturesTable { name: string; @@ -61,10 +62,7 @@ export default class FeatureToggleClientStore archived: boolean = false, isAdmin: boolean = true, ): Promise { - const environments = [':global:']; - if (featureQuery?.environment) { - environments.push(featureQuery.environment); - } + const environment = featureQuery?.environment || DEFAULT_ENV; const stopTimer = this.timer('getFeatureAdmin'); let query = this.db('features') .select( @@ -97,7 +95,7 @@ export default class FeatureToggleClientStore 'feature_environments.environment', ); }) - .whereIn('feature_environments.environment', environments) + .where('feature_environments.environment', environment) .where({ archived }); if (featureQuery) { if (featureQuery.tag) { @@ -133,11 +131,7 @@ export default class FeatureToggleClientStore if (r.strategy_name) { feature.strategies.push(this.getAdminStrategy(r, isAdmin)); } - if (feature.enabled === undefined) { - feature.enabled = r.enabled; - } else { - feature.enabled = feature.enabled && r.enabled; - } + feature.enabled = r.enabled; feature.name = r.name; feature.description = r.description; feature.project = r.project; diff --git a/src/lib/db/project-store.ts b/src/lib/db/project-store.ts index 840d48abd3..adba0644c5 100644 --- a/src/lib/db/project-store.ts +++ b/src/lib/db/project-store.ts @@ -8,6 +8,7 @@ import { IProjectInsert, IProjectStore, } from '../types/stores/project-store'; +import { DEFAULT_ENV } from '../util/constants'; const COLUMNS = ['id', 'name', 'description', 'created_at', 'health']; const TABLE = 'projects'; @@ -99,16 +100,16 @@ class ProjectStore implements IProjectStore { .onConflict('id') .ignore(); if (rows.length > 0) { - await this.addGlobalEnvironment(rows); + await this.addDefaultEnvironment(rows); return rows.map(this.mapRow); } return []; } - async addGlobalEnvironment(projects: any[]): Promise { + async addDefaultEnvironment(projects: any[]): Promise { const environments = projects.map((p) => ({ project_id: p.id, - environment_name: ':global:', + environment_name: DEFAULT_ENV, })); await this.db('project_environments') .insert(environments) diff --git a/src/lib/routes/admin-api/feature.ts b/src/lib/routes/admin-api/feature.ts index 337832c082..0823fe629f 100644 --- a/src/lib/routes/admin-api/feature.ts +++ b/src/lib/routes/admin-api/feature.ts @@ -16,8 +16,8 @@ import FeatureToggleServiceV2 from '../../services/feature-toggle-service-v2'; import { featureSchema, querySchema } from '../../schema/feature-schema'; import { IFeatureToggleQuery } from '../../types/model'; import FeatureTagService from '../../services/feature-tag-service'; -import { GLOBAL_ENV } from '../../types/environment'; import { IAuthRequest } from '../unleash-types'; +import { DEFAULT_ENV } from '../../util/constants'; const version = 1; @@ -110,11 +110,11 @@ class FeatureController extends Controller { private async getLegacyFeatureToggle(name: string): Promise { const feature = await this.featureService2.getFeatureToggle(name); - const globalEnv = feature.environments.find( - (e) => e.name === GLOBAL_ENV, + const defaultEnv = feature.environments.find( + (e) => e.name === DEFAULT_ENV, ); - const strategies = globalEnv?.strategies || []; - const enabled = globalEnv?.enabled || false; + const strategies = defaultEnv?.strategies || []; + const enabled = defaultEnv?.enabled || false; delete feature.environments; return { ...feature, enabled, strategies }; @@ -181,7 +181,7 @@ class FeatureController extends Controller { await this.featureService2.updateEnabled( createdFeature.project, createdFeature.name, - GLOBAL_ENV, + DEFAULT_ENV, enabled, userName, ); @@ -228,7 +228,7 @@ class FeatureController extends Controller { await this.featureService2.updateEnabled( projectId, updatedFeature.name, - GLOBAL_ENV, + DEFAULT_ENV, updatedFeature.enabled, userName, ); @@ -246,7 +246,7 @@ class FeatureController extends Controller { const feature = await this.featureService2.toggle( projectId, featureName, - GLOBAL_ENV, + DEFAULT_ENV, userName, ); res.status(200).json(feature); @@ -259,7 +259,7 @@ class FeatureController extends Controller { const feature = await this.featureService2.updateEnabled( projectId, featureName, - GLOBAL_ENV, + DEFAULT_ENV, true, userName, ); @@ -273,7 +273,7 @@ class FeatureController extends Controller { const feature = await this.featureService2.updateEnabled( projectId, featureName, - GLOBAL_ENV, + DEFAULT_ENV, false, userName, ); diff --git a/src/lib/services/feature-toggle-service-v2.ts b/src/lib/services/feature-toggle-service-v2.ts index 85ebca88e1..9aa66d8618 100644 --- a/src/lib/services/feature-toggle-service-v2.ts +++ b/src/lib/services/feature-toggle-service-v2.ts @@ -19,7 +19,6 @@ import { FEATURE_STRATEGY_UPDATE, FEATURE_UPDATED, } from '../types/events'; -import { GLOBAL_ENV } from '../types/environment'; import NotFoundError from '../error/notfound-error'; import { FeatureConfigurationClient, @@ -42,6 +41,7 @@ import { } from '../types/model'; import { IFeatureEnvironmentStore } from '../types/stores/feature-environment-store'; import { IFeatureToggleClientStore } from '../types/stores/feature-toggle-client-store'; +import { DEFAULT_ENV } from '../util/constants'; class FeatureToggleServiceV2 { private logger: Logger; @@ -96,7 +96,7 @@ class FeatureToggleServiceV2 { projectId: string, featureName: string, userName: string, - environment: string = GLOBAL_ENV, + environment: string = DEFAULT_ENV, ): Promise { try { const newFeatureStrategy = @@ -226,7 +226,7 @@ class FeatureToggleServiceV2 { id: string, userName: string, project: string = 'default', - environment: string = GLOBAL_ENV, + environment: string = DEFAULT_ENV, ): Promise { await this.featureStrategiesStore.delete(id); await this.eventStore.store({ @@ -243,7 +243,7 @@ class FeatureToggleServiceV2 { async getStrategiesForEnvironment( project: string, featureName: string, - environment: string = GLOBAL_ENV, + environment: string = DEFAULT_ENV, ): Promise { const hasEnv = await this.featureEnvironmentStore.featureHasEnvironment( environment, @@ -405,7 +405,7 @@ class FeatureToggleServiceV2 { async removeAllStrategiesForEnv( toggleName: string, - environment: string = GLOBAL_ENV, + environment: string = DEFAULT_ENV, ): Promise { await this.featureStrategiesStore.removeAllStrategiesForFeatureEnv( toggleName, @@ -605,11 +605,11 @@ class FeatureToggleServiceV2 { await this.featureStrategiesStore.getFeatureToggleWithEnvs( featureName, ); - const globalEnv = feature.environments.find( - (e) => e.name === GLOBAL_ENV, + const defaultEnv = feature.environments.find( + (e) => e.name === DEFAULT_ENV, ); - const strategies = globalEnv?.strategies || []; - const enabled = globalEnv?.enabled || false; + const strategies = defaultEnv?.strategies || []; + const enabled = defaultEnv?.enabled || false; return { ...feature, enabled, strategies }; } diff --git a/src/lib/services/project-service.ts b/src/lib/services/project-service.ts index 1ec6c8777d..7006c8100d 100644 --- a/src/lib/services/project-service.ts +++ b/src/lib/services/project-service.ts @@ -20,7 +20,6 @@ import { IUserWithRole, RoleName, } from '../types/model'; -import { GLOBAL_ENV } from '../types/environment'; import { IEnvironmentStore } from '../types/stores/environment-store'; import { IFeatureTypeStore } from '../types/stores/feature-type-store'; import { IFeatureToggleStore } from '../types/stores/feature-toggle-store'; @@ -31,6 +30,7 @@ import { IEventStore } from '../types/stores/event-store'; import FeatureToggleServiceV2 from './feature-toggle-service-v2'; import { CREATE_FEATURE, UPDATE_FEATURE } from '../types/permissions'; import NoAccessError from '../error/no-access-error'; +import { DEFAULT_ENV } from '../util/constants'; const getCreatedBy = (user: User) => user.email || user.username; @@ -123,7 +123,8 @@ export default class ProjectService { await this.store.create(data); - await this.featureEnvironmentStore.connectProject(GLOBAL_ENV, data.id); + // TODO: we should only connect to enabled environments + await this.featureEnvironmentStore.connectProject(DEFAULT_ENV, data.id); await this.accessService.createDefaultProjectRoles(user, data.id); diff --git a/src/lib/services/state-schema.ts b/src/lib/services/state-schema.ts index 2345d7aa93..f7770847f9 100644 --- a/src/lib/services/state-schema.ts +++ b/src/lib/services/state-schema.ts @@ -26,7 +26,7 @@ export const featureEnvironmentsSchema = joi.object().keys({ }); export const environmentSchema = joi.object().keys({ - name: nameType.allow(':global:'), + name: nameType, displayName: joi.string().optional().allow(''), type: joi.string().required(), sortOrder: joi.number().optional(), diff --git a/src/lib/services/state-service.ts b/src/lib/services/state-service.ts index e1251b8b6a..f47044fa3a 100644 --- a/src/lib/services/state-service.ts +++ b/src/lib/services/state-service.ts @@ -28,7 +28,6 @@ import { IProject, IStrategyConfig, } from '../types/model'; -import { GLOBAL_ENV } from '../types/environment'; import { Logger } from '../logger'; import { IFeatureTag, @@ -44,6 +43,7 @@ import { IFeatureStrategiesStore } from '../types/stores/feature-strategies-stor import { IEnvironmentStore } from '../types/stores/environment-store'; import { IFeatureEnvironmentStore } from '../types/stores/feature-environment-store'; import { IUnleashStores } from '../types/stores'; +import { DEFAULT_ENV } from '../util/constants'; export interface IBackupOption { includeFeatureToggles: boolean; @@ -245,14 +245,14 @@ export default class StateService { projectId: f.project, constraints: strategy.constraints || [], parameters: strategy.parameters || {}, - environment: GLOBAL_ENV, + environment: DEFAULT_ENV, strategyName: strategy.name, })), ); const newFeatures = features; const featureEnvironments = features.map((feature) => ({ featureName: feature.name, - environment: GLOBAL_ENV, + environment: DEFAULT_ENV, enabled: feature.enabled, })); return { diff --git a/src/lib/util/constants.ts b/src/lib/util/constants.ts index a81e7ceb6b..6930597ae8 100644 --- a/src/lib/util/constants.ts +++ b/src/lib/util/constants.ts @@ -1,2 +1,3 @@ export const MILLISECONDS_IN_DAY = 86400000; export const MILLISECONDS_IN_ONE_HOUR = 3600000; +export const DEFAULT_ENV = 'default'; diff --git a/src/migrations/20210920104218-rename-global-env-to-default-env.js b/src/migrations/20210920104218-rename-global-env-to-default-env.js new file mode 100644 index 0000000000..6f0fc1f6f6 --- /dev/null +++ b/src/migrations/20210920104218-rename-global-env-to-default-env.js @@ -0,0 +1,31 @@ +'use strict'; + +exports.up = function (db, cb) { + db.runSql( + ` + INSERT INTO environments(name, display_name, protected, sort_order) VALUES ('default', 'Default Environment', true, 1); + ALTER TABLE feature_strategies ALTER COLUMN environment SET DEFAULT 'default'; + ALTER TABLE feature_environments ALTER COLUMN environment SET DEFAULT 'default'; + UPDATE feature_strategies SET environment = 'default' WHERE environment = ':global:'; + UPDATE feature_environments SET environment = 'default' WHERE environment = ':global:'; + UPDATE project_environments SET environment_name = 'default' WHERE environment_name = ':global:'; + DELETE FROM environments WHERE name = ':global:'; + `, + cb, + ); +}; + +exports.down = function (db, cb) { + db.runSql( + ` + INSERT INTO environments(name, display_name, protected) VALUES (':global:', 'Across all environments', true); + ALTER TABLE feature_strategies ALTER COLUMN environment SET DEFAULT ':global:'; + ALTER TABLE feature_environments ALTER COLUMN environment SET DEFAULT ':global:'; + UPDATE feature_strategies SET environment = ':global:' WHERE environment = 'default'; + UPDATE feature_environments SET environment = ':global:' WHERE environment = 'default'; + UPDATE project_environments SET environment_name = ':global:' WHERE environment_name = 'default'; + DELETE FROM environments WHERE name = 'default'; + `, + cb, + ); +}; diff --git a/src/test/e2e/api/admin/api-token.e2e.test.ts b/src/test/e2e/api/admin/api-token.e2e.test.ts index 7d79431500..e3339cc93d 100644 --- a/src/test/e2e/api/admin/api-token.e2e.test.ts +++ b/src/test/e2e/api/admin/api-token.e2e.test.ts @@ -2,6 +2,7 @@ import { setupApp } from '../../helpers/test-helper'; import dbInit from '../../helpers/database-init'; import getLogger from '../../../fixtures/no-logger'; import { ALL, ApiTokenType } from '../../../../lib/types/models/api-token'; +import { DEFAULT_ENV } from '../../../../lib/util/constants'; let db; let app; @@ -214,14 +215,14 @@ test('creates new client token with project & environment set', async () => { username: 'default-client', type: 'client', project: 'default', - environment: ':global:', + environment: DEFAULT_ENV, }) .set('Content-Type', 'application/json') .expect(201) .expect((res) => { expect(res.body.type).toBe('client'); expect(res.body.secret.length > 16).toBe(true); - expect(res.body.environment).toBe(':global:'); + expect(res.body.environment).toBe(DEFAULT_ENV); expect(res.body.project).toBe('default'); }); }); @@ -247,12 +248,12 @@ test('should prefix token with "project:environment."', async () => { username: 'default-client', type: 'client', project: 'default', - environment: ':global:', + environment: DEFAULT_ENV, }) .set('Content-Type', 'application/json') .expect(201) .expect((res) => { - expect(res.body.secret).toMatch(/default::global:\..*/); + expect(res.body.secret).toMatch(/default:default\..*/); }); }); @@ -323,7 +324,7 @@ test('admin token only supports ALL environments', async () => { username: 'default-admin', type: 'admin', project: '*', - environment: ':global:', + environment: DEFAULT_ENV, }) .set('Content-Type', 'application/json') .expect(400); diff --git a/src/test/e2e/api/admin/environment.test.ts b/src/test/e2e/api/admin/environment.test.ts index a901b7c9f6..8296208f2d 100644 --- a/src/test/e2e/api/admin/environment.test.ts +++ b/src/test/e2e/api/admin/environment.test.ts @@ -1,6 +1,7 @@ import dbInit, { ITestDb } from '../../helpers/database-init'; import getLogger from '../../../fixtures/no-logger'; import { IUnleashTest, setupApp } from '../../helpers/test-helper'; +import { DEFAULT_ENV } from '../../../../lib/util/constants'; let app: IUnleashTest; let db: ITestDb; @@ -23,8 +24,8 @@ test('Can list all existing environments', async () => { .expect((res) => { expect(res.body.version).toBe(1); expect(res.body.environments[0]).toStrictEqual({ - displayName: 'Across all environments', - name: ':global:', + displayName: 'Default Environment', + name: DEFAULT_ENV, enabled: true, sortOrder: 1, type: 'production', @@ -43,7 +44,7 @@ test('Can update sort order', async () => { await app.request .put('/api/admin/environments/sort-order') .send({ - ':global:': 2, + [DEFAULT_ENV]: 2, [envName]: 1, }) .expect(200); @@ -56,11 +57,11 @@ test('Can update sort order', async () => { const updatedSort = res.body.environments.find( (t) => t.name === envName, ); - const global = res.body.environments.find( - (t) => t.name === ':global:', + const defaultEnv = res.body.environments.find( + (t) => t.name === DEFAULT_ENV, ); expect(updatedSort.sortOrder).toBe(1); - expect(global.sortOrder).toBe(2); + expect(defaultEnv.sortOrder).toBe(2); }); }); @@ -70,7 +71,7 @@ test('Sort order will fail on wrong data format', async () => { await app.request .put('/api/admin/environments/sort-order') .send({ - ':global:': 'test', + [DEFAULT_ENV]: 'test', [envName]: 1, }) .expect(400); diff --git a/src/test/e2e/api/admin/project/environments.e2e.test.ts b/src/test/e2e/api/admin/project/environments.e2e.test.ts index d52a8bac8c..06907d29b8 100644 --- a/src/test/e2e/api/admin/project/environments.e2e.test.ts +++ b/src/test/e2e/api/admin/project/environments.e2e.test.ts @@ -1,6 +1,7 @@ import dbInit, { ITestDb } from '../../../helpers/database-init'; import { IUnleashTest, setupApp } from '../../../helpers/test-helper'; import getLogger from '../../../../fixtures/no-logger'; +import { DEFAULT_ENV } from '../../../../../lib/util/constants'; let app: IUnleashTest; let db: ITestDb; @@ -16,7 +17,7 @@ afterEach(async () => { ); await Promise.all( all - .filter((env) => env !== ':global:') + .filter((env) => env !== DEFAULT_ENV) .map(async (env) => db.stores.projectStore.deleteEnvironmentForProject( 'default', diff --git a/src/test/e2e/api/admin/project/feature.strategy.e2e.test.ts b/src/test/e2e/api/admin/project/feature.strategy.e2e.test.ts index f8234a6405..b987f95524 100644 --- a/src/test/e2e/api/admin/project/feature.strategy.e2e.test.ts +++ b/src/test/e2e/api/admin/project/feature.strategy.e2e.test.ts @@ -2,6 +2,7 @@ import dbInit, { ITestDb } from '../../../helpers/database-init'; import { IUnleashTest, setupApp } from '../../../helpers/test-helper'; import getLogger from '../../../../fixtures/no-logger'; import { GLOBAL_ENV } from '../../../../../lib/types/environment'; +import { DEFAULT_ENV } from '../../../../../lib/util/constants'; let app: IUnleashTest; let db: ITestDb; @@ -20,7 +21,7 @@ afterEach(async () => { ); await Promise.all( all - .filter((env) => env !== ':global:') + .filter((env) => env !== DEFAULT_ENV) .map(async (env) => db.stores.projectStore.deleteEnvironmentForProject( 'default', @@ -181,7 +182,7 @@ test('Project overview includes environment connected to feature', async () => { .get('/api/admin/projects/default') .expect(200) .expect((r) => { - expect(r.body.features[0].environments[0].name).toBe(':global:'); + expect(r.body.features[0].environments[0].name).toBe(DEFAULT_ENV); expect(r.body.features[0].environments[1].name).toBe( 'project-overview', ); @@ -540,8 +541,8 @@ test('Should archive feature toggle', async () => { expect(toggle).toBeDefined(); }); -test('Can add strategy to feature toggle to default env', async () => { - const envName = 'default'; +test('Can add strategy to feature toggle to a "some-env-2"', async () => { + const envName = 'some-env-2'; const featureName = 'feature.strategy.toggle'; // Create environment await db.stores.environmentStore.create({ diff --git a/src/test/e2e/api/admin/state.e2e.test.ts b/src/test/e2e/api/admin/state.e2e.test.ts index e396d81153..fde6fb4683 100644 --- a/src/test/e2e/api/admin/state.e2e.test.ts +++ b/src/test/e2e/api/admin/state.e2e.test.ts @@ -1,7 +1,7 @@ import dbInit, { ITestDb } from '../../helpers/database-init'; import { IUnleashTest, setupApp } from '../../helpers/test-helper'; import getLogger from '../../../fixtures/no-logger'; -import { GLOBAL_ENV } from '../../../../lib/types/environment'; +import { DEFAULT_ENV } from '../../../../lib/util/constants'; const importData = require('../../../examples/import.json'); @@ -265,7 +265,7 @@ test('Roundtrip with strategies in multiple environments works', async () => { projectId, ); await app.services.environmentService.addEnvironmentToProject( - GLOBAL_ENV, + DEFAULT_ENV, projectId, ); await app.services.featureToggleServiceV2.createFeatureToggle( @@ -299,7 +299,7 @@ test('Roundtrip with strategies in multiple environments works', async () => { }, projectId, featureName, - GLOBAL_ENV, + DEFAULT_ENV, ); const data = await app.services.stateService.export({}); await app.services.stateService.import({ diff --git a/src/test/e2e/api/client/feature.e2e.test.ts b/src/test/e2e/api/client/feature.e2e.test.ts index ada2a5ee72..e0e73f852d 100644 --- a/src/test/e2e/api/client/feature.e2e.test.ts +++ b/src/test/e2e/api/client/feature.e2e.test.ts @@ -1,6 +1,7 @@ import { IUnleashTest, setupApp } from '../../helpers/test-helper'; import dbInit, { ITestDb } from '../../helpers/database-init'; import getLogger from '../../../fixtures/no-logger'; +import { DEFAULT_ENV } from '../../../../lib/util/constants'; let app: IUnleashTest; let db: ITestDb; @@ -153,6 +154,7 @@ test('Can filter features by namePrefix', async () => { test('Can get strategies for specific environment', async () => { const featureName = 'test.feature.with.env'; + const env = DEFAULT_ENV; // Create feature toggle await app.request.post('/api/admin/projects/default/features').send({ @@ -163,7 +165,7 @@ test('Can get strategies for specific environment', async () => { // Add global strategy await app.request .post( - `/api/admin/projects/default/features/${featureName}/environments/:global:/strategies`, + `/api/admin/projects/default/features/${featureName}/environments/${env}/strategies`, ) .send({ name: 'default', @@ -198,7 +200,7 @@ test('Can get strategies for specific environment', async () => { .expect(200) .expect((res) => { expect(res.body.name).toBe(featureName); - expect(res.body.strategies).toHaveLength(2); + expect(res.body.strategies).toHaveLength(1); expect( res.body.strategies.find((s) => s.name === 'custom1'), ).toBeDefined(); diff --git a/src/test/e2e/api/client/feature.token.access.e2e.test.ts b/src/test/e2e/api/client/feature.token.access.e2e.test.ts index 26b1c9f833..0b394bc5a4 100644 --- a/src/test/e2e/api/client/feature.token.access.e2e.test.ts +++ b/src/test/e2e/api/client/feature.token.access.e2e.test.ts @@ -3,6 +3,7 @@ import dbInit, { ITestDb } from '../../helpers/database-init'; import getLogger from '../../../fixtures/no-logger'; import { ApiTokenService } from '../../../../lib/services/api-token-service'; import { ApiTokenType } from '../../../../lib/types/models/api-token'; +import { DEFAULT_ENV } from '../../../../lib/util/constants'; let app: IUnleashTest; let db: ITestDb; @@ -117,11 +118,11 @@ afterAll(async () => { await db.destroy(); }); -test('returns feature toggle with :global: config', async () => { +test('returns feature toggle with "default" config', async () => { const token = await apiTokenService.createApiToken({ type: ApiTokenType.CLIENT, username, - environment: ':global:', + environment: DEFAULT_ENV, project, }); await app.request @@ -157,7 +158,8 @@ test('returns feature toggle with testing environment config', async () => { const f2 = features.find((f) => f.name === feature2); expect(features).toHaveLength(2); - expect(f1.strategies).toHaveLength(2); + expect(f1.strategies).toHaveLength(1); + expect(f1.strategies[0].name).toBe('custom-testing'); expect(f2.strategies).toHaveLength(1); expect(query.project[0]).toBe(project); expect(query.environment).toBe(environment); diff --git a/src/test/e2e/helpers/database-init.ts b/src/test/e2e/helpers/database-init.ts index 16961fc5fb..64e1ab4aa2 100644 --- a/src/test/e2e/helpers/database-init.ts +++ b/src/test/e2e/helpers/database-init.ts @@ -10,6 +10,7 @@ import noLoggerProvider from '../../fixtures/no-logger'; import EnvironmentStore from '../../../lib/db/environment-store'; import { IUnleashStores } from '../../../lib/types'; import { IFeatureEnvironmentStore } from '../../../lib/types/stores/feature-environment-store'; +import { DEFAULT_ENV } from '../../../lib/util/constants'; // require('db-migrate-shared').log.silence(false); @@ -54,7 +55,7 @@ function createTagTypes(store) { } async function connectProject(store: IFeatureEnvironmentStore): Promise { - await store.connectProject(':global:', 'default'); + await store.connectProject(DEFAULT_ENV, 'default'); } async function createEnvironments(store: EnvironmentStore): Promise { diff --git a/src/test/e2e/helpers/database.json b/src/test/e2e/helpers/database.json index 32f18a10ab..f5d4b3d7e9 100644 --- a/src/test/e2e/helpers/database.json +++ b/src/test/e2e/helpers/database.json @@ -29,8 +29,8 @@ ], "environments": [ { - "name": ":global:", - "displayName": "Across all environments", + "name": "default", + "displayName": "Default Environment", "type": "production", "sortOrder": 1, "enabled": true, diff --git a/src/test/e2e/services/environment-service.test.ts b/src/test/e2e/services/environment-service.test.ts index 2817ced5f6..ac97dca4fd 100644 --- a/src/test/e2e/services/environment-service.test.ts +++ b/src/test/e2e/services/environment-service.test.ts @@ -38,7 +38,7 @@ test('Can get all', async () => { }); const environments = await service.getAll(); - expect(environments).toHaveLength(3); // the one we created plus ':global:' + expect(environments).toHaveLength(3); // the one we created plus 'default' }); test('Can connect environment to project', async () => { diff --git a/src/test/e2e/services/feature-toggle-service-v2.e2e.test.ts b/src/test/e2e/services/feature-toggle-service-v2.e2e.test.ts index 43b764417d..45a7e2bf08 100644 --- a/src/test/e2e/services/feature-toggle-service-v2.e2e.test.ts +++ b/src/test/e2e/services/feature-toggle-service-v2.e2e.test.ts @@ -4,7 +4,7 @@ import FeatureToggleServiceV2 from '../../../lib/services/feature-toggle-service import { IStrategyConfig } from '../../../lib/types/model'; import { createTestConfig } from '../../config/test-config'; import dbInit from '../helpers/database-init'; -import { GLOBAL_ENV } from '../../../lib/types/environment'; +import { DEFAULT_ENV } from '../../../lib/util/constants'; let stores; let db; @@ -80,7 +80,7 @@ test('Should be able to update existing strategy configuration', async () => { expect(createdConfig.name).toEqual('default'); const updatedConfig = await service.updateStrategy( createdConfig.id, - GLOBAL_ENV, + DEFAULT_ENV, projectId, username, { @@ -112,7 +112,7 @@ test('Should include legacy props in event log when updating strategy configurat await service.updateEnabled( 'default', featureName, - GLOBAL_ENV, + DEFAULT_ENV, true, userName, ); diff --git a/src/test/examples/exported-3175-enterprise.json b/src/test/examples/exported-3175-enterprise.json index b447711506..29adaca842 100644 --- a/src/test/examples/exported-3175-enterprise.json +++ b/src/test/examples/exported-3175-enterprise.json @@ -1 +1,126 @@ -{"version":1,"features":[{"name":"in-another-project","description":"","type":"release","project":"someother","enabled":true,"stale":false,"strategies":[{"name":"gradualRolloutRandom","parameters":{"percentage":"29"},"constraints":[{"contextName":"environment","operator":"IN","values":["dev"]},{"contextName":"environment","operator":"IN","values":["prod"]}]}],"variants":[],"createdAt":"2021-09-17T07:14:03.718Z","lastSeenAt":null},{"name":"this-is-fun","description":"","type":"release","project":"default","enabled":true,"stale":false,"strategies":[{"name":"gradualRolloutRandom","parameters":{"percentage":"100"}}],"variants":[],"createdAt":"2021-09-17T07:06:40.925Z","lastSeenAt":null},{"name":"version.three.seventeen","description":"","type":"operational","project":"default","enabled":true,"stale":false,"strategies":[{"name":"default","parameters":{}}],"variants":[],"createdAt":"2021-09-17T07:06:56.421Z","lastSeenAt":null},{"name":"with-constraints","description":"","type":"release","project":"default","enabled":true,"stale":false,"strategies":[{"name":"default","parameters":{},"constraints":[{"contextName":"userId","operator":"IN","values":["123456"]}]}],"variants":[],"createdAt":"2021-09-17T07:14:39.509Z","lastSeenAt":null}],"strategies":[],"projects":[{"id":"default","name":"Default","description":"Default project","createdAt":"2021-09-17T05:06:16.299Z"},{"id":"someother","name":"Some other project","description":"","createdAt":"2021-09-17T05:13:45.011Z"}],"tagTypes":[{"name":"simple","description":"Used to simplify filtering of features","icon":"#"}],"tags":[],"featureTags":[]} \ No newline at end of file +{ + "version": 1, + "features": [ + { + "name": "in-another-project", + "description": "", + "type": "release", + "project": "someother", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "gradualRolloutRandom", + "parameters": { + "percentage": "29" + }, + "constraints": [ + { + "contextName": "environment", + "operator": "IN", + "values": [ + "dev" + ] + }, + { + "contextName": "environment", + "operator": "IN", + "values": [ + "prod" + ] + } + ] + } + ], + "variants": [], + "createdAt": "2021-09-17T07:14:03.718Z", + "lastSeenAt": null + }, + { + "name": "this-is-fun", + "description": "", + "type": "release", + "project": "default", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "gradualRolloutRandom", + "parameters": { + "percentage": "100" + } + } + ], + "variants": [], + "createdAt": "2021-09-17T07:06:40.925Z", + "lastSeenAt": null + }, + { + "name": "version.three.seventeen", + "description": "", + "type": "operational", + "project": "default", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "default", + "parameters": {} + } + ], + "variants": [], + "createdAt": "2021-09-17T07:06:56.421Z", + "lastSeenAt": null + }, + { + "name": "with-constraints", + "description": "", + "type": "release", + "project": "default", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "default", + "parameters": {}, + "constraints": [ + { + "contextName": "userId", + "operator": "IN", + "values": [ + "123456" + ] + } + ] + } + ], + "variants": [], + "createdAt": "2021-09-17T07:14:39.509Z", + "lastSeenAt": null + } + ], + "strategies": [], + "projects": [ + { + "id": "default", + "name": "Default", + "description": "Default project", + "createdAt": "2021-09-17T05:06:16.299Z" + }, + { + "id": "someother", + "name": "Some other project", + "description": "", + "createdAt": "2021-09-17T05:13:45.011Z" + } + ], + "tagTypes": [ + { + "name": "simple", + "description": "Used to simplify filtering of features", + "icon": "#" + } + ], + "tags": [], + "featureTags": [] +} \ No newline at end of file diff --git a/src/test/examples/exported405-enterprise.json b/src/test/examples/exported405-enterprise.json index d2c0b5e30d..b43e29db81 100644 --- a/src/test/examples/exported405-enterprise.json +++ b/src/test/examples/exported405-enterprise.json @@ -1 +1,198 @@ -{"version":1,"features":[{"name":"another-toggle","description":"","type":"release","project":"someother","enabled":true,"stale":false,"strategies":[{"name":"userWithId","parameters":{"userIds":"12541,123"},"constraints":[]}],"variants":[],"createdAt":"2021-09-17T07:22:16.404Z","lastSeenAt":null},{"name":"in-another-project","description":"","type":"release","project":"someother","enabled":true,"stale":false,"strategies":[{"name":"gradualRolloutRandom","parameters":{"percentage":"29"},"constraints":[{"contextName":"environment","operator":"IN","values":["dev"]},{"contextName":"environment","operator":"IN","values":["prod"]}]}],"variants":[],"createdAt":"2021-09-17T07:14:03.718Z","lastSeenAt":null},{"name":"this-is-fun","description":"","type":"release","project":"default","enabled":true,"stale":false,"strategies":[{"name":"gradualRolloutRandom","parameters":{"percentage":"100"}}],"variants":[],"createdAt":"2021-09-17T07:06:40.925Z","lastSeenAt":null},{"name":"version.three.seventeen","description":"","type":"operational","project":"default","enabled":true,"stale":false,"strategies":[{"name":"default","parameters":{}}],"variants":[],"createdAt":"2021-09-17T07:06:56.421Z","lastSeenAt":null},{"name":"with-constraints","description":"","type":"release","project":"default","enabled":true,"stale":false,"strategies":[{"name":"default","parameters":{},"constraints":[{"contextName":"userId","operator":"IN","values":["123456"]}]}],"variants":[],"createdAt":"2021-09-17T07:14:39.509Z","lastSeenAt":null}],"strategies":[{"name":"gradualRolloutRandom","description":"Randomly activate the feature toggle. No stickiness.","parameters":[{"name":"percentage","type":"percentage","description":"","required":false}],"deprecated":true},{"name":"gradualRolloutSessionId","description":"Gradually activate feature toggle. Stickiness based on session id.","parameters":[{"name":"percentage","type":"percentage","description":"","required":false},{"name":"groupId","type":"string","description":"Used to define a activation groups, which allows you to correlate across feature toggles.","required":true}],"deprecated":true},{"name":"gradualRolloutUserId","description":"Gradually activate feature toggle for logged in users. Stickiness based on user id.","parameters":[{"name":"percentage","type":"percentage","description":"","required":false},{"name":"groupId","type":"string","description":"Used to define a activation groups, which allows you to correlate across feature toggles.","required":true}],"deprecated":true}],"projects":[{"id":"default","name":"Default","description":"Default project","createdAt":"2021-09-17T05:06:16.299Z"},{"id":"someother","name":"Some other project","description":"","createdAt":"2021-09-17T05:13:45.011Z"}],"tagTypes":[{"name":"simple","description":"Used to simplify filtering of features","icon":"#"}],"tags":[],"featureTags":[]} \ No newline at end of file +{ + "version": 1, + "features": [ + { + "name": "another-toggle", + "description": "", + "type": "release", + "project": "someother", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "userWithId", + "parameters": { + "userIds": "12541,123" + }, + "constraints": [] + } + ], + "variants": [], + "createdAt": "2021-09-17T07:22:16.404Z", + "lastSeenAt": null + }, + { + "name": "in-another-project", + "description": "", + "type": "release", + "project": "someother", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "gradualRolloutRandom", + "parameters": { + "percentage": "29" + }, + "constraints": [ + { + "contextName": "environment", + "operator": "IN", + "values": [ + "dev" + ] + }, + { + "contextName": "environment", + "operator": "IN", + "values": [ + "prod" + ] + } + ] + } + ], + "variants": [], + "createdAt": "2021-09-17T07:14:03.718Z", + "lastSeenAt": null + }, + { + "name": "this-is-fun", + "description": "", + "type": "release", + "project": "default", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "gradualRolloutRandom", + "parameters": { + "percentage": "100" + } + } + ], + "variants": [], + "createdAt": "2021-09-17T07:06:40.925Z", + "lastSeenAt": null + }, + { + "name": "version.three.seventeen", + "description": "", + "type": "operational", + "project": "default", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "default", + "parameters": {} + } + ], + "variants": [], + "createdAt": "2021-09-17T07:06:56.421Z", + "lastSeenAt": null + }, + { + "name": "with-constraints", + "description": "", + "type": "release", + "project": "default", + "enabled": true, + "stale": false, + "strategies": [ + { + "name": "default", + "parameters": {}, + "constraints": [ + { + "contextName": "userId", + "operator": "IN", + "values": [ + "123456" + ] + } + ] + } + ], + "variants": [], + "createdAt": "2021-09-17T07:14:39.509Z", + "lastSeenAt": null + } + ], + "strategies": [ + { + "name": "gradualRolloutRandom", + "description": "Randomly activate the feature toggle. No stickiness.", + "parameters": [ + { + "name": "percentage", + "type": "percentage", + "description": "", + "required": false + } + ], + "deprecated": true + }, + { + "name": "gradualRolloutSessionId", + "description": "Gradually activate feature toggle. Stickiness based on session id.", + "parameters": [ + { + "name": "percentage", + "type": "percentage", + "description": "", + "required": false + }, + { + "name": "groupId", + "type": "string", + "description": "Used to define a activation groups, which allows you to correlate across feature toggles.", + "required": true + } + ], + "deprecated": true + }, + { + "name": "gradualRolloutUserId", + "description": "Gradually activate feature toggle for logged in users. Stickiness based on user id.", + "parameters": [ + { + "name": "percentage", + "type": "percentage", + "description": "", + "required": false + }, + { + "name": "groupId", + "type": "string", + "description": "Used to define a activation groups, which allows you to correlate across feature toggles.", + "required": true + } + ], + "deprecated": true + } + ], + "projects": [ + { + "id": "default", + "name": "Default", + "description": "Default project", + "createdAt": "2021-09-17T05:06:16.299Z" + }, + { + "id": "someother", + "name": "Some other project", + "description": "", + "createdAt": "2021-09-17T05:13:45.011Z" + } + ], + "tagTypes": [ + { + "name": "simple", + "description": "Used to simplify filtering of features", + "icon": "#" + } + ], + "tags": [], + "featureTags": [] +} \ No newline at end of file diff --git a/src/test/examples/exported412-enterprise-necessary-fixes.json b/src/test/examples/exported412-enterprise-necessary-fixes.json index d1b1093d1c..0296d749f9 100644 --- a/src/test/examples/exported412-enterprise-necessary-fixes.json +++ b/src/test/examples/exported412-enterprise-necessary-fixes.json @@ -145,7 +145,7 @@ "id": "2ea91298-4565-4db2-8a23-50757001a076", "featureName": "this-is-fun", "projectId": "default", - "environment": ":global:", + "environment": "default", "strategyName": "gradualRolloutRandom", "parameters": { "percentage": "100" @@ -157,7 +157,7 @@ "id": "edaffaee-cf6e-473f-b137-ae15fb88ff53", "featureName": "version.three.seventeen", "projectId": "default", - "environment": ":global:", + "environment": "default", "strategyName": "default", "parameters": {}, "constraints": [], @@ -167,7 +167,7 @@ "id": "e6eaede4-027a-41a9-8e80-0e0fc0a5d7af", "featureName": "in-another-project", "projectId": "someother", - "environment": ":global:", + "environment": "default", "strategyName": "gradualRolloutRandom", "parameters": { "percentage": "29" @@ -194,7 +194,7 @@ "id": "da60e934-246c-4b3e-b314-f2fd1828dd51", "featureName": "with-constraints", "projectId": "default", - "environment": ":global:", + "environment": "default", "strategyName": "default", "parameters": {}, "constraints": [ @@ -212,7 +212,7 @@ "id": "162058f5-3600-4299-97df-d543a0301bdd", "featureName": "another-toggle", "projectId": "someother", - "environment": ":global:", + "environment": "default", "strategyName": "userWithId", "parameters": { "userIds": "12541,123" @@ -224,7 +224,7 @@ "id": "5630e0fb-ebc1-4313-b6df-06b0a563c7b4", "featureName": "toggle-created-in-4-1", "projectId": "default", - "environment": ":global:", + "environment": "default", "strategyName": "applicationHostname", "parameters": { "hostNames": "vg.no" @@ -235,7 +235,7 @@ ], "environments": [ { - "name": ":global:", + "name": "default", "displayName": "Across all environments", "type": "production" } @@ -244,32 +244,32 @@ { "enabled": true, "featureName": "this-is-fun", - "environment": ":global:" + "environment": "default" }, { "enabled": true, "featureName": "version.three.seventeen", - "environment": ":global:" + "environment": "default" }, { "enabled": true, "featureName": "in-another-project", - "environment": ":global:" + "environment": "default" }, { "enabled": true, "featureName": "with-constraints", - "environment": ":global:" + "environment": "default" }, { "enabled": true, "featureName": "another-toggle", - "environment": ":global:" + "environment": "default" }, { "enabled": true, "featureName": "toggle-created-in-4-1", - "environment": ":global:" + "environment": "default" } ] } diff --git a/src/test/examples/exported412-enterprise.json b/src/test/examples/exported412-enterprise.json index f14f23835d..a672ef0aed 100644 --- a/src/test/examples/exported412-enterprise.json +++ b/src/test/examples/exported412-enterprise.json @@ -145,7 +145,7 @@ exported412-enterprise.json{ "id": "2ea91298-4565-4db2-8a23-50757001a076", "featureName": "this-is-fun", "projectName": "default", - "environment": ":global:", + "environment": "default", "strategyName": "gradualRolloutRandom", "parameters": { "percentage": "100" @@ -157,7 +157,7 @@ exported412-enterprise.json{ "id": "edaffaee-cf6e-473f-b137-ae15fb88ff53", "featureName": "version.three.seventeen", "projectName": "default", - "environment": ":global:", + "environment": "default", "strategyName": "default", "parameters": {}, "constraints": [], @@ -167,7 +167,7 @@ exported412-enterprise.json{ "id": "e6eaede4-027a-41a9-8e80-0e0fc0a5d7af", "featureName": "in-another-project", "projectName": "someother", - "environment": ":global:", + "environment": "default", "strategyName": "gradualRolloutRandom", "parameters": { "percentage": "29" @@ -194,7 +194,7 @@ exported412-enterprise.json{ "id": "da60e934-246c-4b3e-b314-f2fd1828dd51", "featureName": "with-constraints", "projectName": "default", - "environment": ":global:", + "environment": "default", "strategyName": "default", "parameters": {}, "constraints": [ @@ -212,7 +212,7 @@ exported412-enterprise.json{ "id": "162058f5-3600-4299-97df-d543a0301bdd", "featureName": "another-toggle", "projectName": "someother", - "environment": ":global:", + "environment": "default", "strategyName": "userWithId", "parameters": { "userIds": "12541,123" @@ -224,7 +224,7 @@ exported412-enterprise.json{ "id": "5630e0fb-ebc1-4313-b6df-06b0a563c7b4", "featureName": "toggle-created-in-4-1", "projectName": "default", - "environment": ":global:", + "environment": "default", "strategyName": "applicationHostname", "parameters": { "hostNames": "vg.no" @@ -235,7 +235,7 @@ exported412-enterprise.json{ ], "environments": [ { - "name": ":global:", + "name": "default", "displayName": "Across all environments" } ], @@ -243,32 +243,32 @@ exported412-enterprise.json{ { "enabled": true, "featureName": "this-is-fun", - "environment": ":global:" + "environment": "default" }, { "enabled": true, "featureName": "version.three.seventeen", - "environment": ":global:" + "environment": "default" }, { "enabled": true, "featureName": "in-another-project", - "environment": ":global:" + "environment": "default" }, { "enabled": true, "featureName": "with-constraints", - "environment": ":global:" + "environment": "default" }, { "enabled": true, "featureName": "another-toggle", - "environment": ":global:" + "environment": "default" }, { "enabled": true, "featureName": "toggle-created-in-4-1", - "environment": ":global:" + "environment": "default" } ] } diff --git a/websitev2/docs/api/admin/feature-toggles-api-v2.md b/websitev2/docs/api/admin/feature-toggles-api-v2.md index 2ccf0e4e01..e90c0895ff 100644 --- a/websitev2/docs/api/admin/feature-toggles-api-v2.md +++ b/websitev2/docs/api/admin/feature-toggles-api-v2.md @@ -13,7 +13,7 @@ In this document we will guide you on how you can work with feature toggles and - A feature toggle can take different configuration, activation strategies, per environment. TODO: Need to explain the following in a bit more details: -- The _:global:: environment +- The _default_ environment > We will in this guide use [HTTPie](https://httpie.io) commands to show examples on how to interact with the API.