mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
* fix: Does not delete api_tokens on drop-Import * feat: Cleans unused apiTokens on environment import * refactor: Moves ALL_PROJECTS and ALL_ENVIRONMENTS to constants * refactor: Renames migration 20220528143630 for a more precise name * refactor: Removes unecessary console.log * fix: Adds correct down-script for migration 20220528143630
This commit is contained in:
parent
d7c450abf8
commit
7ead887147
19
scripts/docker-compose.yml
Normal file
19
scripts/docker-compose.yml
Normal file
@ -0,0 +1,19 @@
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:alpine3.15
|
||||
environment:
|
||||
POSTGRES_USER: unleash_user
|
||||
POSTGRES_PASSWORD: passord
|
||||
POSTGRES_DB: unleash
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
pgadmin:
|
||||
image: dpage/pgadmin4:6.8
|
||||
environment:
|
||||
PGADMIN_DEFAULT_EMAIL: 'admin@admin.com'
|
||||
PGADMIN_DEFAULT_PASSWORD: 'admin'
|
||||
ports:
|
||||
- 8080:80
|
@ -11,7 +11,7 @@ import {
|
||||
IApiTokenCreate,
|
||||
isAllProjects,
|
||||
} from '../types/models/api-token';
|
||||
import { ALL_PROJECTS } from '../../lib/services/access-service';
|
||||
import { ALL_PROJECTS } from '../util/constants';
|
||||
|
||||
const TABLE = 'api_tokens';
|
||||
const API_LINK_TABLE = 'api_token_project';
|
||||
|
@ -24,13 +24,10 @@ import NameExistsError from '../error/name-exists-error';
|
||||
import { IEnvironmentStore } from 'lib/types/stores/environment-store';
|
||||
import RoleInUseError from '../error/role-in-use-error';
|
||||
import { roleSchema } from '../schema/role-schema';
|
||||
import { CUSTOM_ROLE_TYPE } from '../util/constants';
|
||||
import { CUSTOM_ROLE_TYPE, ALL_PROJECTS, ALL_ENVS } from '../util/constants';
|
||||
import { DEFAULT_PROJECT } from '../types/project';
|
||||
import InvalidOperationError from '../error/invalid-operation-error';
|
||||
|
||||
export const ALL_PROJECTS = '*';
|
||||
export const ALL_ENVS = '*';
|
||||
|
||||
const { ADMIN } = permissions;
|
||||
|
||||
const PROJECT_ADMIN = [
|
||||
|
@ -46,10 +46,11 @@ 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';
|
||||
import { DEFAULT_ENV, ALL_ENVS } from '../util/constants';
|
||||
import { GLOBAL_ENV } from '../types/environment';
|
||||
import { ISegmentStore } from '../types/stores/segment-store';
|
||||
import { PartialSome } from '../types/partial';
|
||||
import { IApiTokenStore } from 'lib/types/stores/api-token-store';
|
||||
|
||||
export interface IBackupOption {
|
||||
includeFeatureToggles: boolean;
|
||||
@ -92,6 +93,8 @@ export default class StateService {
|
||||
|
||||
private segmentStore: ISegmentStore;
|
||||
|
||||
private apiTokenStore: IApiTokenStore;
|
||||
|
||||
constructor(
|
||||
stores: IUnleashStores,
|
||||
{ getLogger }: Pick<IUnleashConfig, 'getLogger'>,
|
||||
@ -107,6 +110,7 @@ export default class StateService {
|
||||
this.featureTagStore = stores.featureTagStore;
|
||||
this.environmentStore = stores.environmentStore;
|
||||
this.segmentStore = stores.segmentStore;
|
||||
this.apiTokenStore = stores.apiTokenStore;
|
||||
this.logger = getLogger('services/state-service.js');
|
||||
}
|
||||
|
||||
@ -433,6 +437,15 @@ export default class StateService {
|
||||
data: env,
|
||||
}));
|
||||
await this.eventStore.batchStore(importedEnvironmentEvents);
|
||||
|
||||
const apiTokens = await this.apiTokenStore.getAll();
|
||||
const envNames = importedEnvs.map((env) => env.name);
|
||||
apiTokens
|
||||
.filter((apiToken) => !(apiToken.environment === ALL_ENVS))
|
||||
.filter((apiToken) => !envNames.includes(apiToken.environment))
|
||||
.forEach((apiToken) =>
|
||||
this.apiTokenStore.delete(apiToken.secret),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
export const DEFAULT_ENV = 'default';
|
||||
|
||||
export const ALL_PROJECTS = '*';
|
||||
export const ALL_ENVS = '*';
|
||||
|
||||
export const ROOT_PERMISSION_TYPE = 'root';
|
||||
export const ENVIRONMENT_PERMISSION_TYPE = 'environment';
|
||||
export const PROJECT_PERMISSION_TYPE = 'project';
|
||||
|
@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
exports.up = function (db, cb) {
|
||||
db.runSql(
|
||||
`
|
||||
ALTER TABLE api_tokens DROP CONSTRAINT api_tokens_environment_fkey;
|
||||
`,
|
||||
cb,
|
||||
);
|
||||
};
|
||||
|
||||
exports.down = function (db, cb) {
|
||||
db.runSql(
|
||||
`
|
||||
ALTER TABLE api_tokens ADD CONSTRAINT api_tokens_environment_fkey FOREIGN KEY(environment) REFERENCES environments(name) ON DELETE CASCADE;
|
||||
`,
|
||||
cb,
|
||||
);
|
||||
};
|
@ -3,6 +3,7 @@ import { IUnleashTest, setupApp } from '../../helpers/test-helper';
|
||||
import getLogger from '../../../fixtures/no-logger';
|
||||
import { DEFAULT_ENV } from '../../../../lib/util/constants';
|
||||
import { collectIds } from '../../../../lib/util/collect-ids';
|
||||
import { ApiTokenType } from '../../../../lib/types/models/api-token';
|
||||
|
||||
const importData = require('../../../examples/import.json');
|
||||
|
||||
@ -337,3 +338,105 @@ test(`should import segments and connect them to feature strategies`, async () =
|
||||
expect(activeSegments.length).toEqual(1);
|
||||
expect(collectIds(activeSegments)).toEqual([1]);
|
||||
});
|
||||
|
||||
test(`should not delete api_tokens on import when drop-flag is set`, async () => {
|
||||
const projectId = 'reimported-project';
|
||||
const environment = 'reimported-environment';
|
||||
const apiTokenName = 'not-dropped-token';
|
||||
const featureName = 'reimportedFeature';
|
||||
const userName = 'apiTokens-user';
|
||||
|
||||
await db.stores.environmentStore.create({
|
||||
name: environment,
|
||||
type: 'test',
|
||||
});
|
||||
await db.stores.projectStore.create({
|
||||
name: projectId,
|
||||
id: projectId,
|
||||
description: 'Project for export',
|
||||
});
|
||||
await app.services.environmentService.addEnvironmentToProject(
|
||||
environment,
|
||||
projectId,
|
||||
);
|
||||
await app.services.featureToggleServiceV2.createFeatureToggle(
|
||||
projectId,
|
||||
{
|
||||
type: 'Release',
|
||||
name: featureName,
|
||||
description: 'Feature for export',
|
||||
},
|
||||
userName,
|
||||
);
|
||||
await app.services.featureToggleServiceV2.createStrategy(
|
||||
{
|
||||
name: 'default',
|
||||
constraints: [
|
||||
{ contextName: 'userId', operator: 'IN', values: ['123'] },
|
||||
],
|
||||
parameters: {},
|
||||
},
|
||||
{
|
||||
projectId,
|
||||
featureName,
|
||||
environment,
|
||||
},
|
||||
userName,
|
||||
);
|
||||
await app.services.apiTokenService.createApiTokenWithProjects({
|
||||
username: apiTokenName,
|
||||
type: ApiTokenType.CLIENT,
|
||||
environment: environment,
|
||||
projects: [projectId],
|
||||
});
|
||||
|
||||
const data = await app.services.stateService.export({});
|
||||
await app.services.stateService.import({
|
||||
data,
|
||||
dropBeforeImport: true,
|
||||
keepExisting: false,
|
||||
userName: userName,
|
||||
});
|
||||
|
||||
const apiTokens = await app.services.apiTokenService.getAllTokens();
|
||||
|
||||
expect(apiTokens.length).toEqual(1);
|
||||
expect(apiTokens[0].username).toBe(apiTokenName);
|
||||
});
|
||||
|
||||
test(`should clean apitokens for not existing environment after import with drop`, async () => {
|
||||
const projectId = 'not-reimported-project';
|
||||
const environment = 'not-reimported-environment';
|
||||
const apiTokenName = 'dropped-token';
|
||||
|
||||
await db.stores.environmentStore.create({
|
||||
name: environment,
|
||||
type: 'test',
|
||||
});
|
||||
await db.stores.projectStore.create({
|
||||
name: projectId,
|
||||
id: projectId,
|
||||
description: 'Project for export',
|
||||
});
|
||||
await app.services.environmentService.addEnvironmentToProject(
|
||||
environment,
|
||||
projectId,
|
||||
);
|
||||
await app.services.apiTokenService.createApiTokenWithProjects({
|
||||
username: apiTokenName,
|
||||
type: ApiTokenType.CLIENT,
|
||||
environment: environment,
|
||||
projects: [projectId],
|
||||
});
|
||||
|
||||
await app.request
|
||||
.post('/api/admin/state/import?drop=true')
|
||||
.attach('file', 'src/test/examples/v3-minimal.json')
|
||||
.expect(202);
|
||||
|
||||
const apiTokens = await app.services.apiTokenService.getAllTokens();
|
||||
|
||||
console.log(apiTokens);
|
||||
|
||||
expect(apiTokens.length).toEqual(0);
|
||||
});
|
||||
|
@ -2,10 +2,7 @@ import dbInit, { ITestDb } from '../helpers/database-init';
|
||||
import getLogger from '../../fixtures/no-logger';
|
||||
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import {
|
||||
AccessService,
|
||||
ALL_PROJECTS,
|
||||
} from '../../../lib/services/access-service';
|
||||
import { AccessService } from '../../../lib/services/access-service';
|
||||
|
||||
import * as permissions from '../../../lib/types/permissions';
|
||||
import { RoleName } from '../../../lib/types/model';
|
||||
@ -14,6 +11,7 @@ import FeatureToggleService from '../../../lib/services/feature-toggle-service';
|
||||
import ProjectService from '../../../lib/services/project-service';
|
||||
import { createTestConfig } from '../../config/test-config';
|
||||
import { DEFAULT_PROJECT } from '../../../lib/types/project';
|
||||
import { ALL_PROJECTS } from '../../../lib/util/constants';
|
||||
import { SegmentService } from '../../../lib/services/segment-service';
|
||||
|
||||
let db: ITestDb;
|
||||
|
36
src/test/examples/v3-minimal.json
Normal file
36
src/test/examples/v3-minimal.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"version": 3,
|
||||
"projects": [
|
||||
{
|
||||
"id": "default",
|
||||
"name": "Default",
|
||||
"description": "Default project",
|
||||
"createdAt": "2022-05-19T18:28:31.927Z",
|
||||
"health": 100,
|
||||
"updatedAt": "2022-05-19T20:28:32.736Z"
|
||||
}
|
||||
],
|
||||
"environments": [
|
||||
{
|
||||
"name": "default",
|
||||
"type": "production",
|
||||
"sortOrder": 1,
|
||||
"enabled": false,
|
||||
"protected": true
|
||||
},
|
||||
{
|
||||
"name": "development",
|
||||
"type": "development",
|
||||
"sortOrder": 100,
|
||||
"enabled": true,
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"name": "production",
|
||||
"type": "production",
|
||||
"sortOrder": 200,
|
||||
"enabled": true,
|
||||
"protected": false
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user