mirror of
https://github.com/Unleash/unleash.git
synced 2025-03-18 00:19:49 +01:00
refactor: add schemas to strategy controller (#1744)
* refactor: avoid duplicate feature strategy operationIds * refactor: fix flaky feature tests * refactor: remove duplicate controller error handling * refactor: unify feature strategy schemas * refactor: add schemas to strategy controller
This commit is contained in:
parent
e013a72ddd
commit
ac3f076a31
@ -7,8 +7,8 @@ import { contextFieldSchema } from './spec/context-field-schema';
|
|||||||
import { contextFieldsSchema } from './spec/context-fields-schema';
|
import { contextFieldsSchema } from './spec/context-fields-schema';
|
||||||
import { createApiTokenSchema } from './spec/create-api-token-schema';
|
import { createApiTokenSchema } from './spec/create-api-token-schema';
|
||||||
import { createFeatureSchema } from './spec/create-feature-schema';
|
import { createFeatureSchema } from './spec/create-feature-schema';
|
||||||
import { createStrategySchema } from './spec/create-strategy-schema';
|
|
||||||
import { createUserSchema } from './spec/create-user-schema';
|
import { createUserSchema } from './spec/create-user-schema';
|
||||||
|
import { createFeatureStrategySchema } from './spec/create-feature-strategy-schema';
|
||||||
import { environmentSchema } from './spec/environment-schema';
|
import { environmentSchema } from './spec/environment-schema';
|
||||||
import { environmentsSchema } from './spec/environments-schema';
|
import { environmentsSchema } from './spec/environments-schema';
|
||||||
import { featureEnvironmentSchema } from './spec/feature-environment-schema';
|
import { featureEnvironmentSchema } from './spec/feature-environment-schema';
|
||||||
@ -40,14 +40,13 @@ import { projectsSchema } from './spec/projects-schema';
|
|||||||
import { roleSchema } from './spec/role-schema';
|
import { roleSchema } from './spec/role-schema';
|
||||||
import { sortOrderSchema } from './spec/sort-order-schema';
|
import { sortOrderSchema } from './spec/sort-order-schema';
|
||||||
import { splashSchema } from './spec/splash-schema';
|
import { splashSchema } from './spec/splash-schema';
|
||||||
import { strategySchema } from './spec/strategy-schema';
|
|
||||||
import { tagSchema } from './spec/tag-schema';
|
import { tagSchema } from './spec/tag-schema';
|
||||||
import { tagsSchema } from './spec/tags-schema';
|
import { tagsSchema } from './spec/tags-schema';
|
||||||
import { tagTypeSchema } from './spec/tag-type-schema';
|
import { tagTypeSchema } from './spec/tag-type-schema';
|
||||||
import { tagTypesSchema } from './spec/tag-types-schema';
|
import { tagTypesSchema } from './spec/tag-types-schema';
|
||||||
import { uiConfigSchema } from './spec/ui-config-schema';
|
import { uiConfigSchema } from './spec/ui-config-schema';
|
||||||
import { updateFeatureSchema } from './spec/update-feature-schema';
|
import { updateFeatureSchema } from './spec/update-feature-schema';
|
||||||
import { updateStrategySchema } from './spec/update-strategy-schema';
|
import { updateFeatureStrategySchema } from './spec/update-feature-strategy-schema';
|
||||||
import { updateApiTokenSchema } from './spec/update-api-token-schema';
|
import { updateApiTokenSchema } from './spec/update-api-token-schema';
|
||||||
import { updateTagTypeSchema } from './spec/update-tag-type-schema';
|
import { updateTagTypeSchema } from './spec/update-tag-type-schema';
|
||||||
import { upsertContextFieldSchema } from './spec/upsert-context-field-schema';
|
import { upsertContextFieldSchema } from './spec/upsert-context-field-schema';
|
||||||
@ -76,6 +75,9 @@ import { stateSchema } from './spec/state-schema';
|
|||||||
import { featureTagSchema } from './spec/feature-tag-schema';
|
import { featureTagSchema } from './spec/feature-tag-schema';
|
||||||
import { exportParametersSchema } from './spec/export-parameters-schema';
|
import { exportParametersSchema } from './spec/export-parameters-schema';
|
||||||
import { emailSchema } from './spec/email-schema';
|
import { emailSchema } from './spec/email-schema';
|
||||||
|
import { strategySchema } from './spec/strategy-schema';
|
||||||
|
import { strategiesSchema } from './spec/strategies-schema';
|
||||||
|
import { upsertStrategySchema } from './spec/upsert-strategy-schema';
|
||||||
|
|
||||||
// All schemas in `openapi/spec` should be listed here.
|
// All schemas in `openapi/spec` should be listed here.
|
||||||
export const schemas = {
|
export const schemas = {
|
||||||
@ -94,7 +96,7 @@ export const schemas = {
|
|||||||
contextFieldsSchema,
|
contextFieldsSchema,
|
||||||
createApiTokenSchema,
|
createApiTokenSchema,
|
||||||
createFeatureSchema,
|
createFeatureSchema,
|
||||||
createStrategySchema,
|
createFeatureStrategySchema,
|
||||||
createUserSchema,
|
createUserSchema,
|
||||||
emailSchema,
|
emailSchema,
|
||||||
environmentSchema,
|
environmentSchema,
|
||||||
@ -132,6 +134,7 @@ export const schemas = {
|
|||||||
sortOrderSchema,
|
sortOrderSchema,
|
||||||
splashSchema,
|
splashSchema,
|
||||||
stateSchema,
|
stateSchema,
|
||||||
|
strategiesSchema,
|
||||||
strategySchema,
|
strategySchema,
|
||||||
tagSchema,
|
tagSchema,
|
||||||
tagWithVersionSchema,
|
tagWithVersionSchema,
|
||||||
@ -141,10 +144,11 @@ export const schemas = {
|
|||||||
tokenUserSchema,
|
tokenUserSchema,
|
||||||
uiConfigSchema,
|
uiConfigSchema,
|
||||||
updateFeatureSchema,
|
updateFeatureSchema,
|
||||||
updateStrategySchema,
|
updateFeatureStrategySchema,
|
||||||
updateApiTokenSchema,
|
updateApiTokenSchema,
|
||||||
updateTagTypeSchema,
|
updateTagTypeSchema,
|
||||||
upsertContextFieldSchema,
|
upsertContextFieldSchema,
|
||||||
|
upsertStrategySchema,
|
||||||
validatePasswordSchema,
|
validatePasswordSchema,
|
||||||
validateTagTypeSchema,
|
validateTagTypeSchema,
|
||||||
updateUserSchema,
|
updateUserSchema,
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`featureEnvironmentSchema empty 1`] = `
|
||||||
|
Object {
|
||||||
|
"data": Object {},
|
||||||
|
"errors": Array [
|
||||||
|
Object {
|
||||||
|
"instancePath": "",
|
||||||
|
"keyword": "required",
|
||||||
|
"message": "must have required property 'name'",
|
||||||
|
"params": Object {
|
||||||
|
"missingProperty": "name",
|
||||||
|
},
|
||||||
|
"schemaPath": "#/required",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"schema": "#/components/schemas/featureEnvironmentSchema",
|
||||||
|
}
|
||||||
|
`;
|
@ -17,13 +17,13 @@ Object {
|
|||||||
},
|
},
|
||||||
"errors": Array [
|
"errors": Array [
|
||||||
Object {
|
Object {
|
||||||
"instancePath": "/strategies/0/constraints/0",
|
"instancePath": "/strategies/0",
|
||||||
"keyword": "required",
|
"keyword": "required",
|
||||||
"message": "must have required property 'operator'",
|
"message": "must have required property 'id'",
|
||||||
"params": Object {
|
"params": Object {
|
||||||
"missingProperty": "operator",
|
"missingProperty": "id",
|
||||||
},
|
},
|
||||||
"schemaPath": "#/components/schemas/constraintSchema/required",
|
"schemaPath": "#/required",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"schema": "#/components/schemas/featureSchema",
|
"schema": "#/components/schemas/featureSchema",
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`strategySchema 1`] = `
|
||||||
|
Object {
|
||||||
|
"data": Object {},
|
||||||
|
"errors": Array [
|
||||||
|
Object {
|
||||||
|
"instancePath": "",
|
||||||
|
"keyword": "required",
|
||||||
|
"message": "must have required property 'name'",
|
||||||
|
"params": Object {
|
||||||
|
"missingProperty": "name",
|
||||||
|
},
|
||||||
|
"schemaPath": "#/required",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"schema": "#/components/schemas/strategySchema",
|
||||||
|
}
|
||||||
|
`;
|
@ -2,8 +2,8 @@ import { FromSchema } from 'json-schema-to-ts';
|
|||||||
import { parametersSchema } from './parameters-schema';
|
import { parametersSchema } from './parameters-schema';
|
||||||
import { constraintSchema } from './constraint-schema';
|
import { constraintSchema } from './constraint-schema';
|
||||||
|
|
||||||
export const createStrategySchema = {
|
export const createFeatureStrategySchema = {
|
||||||
$id: '#/components/schemas/createStrategySchema',
|
$id: '#/components/schemas/createFeatureStrategySchema',
|
||||||
type: 'object',
|
type: 'object',
|
||||||
required: ['name'],
|
required: ['name'],
|
||||||
properties: {
|
properties: {
|
||||||
@ -31,4 +31,6 @@ export const createStrategySchema = {
|
|||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type CreateStrategySchema = FromSchema<typeof createStrategySchema>;
|
export type CreateFeatureStrategySchema = FromSchema<
|
||||||
|
typeof createFeatureStrategySchema
|
||||||
|
>;
|
30
src/lib/openapi/spec/feature-environment-schema.test.ts
Normal file
30
src/lib/openapi/spec/feature-environment-schema.test.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { validateSchema } from '../validate';
|
||||||
|
import { FeatureEnvironmentSchema } from './feature-environment-schema';
|
||||||
|
|
||||||
|
test('featureEnvironmentSchema', () => {
|
||||||
|
const data: FeatureEnvironmentSchema = {
|
||||||
|
name: '',
|
||||||
|
enabled: true,
|
||||||
|
strategies: [
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
featureName: '',
|
||||||
|
projectId: '',
|
||||||
|
environment: '',
|
||||||
|
strategyName: '',
|
||||||
|
constraints: [{ contextName: '', operator: 'IN' }],
|
||||||
|
parameters: { a: '' },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
validateSchema('#/components/schemas/featureEnvironmentSchema', data),
|
||||||
|
).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('featureEnvironmentSchema empty', () => {
|
||||||
|
expect(
|
||||||
|
validateSchema('#/components/schemas/featureEnvironmentSchema', {}),
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
@ -1,5 +1,4 @@
|
|||||||
import { FromSchema } from 'json-schema-to-ts';
|
import { FromSchema } from 'json-schema-to-ts';
|
||||||
import { featureStrategySchema } from './feature-strategy-schema';
|
|
||||||
import { constraintSchema } from './constraint-schema';
|
import { constraintSchema } from './constraint-schema';
|
||||||
import { parametersSchema } from './parameters-schema';
|
import { parametersSchema } from './parameters-schema';
|
||||||
|
|
||||||
@ -24,13 +23,55 @@ export const featureEnvironmentSchema = {
|
|||||||
strategies: {
|
strategies: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: {
|
items: {
|
||||||
$ref: '#/components/schemas/featureStrategySchema',
|
type: 'object',
|
||||||
|
additionalProperties: false,
|
||||||
|
required: [
|
||||||
|
'id',
|
||||||
|
'featureName',
|
||||||
|
'projectId',
|
||||||
|
'environment',
|
||||||
|
'strategyName',
|
||||||
|
'constraints',
|
||||||
|
'parameters',
|
||||||
|
],
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
featureName: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
projectId: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
environment: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
strategyName: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
sortOrder: {
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
createdAt: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'date-time',
|
||||||
|
},
|
||||||
|
constraints: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
$ref: '#/components/schemas/constraintSchema',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
parameters: {
|
||||||
|
$ref: '#/components/schemas/parametersSchema',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
schemas: {
|
schemas: {
|
||||||
featureStrategySchema,
|
|
||||||
constraintSchema,
|
constraintSchema,
|
||||||
parametersSchema,
|
parametersSchema,
|
||||||
},
|
},
|
||||||
|
@ -6,6 +6,7 @@ test('featureSchema', () => {
|
|||||||
name: 'a',
|
name: 'a',
|
||||||
strategies: [
|
strategies: [
|
||||||
{
|
{
|
||||||
|
id: 'a',
|
||||||
name: 'a',
|
name: 'a',
|
||||||
constraints: [
|
constraints: [
|
||||||
{
|
{
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { FromSchema } from 'json-schema-to-ts';
|
import { FromSchema } from 'json-schema-to-ts';
|
||||||
import { variantSchema } from './variant-schema';
|
import { variantSchema } from './variant-schema';
|
||||||
import { strategySchema } from './strategy-schema';
|
|
||||||
import { constraintSchema } from './constraint-schema';
|
import { constraintSchema } from './constraint-schema';
|
||||||
import { overrideSchema } from './override-schema';
|
import { overrideSchema } from './override-schema';
|
||||||
import { parametersSchema } from './parameters-schema';
|
import { parametersSchema } from './parameters-schema';
|
||||||
import { environmentSchema } from './environment-schema';
|
import { environmentSchema } from './environment-schema';
|
||||||
|
import { featureStrategySchema } from './feature-strategy-schema';
|
||||||
|
|
||||||
export const featureSchema = {
|
export const featureSchema = {
|
||||||
$id: '#/components/schemas/featureSchema',
|
$id: '#/components/schemas/featureSchema',
|
||||||
@ -55,7 +55,7 @@ export const featureSchema = {
|
|||||||
strategies: {
|
strategies: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: {
|
items: {
|
||||||
$ref: '#/components/schemas/strategySchema',
|
$ref: '#/components/schemas/featureStrategySchema',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
variants: {
|
variants: {
|
||||||
@ -71,7 +71,7 @@ export const featureSchema = {
|
|||||||
environmentSchema,
|
environmentSchema,
|
||||||
overrideSchema,
|
overrideSchema,
|
||||||
parametersSchema,
|
parametersSchema,
|
||||||
strategySchema,
|
featureStrategySchema,
|
||||||
variantSchema,
|
variantSchema,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -6,14 +6,7 @@ export const featureStrategySchema = {
|
|||||||
$id: '#/components/schemas/featureStrategySchema',
|
$id: '#/components/schemas/featureStrategySchema',
|
||||||
type: 'object',
|
type: 'object',
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
required: [
|
required: ['name', 'id'],
|
||||||
'id',
|
|
||||||
'featureName',
|
|
||||||
'strategyName',
|
|
||||||
'constraints',
|
|
||||||
'parameters',
|
|
||||||
'environment',
|
|
||||||
],
|
|
||||||
properties: {
|
properties: {
|
||||||
id: {
|
id: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
@ -21,23 +14,6 @@ export const featureStrategySchema = {
|
|||||||
name: {
|
name: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
createdAt: {
|
|
||||||
type: 'string',
|
|
||||||
format: 'date-time',
|
|
||||||
nullable: true,
|
|
||||||
},
|
|
||||||
featureName: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
projectId: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
environment: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
strategyName: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
sortOrder: {
|
sortOrder: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
},
|
},
|
||||||
|
@ -4,7 +4,7 @@ import { parametersSchema } from './parameters-schema';
|
|||||||
import { variantSchema } from './variant-schema';
|
import { variantSchema } from './variant-schema';
|
||||||
import { overrideSchema } from './override-schema';
|
import { overrideSchema } from './override-schema';
|
||||||
import { constraintSchema } from './constraint-schema';
|
import { constraintSchema } from './constraint-schema';
|
||||||
import { strategySchema } from './strategy-schema';
|
import { featureStrategySchema } from './feature-strategy-schema';
|
||||||
import { environmentSchema } from './environment-schema';
|
import { environmentSchema } from './environment-schema';
|
||||||
|
|
||||||
export const featuresSchema = {
|
export const featuresSchema = {
|
||||||
@ -30,7 +30,7 @@ export const featuresSchema = {
|
|||||||
featureSchema,
|
featureSchema,
|
||||||
overrideSchema,
|
overrideSchema,
|
||||||
parametersSchema,
|
parametersSchema,
|
||||||
strategySchema,
|
featureStrategySchema,
|
||||||
variantSchema,
|
variantSchema,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -2,7 +2,7 @@ import { FromSchema } from 'json-schema-to-ts';
|
|||||||
import { parametersSchema } from './parameters-schema';
|
import { parametersSchema } from './parameters-schema';
|
||||||
import { variantSchema } from './variant-schema';
|
import { variantSchema } from './variant-schema';
|
||||||
import { overrideSchema } from './override-schema';
|
import { overrideSchema } from './override-schema';
|
||||||
import { strategySchema } from './strategy-schema';
|
import { featureStrategySchema } from './feature-strategy-schema';
|
||||||
import { featureSchema } from './feature-schema';
|
import { featureSchema } from './feature-schema';
|
||||||
import { constraintSchema } from './constraint-schema';
|
import { constraintSchema } from './constraint-schema';
|
||||||
import { environmentSchema } from './environment-schema';
|
import { environmentSchema } from './environment-schema';
|
||||||
@ -53,7 +53,7 @@ export const healthOverviewSchema = {
|
|||||||
featureSchema,
|
featureSchema,
|
||||||
overrideSchema,
|
overrideSchema,
|
||||||
parametersSchema,
|
parametersSchema,
|
||||||
strategySchema,
|
featureStrategySchema,
|
||||||
variantSchema,
|
variantSchema,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { FromSchema } from 'json-schema-to-ts';
|
import { FromSchema } from 'json-schema-to-ts';
|
||||||
import { featureSchema } from './feature-schema';
|
import { featureSchema } from './feature-schema';
|
||||||
import { strategySchema } from './strategy-schema';
|
|
||||||
import { tagSchema } from './tag-schema';
|
import { tagSchema } from './tag-schema';
|
||||||
import { tagTypeSchema } from './tag-type-schema';
|
import { tagTypeSchema } from './tag-type-schema';
|
||||||
import { featureTagSchema } from './feature-tag-schema';
|
import { featureTagSchema } from './feature-tag-schema';
|
||||||
@ -10,6 +9,7 @@ import { featureEnvironmentSchema } from './feature-environment-schema';
|
|||||||
import { environmentSchema } from './environment-schema';
|
import { environmentSchema } from './environment-schema';
|
||||||
import { segmentSchema } from './segment-schema';
|
import { segmentSchema } from './segment-schema';
|
||||||
import { featureStrategySegmentSchema } from './feature-strategy-segment-schema';
|
import { featureStrategySegmentSchema } from './feature-strategy-segment-schema';
|
||||||
|
import { strategySchema } from './strategy-schema';
|
||||||
|
|
||||||
export const stateSchema = {
|
export const stateSchema = {
|
||||||
$id: '#/components/schemas/stateSchema',
|
$id: '#/components/schemas/stateSchema',
|
||||||
@ -90,7 +90,6 @@ export const stateSchema = {
|
|||||||
components: {
|
components: {
|
||||||
schemas: {
|
schemas: {
|
||||||
featureSchema,
|
featureSchema,
|
||||||
strategySchema,
|
|
||||||
tagSchema,
|
tagSchema,
|
||||||
tagTypeSchema,
|
tagTypeSchema,
|
||||||
featureTagSchema,
|
featureTagSchema,
|
||||||
@ -100,6 +99,7 @@ export const stateSchema = {
|
|||||||
environmentSchema,
|
environmentSchema,
|
||||||
segmentSchema,
|
segmentSchema,
|
||||||
featureStrategySegmentSchema,
|
featureStrategySegmentSchema,
|
||||||
|
strategySchema,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
27
src/lib/openapi/spec/strategies-schema.ts
Normal file
27
src/lib/openapi/spec/strategies-schema.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { FromSchema } from 'json-schema-to-ts';
|
||||||
|
import { strategySchema } from './strategy-schema';
|
||||||
|
|
||||||
|
export const strategiesSchema = {
|
||||||
|
$id: '#/components/schemas/strategiesSchema',
|
||||||
|
type: 'object',
|
||||||
|
additionalProperties: false,
|
||||||
|
required: ['version', 'strategies'],
|
||||||
|
properties: {
|
||||||
|
version: {
|
||||||
|
type: 'integer',
|
||||||
|
},
|
||||||
|
strategies: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
$ref: '#/components/schemas/strategySchema',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
schemas: {
|
||||||
|
strategySchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type StrategiesSchema = FromSchema<typeof strategiesSchema>;
|
28
src/lib/openapi/spec/strategy-schema.test.ts
Normal file
28
src/lib/openapi/spec/strategy-schema.test.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { validateSchema } from '../validate';
|
||||||
|
import { StrategySchema } from './strategy-schema';
|
||||||
|
|
||||||
|
test('strategySchema', () => {
|
||||||
|
const data: StrategySchema = {
|
||||||
|
description: '',
|
||||||
|
name: '',
|
||||||
|
displayName: '',
|
||||||
|
editable: false,
|
||||||
|
deprecated: false,
|
||||||
|
parameters: [
|
||||||
|
{
|
||||||
|
name: '',
|
||||||
|
type: '',
|
||||||
|
description: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
validateSchema('#/components/schemas/strategySchema', data),
|
||||||
|
).toBeUndefined();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
validateSchema('#/components/schemas/strategySchema', {}),
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
@ -1,38 +1,57 @@
|
|||||||
import { FromSchema } from 'json-schema-to-ts';
|
import { FromSchema } from 'json-schema-to-ts';
|
||||||
import { constraintSchema } from './constraint-schema';
|
|
||||||
import { parametersSchema } from './parameters-schema';
|
|
||||||
|
|
||||||
export const strategySchema = {
|
export const strategySchema = {
|
||||||
$id: '#/components/schemas/strategySchema',
|
$id: '#/components/schemas/strategySchema',
|
||||||
type: 'object',
|
type: 'object',
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
required: ['name'],
|
required: [
|
||||||
|
'name',
|
||||||
|
'displayName',
|
||||||
|
'description',
|
||||||
|
'editable',
|
||||||
|
'deprecated',
|
||||||
|
'parameters',
|
||||||
|
],
|
||||||
properties: {
|
properties: {
|
||||||
id: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
name: {
|
name: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
sortOrder: {
|
displayName: {
|
||||||
type: 'number',
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
},
|
},
|
||||||
constraints: {
|
description: {
|
||||||
type: 'array',
|
type: 'string',
|
||||||
items: {
|
},
|
||||||
$ref: '#/components/schemas/constraintSchema',
|
editable: {
|
||||||
},
|
type: 'boolean',
|
||||||
|
},
|
||||||
|
deprecated: {
|
||||||
|
type: 'boolean',
|
||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
$ref: '#/components/schemas/parametersSchema',
|
type: 'array',
|
||||||
},
|
items: {
|
||||||
},
|
type: 'object',
|
||||||
components: {
|
additionalProperties: false,
|
||||||
schemas: {
|
properties: {
|
||||||
constraintSchema,
|
name: {
|
||||||
parametersSchema,
|
type: 'string',
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
required: {
|
||||||
|
type: 'boolean',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
components: {},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type StrategySchema = FromSchema<typeof strategySchema>;
|
export type StrategySchema = FromSchema<typeof strategySchema>;
|
||||||
|
35
src/lib/openapi/spec/update-feature-strategy-schema.ts
Normal file
35
src/lib/openapi/spec/update-feature-strategy-schema.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { FromSchema } from 'json-schema-to-ts';
|
||||||
|
import { parametersSchema } from './parameters-schema';
|
||||||
|
import { constraintSchema } from './constraint-schema';
|
||||||
|
|
||||||
|
export const updateFeatureStrategySchema = {
|
||||||
|
$id: '#/components/schemas/updateFeatureStrategySchema',
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
sortOrder: {
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
constraints: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
$ref: '#/components/schemas/constraintSchema',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
parameters: {
|
||||||
|
$ref: '#/components/schemas/parametersSchema',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
schemas: {
|
||||||
|
constraintSchema,
|
||||||
|
parametersSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type UpdateFeatureStrategySchema = FromSchema<
|
||||||
|
typeof updateFeatureStrategySchema
|
||||||
|
>;
|
@ -1,11 +0,0 @@
|
|||||||
import { FromSchema } from 'json-schema-to-ts';
|
|
||||||
import { strategySchema } from './strategy-schema';
|
|
||||||
|
|
||||||
export const updateStrategySchema = {
|
|
||||||
...strategySchema,
|
|
||||||
$id: '#/components/schemas/updateStrategySchema',
|
|
||||||
required: [],
|
|
||||||
components: {},
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export type UpdateStrategySchema = FromSchema<typeof updateStrategySchema>;
|
|
41
src/lib/openapi/spec/upsert-strategy-schema.ts
Normal file
41
src/lib/openapi/spec/upsert-strategy-schema.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { FromSchema } from 'json-schema-to-ts';
|
||||||
|
|
||||||
|
export const upsertStrategySchema = {
|
||||||
|
$id: '#/components/schemas/upsertStrategySchema',
|
||||||
|
type: 'object',
|
||||||
|
required: ['name'],
|
||||||
|
properties: {
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
editable: {
|
||||||
|
type: 'boolean',
|
||||||
|
},
|
||||||
|
parameters: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
required: {
|
||||||
|
type: 'boolean',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type UpsertStrategySchema = FromSchema<typeof upsertStrategySchema>;
|
@ -22,15 +22,15 @@ import {
|
|||||||
featureSchema,
|
featureSchema,
|
||||||
FeatureSchema,
|
FeatureSchema,
|
||||||
} from '../../../openapi/spec/feature-schema';
|
} from '../../../openapi/spec/feature-schema';
|
||||||
import { StrategySchema } from '../../../openapi/spec/strategy-schema';
|
import { FeatureStrategySchema } from '../../../openapi/spec/feature-strategy-schema';
|
||||||
import { ParametersSchema } from '../../../openapi/spec/parameters-schema';
|
import { ParametersSchema } from '../../../openapi/spec/parameters-schema';
|
||||||
import {
|
import {
|
||||||
featuresSchema,
|
featuresSchema,
|
||||||
FeaturesSchema,
|
FeaturesSchema,
|
||||||
} from '../../../openapi/spec/features-schema';
|
} from '../../../openapi/spec/features-schema';
|
||||||
import { UpdateFeatureSchema } from '../../../openapi/spec/update-feature-schema';
|
import { UpdateFeatureSchema } from '../../../openapi/spec/update-feature-schema';
|
||||||
import { UpdateStrategySchema } from '../../../openapi/spec/update-strategy-schema';
|
import { UpdateFeatureStrategySchema } from '../../../openapi/spec/update-feature-strategy-schema';
|
||||||
import { CreateStrategySchema } from '../../../openapi/spec/create-strategy-schema';
|
import { CreateFeatureStrategySchema } from '../../../openapi/spec/create-feature-strategy-schema';
|
||||||
import { serializeDates } from '../../../types/serialize-dates';
|
import { serializeDates } from '../../../types/serialize-dates';
|
||||||
import { OpenApiService } from '../../../services/openapi-service';
|
import { OpenApiService } from '../../../services/openapi-service';
|
||||||
import { createRequestSchema, createResponseSchema } from '../../../openapi';
|
import { createRequestSchema, createResponseSchema } from '../../../openapi';
|
||||||
@ -133,13 +133,15 @@ export default class ProjectFeaturesController extends Controller {
|
|||||||
this.route({
|
this.route({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
path: PATH_STRATEGIES,
|
path: PATH_STRATEGIES,
|
||||||
handler: this.getStrategies,
|
handler: this.getFeatureStrategies,
|
||||||
permission: NONE,
|
permission: NONE,
|
||||||
middleware: [
|
middleware: [
|
||||||
openApiService.validPath({
|
openApiService.validPath({
|
||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
operationId: 'getStrategies',
|
operationId: 'getFeatureStrategies',
|
||||||
responses: { 200: createResponseSchema('strategySchema') },
|
responses: {
|
||||||
|
200: createResponseSchema('featureStrategySchema'),
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -147,13 +149,15 @@ export default class ProjectFeaturesController extends Controller {
|
|||||||
this.route({
|
this.route({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
path: PATH_STRATEGIES,
|
path: PATH_STRATEGIES,
|
||||||
handler: this.addStrategy,
|
handler: this.addFeatureStrategy,
|
||||||
permission: CREATE_FEATURE_STRATEGY,
|
permission: CREATE_FEATURE_STRATEGY,
|
||||||
middleware: [
|
middleware: [
|
||||||
openApiService.validPath({
|
openApiService.validPath({
|
||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
operationId: 'addStrategy',
|
operationId: 'addFeatureStrategy',
|
||||||
requestBody: createRequestSchema('createStrategySchema'),
|
requestBody: createRequestSchema(
|
||||||
|
'createFeatureStrategySchema',
|
||||||
|
),
|
||||||
responses: {
|
responses: {
|
||||||
200: createResponseSchema('featureStrategySchema'),
|
200: createResponseSchema('featureStrategySchema'),
|
||||||
},
|
},
|
||||||
@ -164,12 +168,12 @@ export default class ProjectFeaturesController extends Controller {
|
|||||||
this.route({
|
this.route({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
path: PATH_STRATEGY,
|
path: PATH_STRATEGY,
|
||||||
handler: this.getStrategy,
|
handler: this.getFeatureStrategy,
|
||||||
permission: NONE,
|
permission: NONE,
|
||||||
middleware: [
|
middleware: [
|
||||||
openApiService.validPath({
|
openApiService.validPath({
|
||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
operationId: 'getStrategy',
|
operationId: 'getFeatureStrategy',
|
||||||
responses: {
|
responses: {
|
||||||
200: createResponseSchema('featureStrategySchema'),
|
200: createResponseSchema('featureStrategySchema'),
|
||||||
},
|
},
|
||||||
@ -180,28 +184,31 @@ export default class ProjectFeaturesController extends Controller {
|
|||||||
this.route({
|
this.route({
|
||||||
method: 'put',
|
method: 'put',
|
||||||
path: PATH_STRATEGY,
|
path: PATH_STRATEGY,
|
||||||
handler: this.updateStrategy,
|
handler: this.updateFeatureStrategy,
|
||||||
permission: UPDATE_FEATURE_STRATEGY,
|
permission: UPDATE_FEATURE_STRATEGY,
|
||||||
middleware: [
|
middleware: [
|
||||||
openApiService.validPath({
|
openApiService.validPath({
|
||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
operationId: 'updateStrategy',
|
operationId: 'updateFeatureStrategy',
|
||||||
requestBody: createRequestSchema('updateStrategySchema'),
|
requestBody: createRequestSchema(
|
||||||
|
'updateFeatureStrategySchema',
|
||||||
|
),
|
||||||
responses: {
|
responses: {
|
||||||
200: createResponseSchema('featureStrategySchema'),
|
200: createResponseSchema('featureStrategySchema'),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
this.route({
|
this.route({
|
||||||
method: 'patch',
|
method: 'patch',
|
||||||
path: PATH_STRATEGY,
|
path: PATH_STRATEGY,
|
||||||
handler: this.patchStrategy,
|
handler: this.patchFeatureStrategy,
|
||||||
permission: UPDATE_FEATURE_STRATEGY,
|
permission: UPDATE_FEATURE_STRATEGY,
|
||||||
middleware: [
|
middleware: [
|
||||||
openApiService.validPath({
|
openApiService.validPath({
|
||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
operationId: 'patchStrategy',
|
operationId: 'patchFeatureStrategy',
|
||||||
requestBody: createRequestSchema('patchesSchema'),
|
requestBody: createRequestSchema('patchesSchema'),
|
||||||
responses: {
|
responses: {
|
||||||
200: createResponseSchema('featureStrategySchema'),
|
200: createResponseSchema('featureStrategySchema'),
|
||||||
@ -213,11 +220,11 @@ export default class ProjectFeaturesController extends Controller {
|
|||||||
method: 'delete',
|
method: 'delete',
|
||||||
path: PATH_STRATEGY,
|
path: PATH_STRATEGY,
|
||||||
acceptAnyContentType: true,
|
acceptAnyContentType: true,
|
||||||
handler: this.deleteStrategy,
|
handler: this.deleteFeatureStrategy,
|
||||||
permission: DELETE_FEATURE_STRATEGY,
|
permission: DELETE_FEATURE_STRATEGY,
|
||||||
middleware: [
|
middleware: [
|
||||||
openApiService.validPath({
|
openApiService.validPath({
|
||||||
operationId: 'deleteStrategy',
|
operationId: 'deleteFeatureStrategy',
|
||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
responses: { 200: emptyResponse },
|
responses: { 200: emptyResponse },
|
||||||
}),
|
}),
|
||||||
@ -516,9 +523,13 @@ export default class ProjectFeaturesController extends Controller {
|
|||||||
res.status(200).end();
|
res.status(200).end();
|
||||||
}
|
}
|
||||||
|
|
||||||
async addStrategy(
|
async addFeatureStrategy(
|
||||||
req: IAuthRequest<FeatureStrategyParams, any, CreateStrategySchema>,
|
req: IAuthRequest<
|
||||||
res: Response<StrategySchema>,
|
FeatureStrategyParams,
|
||||||
|
any,
|
||||||
|
CreateFeatureStrategySchema
|
||||||
|
>,
|
||||||
|
res: Response<FeatureStrategySchema>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { projectId, featureName, environment } = req.params;
|
const { projectId, featureName, environment } = req.params;
|
||||||
const userName = extractUsername(req);
|
const userName = extractUsername(req);
|
||||||
@ -530,9 +541,9 @@ export default class ProjectFeaturesController extends Controller {
|
|||||||
res.status(200).json(strategy);
|
res.status(200).json(strategy);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getStrategies(
|
async getFeatureStrategies(
|
||||||
req: Request<FeatureStrategyParams, any, any, any>,
|
req: Request<FeatureStrategyParams, any, any, any>,
|
||||||
res: Response<StrategySchema[]>,
|
res: Response<FeatureStrategySchema[]>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { projectId, featureName, environment } = req.params;
|
const { projectId, featureName, environment } = req.params;
|
||||||
const featureStrategies =
|
const featureStrategies =
|
||||||
@ -544,9 +555,9 @@ export default class ProjectFeaturesController extends Controller {
|
|||||||
res.status(200).json(featureStrategies);
|
res.status(200).json(featureStrategies);
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateStrategy(
|
async updateFeatureStrategy(
|
||||||
req: IAuthRequest<StrategyIdParams, any, UpdateStrategySchema>,
|
req: IAuthRequest<StrategyIdParams, any, UpdateFeatureStrategySchema>,
|
||||||
res: Response<StrategySchema>,
|
res: Response<FeatureStrategySchema>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { strategyId, environment, projectId, featureName } = req.params;
|
const { strategyId, environment, projectId, featureName } = req.params;
|
||||||
const userName = extractUsername(req);
|
const userName = extractUsername(req);
|
||||||
@ -559,9 +570,9 @@ export default class ProjectFeaturesController extends Controller {
|
|||||||
res.status(200).json(updatedStrategy);
|
res.status(200).json(updatedStrategy);
|
||||||
}
|
}
|
||||||
|
|
||||||
async patchStrategy(
|
async patchFeatureStrategy(
|
||||||
req: IAuthRequest<StrategyIdParams, any, Operation[], any>,
|
req: IAuthRequest<StrategyIdParams, any, Operation[], any>,
|
||||||
res: Response<StrategySchema>,
|
res: Response<FeatureStrategySchema>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { strategyId, projectId, environment, featureName } = req.params;
|
const { strategyId, projectId, environment, featureName } = req.params;
|
||||||
const userName = extractUsername(req);
|
const userName = extractUsername(req);
|
||||||
@ -577,9 +588,9 @@ export default class ProjectFeaturesController extends Controller {
|
|||||||
res.status(200).json(updatedStrategy);
|
res.status(200).json(updatedStrategy);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getStrategy(
|
async getFeatureStrategy(
|
||||||
req: IAuthRequest<StrategyIdParams, any, any, any>,
|
req: IAuthRequest<StrategyIdParams, any, any, any>,
|
||||||
res: Response<StrategySchema>,
|
res: Response<FeatureStrategySchema>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
this.logger.info('Getting strategy');
|
this.logger.info('Getting strategy');
|
||||||
const { strategyId } = req.params;
|
const { strategyId } = req.params;
|
||||||
@ -588,7 +599,7 @@ export default class ProjectFeaturesController extends Controller {
|
|||||||
res.status(200).json(strategy);
|
res.status(200).json(strategy);
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteStrategy(
|
async deleteFeatureStrategy(
|
||||||
req: IAuthRequest<StrategyIdParams, any, any, any>,
|
req: IAuthRequest<StrategyIdParams, any, any, any>,
|
||||||
res: Response<void>,
|
res: Response<void>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
@ -612,7 +623,7 @@ export default class ProjectFeaturesController extends Controller {
|
|||||||
{ name: string; value: string | number },
|
{ name: string; value: string | number },
|
||||||
any
|
any
|
||||||
>,
|
>,
|
||||||
res: Response<StrategySchema>,
|
res: Response<FeatureStrategySchema>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { strategyId, environment, projectId, featureName } = req.params;
|
const { strategyId, environment, projectId, featureName } = req.params;
|
||||||
const userName = extractUsername(req);
|
const userName = extractUsername(req);
|
||||||
|
@ -54,8 +54,8 @@ test('require a name when creating a new strategy', async () => {
|
|||||||
.send({})
|
.send({})
|
||||||
.expect(400)
|
.expect(400)
|
||||||
.expect((res) => {
|
.expect((res) => {
|
||||||
expect(res.body.details[0].message === '"name" is required').toBe(
|
expect(res.body.validation[0].message).toEqual(
|
||||||
true,
|
"should have required property 'name'",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2,18 +2,28 @@ import { IUnleashConfig } from '../../types/option';
|
|||||||
import { IUnleashServices } from '../../types/services';
|
import { IUnleashServices } from '../../types/services';
|
||||||
import StrategyService from '../../services/strategy-service';
|
import StrategyService from '../../services/strategy-service';
|
||||||
import { Logger } from '../../logger';
|
import { Logger } from '../../logger';
|
||||||
|
|
||||||
import Controller from '../controller';
|
import Controller from '../controller';
|
||||||
|
|
||||||
import { extractUsername } from '../../util/extract-user';
|
import { extractUsername } from '../../util/extract-user';
|
||||||
import { handleErrors } from '../util';
|
|
||||||
import {
|
import {
|
||||||
DELETE_STRATEGY,
|
DELETE_STRATEGY,
|
||||||
CREATE_STRATEGY,
|
CREATE_STRATEGY,
|
||||||
UPDATE_STRATEGY,
|
UPDATE_STRATEGY,
|
||||||
|
NONE,
|
||||||
} from '../../types/permissions';
|
} from '../../types/permissions';
|
||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
import { IAuthRequest } from '../unleash-types';
|
import { IAuthRequest } from '../unleash-types';
|
||||||
|
import { OpenApiService } from '../../services/openapi-service';
|
||||||
|
import { emptyResponse } from '../../openapi/spec/empty-response';
|
||||||
|
import { createRequestSchema, createResponseSchema } from '../../openapi';
|
||||||
|
import {
|
||||||
|
strategySchema,
|
||||||
|
StrategySchema,
|
||||||
|
} from '../../openapi/spec/strategy-schema';
|
||||||
|
import {
|
||||||
|
strategiesSchema,
|
||||||
|
StrategiesSchema,
|
||||||
|
} from '../../openapi/spec/strategies-schema';
|
||||||
|
import { UpsertStrategySchema } from '../../openapi/spec/upsert-strategy-schema';
|
||||||
|
|
||||||
const version = 1;
|
const version = 1;
|
||||||
|
|
||||||
@ -22,112 +32,210 @@ class StrategyController extends Controller {
|
|||||||
|
|
||||||
private strategyService: StrategyService;
|
private strategyService: StrategyService;
|
||||||
|
|
||||||
|
private openApiService: OpenApiService;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
config: IUnleashConfig,
|
config: IUnleashConfig,
|
||||||
{ strategyService }: Pick<IUnleashServices, 'strategyService'>,
|
{
|
||||||
|
strategyService,
|
||||||
|
openApiService,
|
||||||
|
}: Pick<IUnleashServices, 'strategyService' | 'openApiService'>,
|
||||||
) {
|
) {
|
||||||
super(config);
|
super(config);
|
||||||
this.logger = config.getLogger('/admin-api/strategy.js');
|
this.logger = config.getLogger('/admin-api/strategy.js');
|
||||||
this.strategyService = strategyService;
|
this.strategyService = strategyService;
|
||||||
|
this.openApiService = openApiService;
|
||||||
|
|
||||||
this.get('/', this.getAllStrategies);
|
this.route({
|
||||||
this.get('/:name', this.getStrategy);
|
method: 'get',
|
||||||
this.delete('/:name', this.removeStrategy, DELETE_STRATEGY);
|
path: '',
|
||||||
this.post('/', this.createStrategy, CREATE_STRATEGY);
|
handler: this.getAllStrategies,
|
||||||
this.put('/:strategyName', this.updateStrategy, UPDATE_STRATEGY);
|
permission: NONE,
|
||||||
this.post(
|
middleware: [
|
||||||
'/:strategyName/deprecate',
|
openApiService.validPath({
|
||||||
this.deprecateStrategy,
|
tags: ['admin'],
|
||||||
UPDATE_STRATEGY,
|
operationId: 'getAllStrategies',
|
||||||
);
|
responses: {
|
||||||
this.post(
|
200: createResponseSchema('strategiesSchema'),
|
||||||
'/:strategyName/reactivate',
|
},
|
||||||
this.reactivateStrategy,
|
}),
|
||||||
UPDATE_STRATEGY,
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
this.route({
|
||||||
|
method: 'get',
|
||||||
|
path: '/:name',
|
||||||
|
handler: this.getStrategy,
|
||||||
|
permission: NONE,
|
||||||
|
middleware: [
|
||||||
|
openApiService.validPath({
|
||||||
|
tags: ['admin'],
|
||||||
|
operationId: 'getStrategy',
|
||||||
|
responses: { 200: createResponseSchema('strategySchema') },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
this.route({
|
||||||
|
method: 'delete',
|
||||||
|
path: '/:name',
|
||||||
|
handler: this.removeStrategy,
|
||||||
|
permission: DELETE_STRATEGY,
|
||||||
|
acceptAnyContentType: true,
|
||||||
|
middleware: [
|
||||||
|
openApiService.validPath({
|
||||||
|
tags: ['admin'],
|
||||||
|
operationId: 'removeStrategy',
|
||||||
|
responses: { 200: emptyResponse },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
this.route({
|
||||||
|
method: 'post',
|
||||||
|
path: '',
|
||||||
|
handler: this.createStrategy,
|
||||||
|
permission: CREATE_STRATEGY,
|
||||||
|
middleware: [
|
||||||
|
openApiService.validPath({
|
||||||
|
tags: ['admin'],
|
||||||
|
operationId: 'createStrategy',
|
||||||
|
requestBody: createRequestSchema('upsertStrategySchema'),
|
||||||
|
responses: { 201: emptyResponse },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
this.route({
|
||||||
|
method: 'put',
|
||||||
|
path: '/:strategyName',
|
||||||
|
handler: this.updateStrategy,
|
||||||
|
permission: UPDATE_STRATEGY,
|
||||||
|
middleware: [
|
||||||
|
openApiService.validPath({
|
||||||
|
tags: ['admin'],
|
||||||
|
operationId: 'updateStrategy',
|
||||||
|
requestBody: createRequestSchema('upsertStrategySchema'),
|
||||||
|
responses: { 200: emptyResponse },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
this.route({
|
||||||
|
method: 'post',
|
||||||
|
path: '/:strategyName/deprecate',
|
||||||
|
handler: this.deprecateStrategy,
|
||||||
|
permission: UPDATE_STRATEGY,
|
||||||
|
acceptAnyContentType: true,
|
||||||
|
middleware: [
|
||||||
|
openApiService.validPath({
|
||||||
|
tags: ['admin'],
|
||||||
|
operationId: 'deprecateStrategy',
|
||||||
|
responses: { 200: emptyResponse },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
this.route({
|
||||||
|
method: 'post',
|
||||||
|
path: '/:strategyName/reactivate',
|
||||||
|
handler: this.reactivateStrategy,
|
||||||
|
permission: UPDATE_STRATEGY,
|
||||||
|
acceptAnyContentType: true,
|
||||||
|
middleware: [
|
||||||
|
openApiService.validPath({
|
||||||
|
tags: ['admin'],
|
||||||
|
operationId: 'reactivateStrategy',
|
||||||
|
responses: { 200: emptyResponse },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAllStrategies(
|
||||||
|
req: Request,
|
||||||
|
res: Response<StrategiesSchema>,
|
||||||
|
): Promise<void> {
|
||||||
|
const strategies = await this.strategyService.getStrategies();
|
||||||
|
|
||||||
|
this.openApiService.respondWithValidation(
|
||||||
|
200,
|
||||||
|
res,
|
||||||
|
strategiesSchema.$id,
|
||||||
|
{ version, strategies },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAllStrategies(req: Request, res: Response): Promise<void> {
|
async getStrategy(
|
||||||
try {
|
req: Request,
|
||||||
const strategies = await this.strategyService.getStrategies();
|
res: Response<StrategySchema>,
|
||||||
res.json({ version, strategies });
|
): Promise<void> {
|
||||||
} catch (err) {
|
const strategy = await this.strategyService.getStrategy(
|
||||||
handleErrors(res, this.logger, err);
|
req.params.name,
|
||||||
}
|
);
|
||||||
}
|
|
||||||
|
|
||||||
async getStrategy(req: Request, res: Response): Promise<void> {
|
this.openApiService.respondWithValidation(
|
||||||
try {
|
200,
|
||||||
const { name } = req.params;
|
res,
|
||||||
const strategy = await this.strategyService.getStrategy(name);
|
strategySchema.$id,
|
||||||
res.json(strategy).end();
|
strategy,
|
||||||
} catch (err) {
|
);
|
||||||
res.status(404).json({ error: 'Could not find strategy' });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeStrategy(req: IAuthRequest, res: Response): Promise<void> {
|
async removeStrategy(req: IAuthRequest, res: Response): Promise<void> {
|
||||||
const strategyName = req.params.name;
|
const strategyName = req.params.name;
|
||||||
const userName = extractUsername(req);
|
const userName = extractUsername(req);
|
||||||
|
|
||||||
try {
|
await this.strategyService.removeStrategy(strategyName, userName);
|
||||||
await this.strategyService.removeStrategy(strategyName, userName);
|
res.status(200).end();
|
||||||
res.status(200).end();
|
|
||||||
} catch (error) {
|
|
||||||
handleErrors(res, this.logger, error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async createStrategy(req: IAuthRequest, res: Response): Promise<void> {
|
async createStrategy(
|
||||||
|
req: IAuthRequest<unknown, UpsertStrategySchema>,
|
||||||
|
res: Response<void>,
|
||||||
|
): Promise<void> {
|
||||||
const userName = extractUsername(req);
|
const userName = extractUsername(req);
|
||||||
try {
|
|
||||||
await this.strategyService.createStrategy(req.body, userName);
|
await this.strategyService.createStrategy(req.body, userName);
|
||||||
res.status(201).end();
|
res.status(201).end();
|
||||||
} catch (error) {
|
|
||||||
handleErrors(res, this.logger, error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateStrategy(req: IAuthRequest, res: Response): Promise<void> {
|
async updateStrategy(
|
||||||
|
req: IAuthRequest<unknown, UpsertStrategySchema>,
|
||||||
|
res: Response<void>,
|
||||||
|
): Promise<void> {
|
||||||
const userName = extractUsername(req);
|
const userName = extractUsername(req);
|
||||||
try {
|
|
||||||
await this.strategyService.updateStrategy(req.body, userName);
|
await this.strategyService.updateStrategy(req.body, userName);
|
||||||
res.status(200).end();
|
res.status(200).end();
|
||||||
} catch (error) {
|
|
||||||
handleErrors(res, this.logger, error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async deprecateStrategy(req: IAuthRequest, res: Response): Promise<void> {
|
async deprecateStrategy(
|
||||||
|
req: IAuthRequest,
|
||||||
|
res: Response<void>,
|
||||||
|
): Promise<void> {
|
||||||
const userName = extractUsername(req);
|
const userName = extractUsername(req);
|
||||||
const { strategyName } = req.params;
|
const { strategyName } = req.params;
|
||||||
|
|
||||||
if (strategyName === 'default') {
|
if (strategyName === 'default') {
|
||||||
res.status(403).end();
|
res.status(403).end();
|
||||||
} else {
|
return;
|
||||||
try {
|
|
||||||
await this.strategyService.deprecateStrategy(
|
|
||||||
strategyName,
|
|
||||||
userName,
|
|
||||||
);
|
|
||||||
res.status(200).end();
|
|
||||||
} catch (error) {
|
|
||||||
handleErrors(res, this.logger, error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.strategyService.deprecateStrategy(strategyName, userName);
|
||||||
|
res.status(200).end();
|
||||||
}
|
}
|
||||||
|
|
||||||
async reactivateStrategy(req: IAuthRequest, res: Response): Promise<void> {
|
async reactivateStrategy(
|
||||||
|
req: IAuthRequest,
|
||||||
|
res: Response<void>,
|
||||||
|
): Promise<void> {
|
||||||
const userName = extractUsername(req);
|
const userName = extractUsername(req);
|
||||||
const { strategyName } = req.params;
|
const { strategyName } = req.params;
|
||||||
try {
|
|
||||||
await this.strategyService.reactivateStrategy(
|
await this.strategyService.reactivateStrategy(strategyName, userName);
|
||||||
strategyName,
|
res.status(200).end();
|
||||||
userName,
|
|
||||||
);
|
|
||||||
res.status(200).end();
|
|
||||||
} catch (error) {
|
|
||||||
handleErrors(res, this.logger, error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default StrategyController;
|
export default StrategyController;
|
||||||
|
@ -390,7 +390,7 @@ class FeatureToggleService {
|
|||||||
value: string | number,
|
value: string | number,
|
||||||
context: IFeatureStrategyContext,
|
context: IFeatureStrategyContext,
|
||||||
userName: string,
|
userName: string,
|
||||||
): Promise<IStrategyConfig> {
|
): Promise<Saved<IStrategyConfig>> {
|
||||||
const { projectId, environment, featureName } = context;
|
const { projectId, environment, featureName } = context;
|
||||||
|
|
||||||
const existingStrategy = await this.featureStrategiesStore.get(id);
|
const existingStrategy = await this.featureStrategiesStore.get(id);
|
||||||
@ -466,7 +466,7 @@ class FeatureToggleService {
|
|||||||
project: string,
|
project: string,
|
||||||
featureName: string,
|
featureName: string,
|
||||||
environment: string = DEFAULT_ENV,
|
environment: string = DEFAULT_ENV,
|
||||||
): Promise<IStrategyConfig[]> {
|
): Promise<Saved<IStrategyConfig>[]> {
|
||||||
const hasEnv = await this.featureEnvironmentStore.featureHasEnvironment(
|
const hasEnv = await this.featureEnvironmentStore.featureHasEnvironment(
|
||||||
environment,
|
environment,
|
||||||
featureName,
|
featureName,
|
||||||
@ -701,7 +701,7 @@ class FeatureToggleService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getStrategy(strategyId: string): Promise<IStrategyConfig> {
|
async getStrategy(strategyId: string): Promise<Saved<IStrategyConfig>> {
|
||||||
const strategy = await this.featureStrategiesStore.getStrategyById(
|
const strategy = await this.featureStrategiesStore.getStrategyById(
|
||||||
strategyId,
|
strategyId,
|
||||||
);
|
);
|
||||||
|
@ -188,7 +188,7 @@ export interface ITag {
|
|||||||
type: string;
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IParameterDefinition {
|
export interface IAddonParameterDefinition {
|
||||||
name: string;
|
name: string;
|
||||||
displayName: string;
|
displayName: string;
|
||||||
type: string;
|
type: string;
|
||||||
@ -203,7 +203,7 @@ export interface IAddonDefinition {
|
|||||||
displayName: string;
|
displayName: string;
|
||||||
documentationUrl: string;
|
documentationUrl: string;
|
||||||
description: string;
|
description: string;
|
||||||
parameters?: IParameterDefinition[];
|
parameters?: IAddonParameterDefinition[];
|
||||||
events?: string[];
|
events?: string[];
|
||||||
tagTypes?: ITagType[];
|
tagTypes?: ITagType[];
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ export interface IStrategy {
|
|||||||
name: string;
|
name: string;
|
||||||
editable: boolean;
|
editable: boolean;
|
||||||
description: string;
|
description: string;
|
||||||
parameters: object;
|
parameters: object[];
|
||||||
deprecated: boolean;
|
deprecated: boolean;
|
||||||
displayName: string;
|
displayName: string;
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,9 @@ import {
|
|||||||
} from '../../helpers/test-helper';
|
} from '../../helpers/test-helper';
|
||||||
import getLogger from '../../../fixtures/no-logger';
|
import getLogger from '../../../fixtures/no-logger';
|
||||||
import { DEFAULT_ENV } from '../../../../lib/util/constants';
|
import { DEFAULT_ENV } from '../../../../lib/util/constants';
|
||||||
import { StrategySchema } from '../../../../lib/openapi/spec/strategy-schema';
|
|
||||||
import { FeatureSchema } from '../../../../lib/openapi/spec/feature-schema';
|
import { FeatureSchema } from '../../../../lib/openapi/spec/feature-schema';
|
||||||
import { VariantSchema } from '../../../../lib/openapi/spec/variant-schema';
|
import { VariantSchema } from '../../../../lib/openapi/spec/variant-schema';
|
||||||
|
import { FeatureStrategySchema } from '../../../../lib/openapi/spec/feature-strategy-schema';
|
||||||
|
|
||||||
let app: IUnleashTest;
|
let app: IUnleashTest;
|
||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
@ -26,7 +26,7 @@ beforeAll(async () => {
|
|||||||
|
|
||||||
const createToggle = async (
|
const createToggle = async (
|
||||||
toggle: Omit<FeatureSchema, 'createdAt'>,
|
toggle: Omit<FeatureSchema, 'createdAt'>,
|
||||||
strategy: Omit<StrategySchema, 'id'> = defaultStrategy,
|
strategy: Omit<FeatureStrategySchema, 'id'> = defaultStrategy,
|
||||||
projectId: string = 'default',
|
projectId: string = 'default',
|
||||||
username: string = 'test',
|
username: string = 'test',
|
||||||
) => {
|
) => {
|
||||||
@ -576,7 +576,7 @@ test('tagging a feature with an already existing tag should be a noop', async ()
|
|||||||
|
|
||||||
test('can untag feature', async () => {
|
test('can untag feature', async () => {
|
||||||
expect.assertions(1);
|
expect.assertions(1);
|
||||||
const feature1Name = faker.lorem.slug(3);
|
const feature1Name = faker.datatype.uuid();
|
||||||
await app.request.post('/api/admin/features').send({
|
await app.request.post('/api/admin/features').send({
|
||||||
name: feature1Name,
|
name: feature1Name,
|
||||||
type: 'killswitch',
|
type: 'killswitch',
|
||||||
@ -584,7 +584,7 @@ test('can untag feature', async () => {
|
|||||||
strategies: [{ name: 'default' }],
|
strategies: [{ name: 'default' }],
|
||||||
});
|
});
|
||||||
const tag = {
|
const tag = {
|
||||||
value: faker.lorem.slug(1),
|
value: faker.lorem.word(),
|
||||||
type: 'simple',
|
type: 'simple',
|
||||||
};
|
};
|
||||||
await app.request
|
await app.request
|
||||||
@ -607,8 +607,8 @@ test('can untag feature', async () => {
|
|||||||
|
|
||||||
test('Can get features tagged by tag', async () => {
|
test('Can get features tagged by tag', async () => {
|
||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
const feature1Name = faker.helpers.slugify(faker.lorem.words(3));
|
const feature1Name = faker.datatype.uuid();
|
||||||
const feature2Name = faker.helpers.slugify(faker.lorem.words(3));
|
const feature2Name = faker.datatype.uuid();
|
||||||
await app.request.post('/api/admin/features').send({
|
await app.request.post('/api/admin/features').send({
|
||||||
name: feature1Name,
|
name: feature1Name,
|
||||||
type: 'killswitch',
|
type: 'killswitch',
|
||||||
@ -637,8 +637,8 @@ test('Can get features tagged by tag', async () => {
|
|||||||
});
|
});
|
||||||
test('Can query for multiple tags using OR', async () => {
|
test('Can query for multiple tags using OR', async () => {
|
||||||
expect.assertions(3);
|
expect.assertions(3);
|
||||||
const feature1Name = faker.helpers.slugify(faker.lorem.words(3));
|
const feature1Name = faker.datatype.uuid();
|
||||||
const feature2Name = faker.helpers.slugify(faker.lorem.words(3));
|
const feature2Name = faker.datatype.uuid();
|
||||||
await app.request.post('/api/admin/features').send({
|
await app.request.post('/api/admin/features').send({
|
||||||
name: feature1Name,
|
name: feature1Name,
|
||||||
type: 'killswitch',
|
type: 'killswitch',
|
||||||
@ -678,8 +678,8 @@ test('Can query for multiple tags using OR', async () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
test('Querying with multiple filters ANDs the filters', async () => {
|
test('Querying with multiple filters ANDs the filters', async () => {
|
||||||
const feature1Name = `test.${faker.helpers.slugify(faker.hacker.phrase())}`;
|
const feature1Name = `test.${faker.datatype.uuid()}`;
|
||||||
const feature2Name = faker.helpers.slugify(faker.lorem.words());
|
const feature2Name = faker.datatype.uuid();
|
||||||
|
|
||||||
await app.request.post('/api/admin/features').send({
|
await app.request.post('/api/admin/features').send({
|
||||||
name: feature1Name,
|
name: feature1Name,
|
||||||
@ -729,7 +729,7 @@ test('Querying with multiple filters ANDs the filters', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Tagging a feature with a tag it already has should return 409', async () => {
|
test('Tagging a feature with a tag it already has should return 409', async () => {
|
||||||
const feature1Name = `test.${faker.helpers.slugify(faker.lorem.words(3))}`;
|
const feature1Name = `test.${faker.datatype.uuid()}`;
|
||||||
await app.request.post('/api/admin/features').send({
|
await app.request.post('/api/admin/features').send({
|
||||||
name: feature1Name,
|
name: feature1Name,
|
||||||
type: 'killswitch',
|
type: 'killswitch',
|
||||||
|
@ -465,7 +465,7 @@ Object {
|
|||||||
],
|
],
|
||||||
"type": "object",
|
"type": "object",
|
||||||
},
|
},
|
||||||
"createStrategySchema": Object {
|
"createFeatureStrategySchema": Object {
|
||||||
"properties": Object {
|
"properties": Object {
|
||||||
"constraints": Object {
|
"constraints": Object {
|
||||||
"items": Object {
|
"items": Object {
|
||||||
@ -612,7 +612,50 @@ Object {
|
|||||||
},
|
},
|
||||||
"strategies": Object {
|
"strategies": Object {
|
||||||
"items": Object {
|
"items": Object {
|
||||||
"$ref": "#/components/schemas/featureStrategySchema",
|
"additionalProperties": false,
|
||||||
|
"properties": Object {
|
||||||
|
"constraints": Object {
|
||||||
|
"items": Object {
|
||||||
|
"$ref": "#/components/schemas/constraintSchema",
|
||||||
|
},
|
||||||
|
"type": "array",
|
||||||
|
},
|
||||||
|
"createdAt": Object {
|
||||||
|
"format": "date-time",
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"environment": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"featureName": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"id": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"parameters": Object {
|
||||||
|
"$ref": "#/components/schemas/parametersSchema",
|
||||||
|
},
|
||||||
|
"projectId": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"sortOrder": Object {
|
||||||
|
"type": "number",
|
||||||
|
},
|
||||||
|
"strategyName": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": Array [
|
||||||
|
"id",
|
||||||
|
"featureName",
|
||||||
|
"projectId",
|
||||||
|
"environment",
|
||||||
|
"strategyName",
|
||||||
|
"constraints",
|
||||||
|
"parameters",
|
||||||
|
],
|
||||||
|
"type": "object",
|
||||||
},
|
},
|
||||||
"type": "array",
|
"type": "array",
|
||||||
},
|
},
|
||||||
@ -668,7 +711,7 @@ Object {
|
|||||||
},
|
},
|
||||||
"strategies": Object {
|
"strategies": Object {
|
||||||
"items": Object {
|
"items": Object {
|
||||||
"$ref": "#/components/schemas/strategySchema",
|
"$ref": "#/components/schemas/featureStrategySchema",
|
||||||
},
|
},
|
||||||
"type": "array",
|
"type": "array",
|
||||||
},
|
},
|
||||||
@ -696,17 +739,6 @@ Object {
|
|||||||
},
|
},
|
||||||
"type": "array",
|
"type": "array",
|
||||||
},
|
},
|
||||||
"createdAt": Object {
|
|
||||||
"format": "date-time",
|
|
||||||
"nullable": true,
|
|
||||||
"type": "string",
|
|
||||||
},
|
|
||||||
"environment": Object {
|
|
||||||
"type": "string",
|
|
||||||
},
|
|
||||||
"featureName": Object {
|
|
||||||
"type": "string",
|
|
||||||
},
|
|
||||||
"id": Object {
|
"id": Object {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
@ -716,23 +748,13 @@ Object {
|
|||||||
"parameters": Object {
|
"parameters": Object {
|
||||||
"$ref": "#/components/schemas/parametersSchema",
|
"$ref": "#/components/schemas/parametersSchema",
|
||||||
},
|
},
|
||||||
"projectId": Object {
|
|
||||||
"type": "string",
|
|
||||||
},
|
|
||||||
"sortOrder": Object {
|
"sortOrder": Object {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"strategyName": Object {
|
|
||||||
"type": "string",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"required": Array [
|
"required": Array [
|
||||||
|
"name",
|
||||||
"id",
|
"id",
|
||||||
"featureName",
|
|
||||||
"strategyName",
|
|
||||||
"constraints",
|
|
||||||
"parameters",
|
|
||||||
"environment",
|
|
||||||
],
|
],
|
||||||
"type": "object",
|
"type": "object",
|
||||||
},
|
},
|
||||||
@ -1383,30 +1405,73 @@ Object {
|
|||||||
],
|
],
|
||||||
"type": "object",
|
"type": "object",
|
||||||
},
|
},
|
||||||
"strategySchema": Object {
|
"strategiesSchema": Object {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": Object {
|
"properties": Object {
|
||||||
"constraints": Object {
|
"strategies": Object {
|
||||||
"items": Object {
|
"items": Object {
|
||||||
"$ref": "#/components/schemas/constraintSchema",
|
"$ref": "#/components/schemas/strategySchema",
|
||||||
},
|
},
|
||||||
"type": "array",
|
"type": "array",
|
||||||
},
|
},
|
||||||
"id": Object {
|
"version": Object {
|
||||||
|
"type": "integer",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": Array [
|
||||||
|
"version",
|
||||||
|
"strategies",
|
||||||
|
],
|
||||||
|
"type": "object",
|
||||||
|
},
|
||||||
|
"strategySchema": Object {
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": Object {
|
||||||
|
"deprecated": Object {
|
||||||
|
"type": "boolean",
|
||||||
|
},
|
||||||
|
"description": Object {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
|
"displayName": Object {
|
||||||
|
"nullable": true,
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"editable": Object {
|
||||||
|
"type": "boolean",
|
||||||
|
},
|
||||||
"name": Object {
|
"name": Object {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"parameters": Object {
|
"parameters": Object {
|
||||||
"$ref": "#/components/schemas/parametersSchema",
|
"items": Object {
|
||||||
},
|
"additionalProperties": false,
|
||||||
"sortOrder": Object {
|
"properties": Object {
|
||||||
"type": "number",
|
"description": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"name": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"required": Object {
|
||||||
|
"type": "boolean",
|
||||||
|
},
|
||||||
|
"type": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "object",
|
||||||
|
},
|
||||||
|
"type": "array",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"required": Array [
|
"required": Array [
|
||||||
"name",
|
"name",
|
||||||
|
"displayName",
|
||||||
|
"description",
|
||||||
|
"editable",
|
||||||
|
"deprecated",
|
||||||
|
"parameters",
|
||||||
],
|
],
|
||||||
"type": "object",
|
"type": "object",
|
||||||
},
|
},
|
||||||
@ -1647,8 +1712,7 @@ Object {
|
|||||||
],
|
],
|
||||||
"type": "object",
|
"type": "object",
|
||||||
},
|
},
|
||||||
"updateStrategySchema": Object {
|
"updateFeatureStrategySchema": Object {
|
||||||
"additionalProperties": false,
|
|
||||||
"properties": Object {
|
"properties": Object {
|
||||||
"constraints": Object {
|
"constraints": Object {
|
||||||
"items": Object {
|
"items": Object {
|
||||||
@ -1656,9 +1720,6 @@ Object {
|
|||||||
},
|
},
|
||||||
"type": "array",
|
"type": "array",
|
||||||
},
|
},
|
||||||
"id": Object {
|
|
||||||
"type": "string",
|
|
||||||
},
|
|
||||||
"name": Object {
|
"name": Object {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
@ -1669,7 +1730,6 @@ Object {
|
|||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"required": Array [],
|
|
||||||
"type": "object",
|
"type": "object",
|
||||||
},
|
},
|
||||||
"updateTagTypeSchema": Object {
|
"updateTagTypeSchema": Object {
|
||||||
@ -1724,6 +1784,43 @@ Object {
|
|||||||
],
|
],
|
||||||
"type": "object",
|
"type": "object",
|
||||||
},
|
},
|
||||||
|
"upsertStrategySchema": Object {
|
||||||
|
"properties": Object {
|
||||||
|
"description": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"editable": Object {
|
||||||
|
"type": "boolean",
|
||||||
|
},
|
||||||
|
"name": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"parameters": Object {
|
||||||
|
"items": Object {
|
||||||
|
"properties": Object {
|
||||||
|
"description": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"name": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"required": Object {
|
||||||
|
"type": "boolean",
|
||||||
|
},
|
||||||
|
"type": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "object",
|
||||||
|
},
|
||||||
|
"type": "array",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": Array [
|
||||||
|
"name",
|
||||||
|
],
|
||||||
|
"type": "object",
|
||||||
|
},
|
||||||
"userSchema": Object {
|
"userSchema": Object {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": Object {
|
"properties": Object {
|
||||||
@ -3434,7 +3531,7 @@ Object {
|
|||||||
},
|
},
|
||||||
"/api/admin/projects/{projectId}/features/{featureName}/environments/{environment}/strategies": Object {
|
"/api/admin/projects/{projectId}/features/{featureName}/environments/{environment}/strategies": Object {
|
||||||
"get": Object {
|
"get": Object {
|
||||||
"operationId": "getStrategies",
|
"operationId": "getFeatureStrategies",
|
||||||
"parameters": Array [
|
"parameters": Array [
|
||||||
Object {
|
Object {
|
||||||
"in": "path",
|
"in": "path",
|
||||||
@ -3466,11 +3563,11 @@ Object {
|
|||||||
"content": Object {
|
"content": Object {
|
||||||
"application/json": Object {
|
"application/json": Object {
|
||||||
"schema": Object {
|
"schema": Object {
|
||||||
"$ref": "#/components/schemas/strategySchema",
|
"$ref": "#/components/schemas/featureStrategySchema",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"description": "strategySchema",
|
"description": "featureStrategySchema",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"tags": Array [
|
"tags": Array [
|
||||||
@ -3478,7 +3575,7 @@ Object {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
"post": Object {
|
"post": Object {
|
||||||
"operationId": "addStrategy",
|
"operationId": "addFeatureStrategy",
|
||||||
"parameters": Array [
|
"parameters": Array [
|
||||||
Object {
|
Object {
|
||||||
"in": "path",
|
"in": "path",
|
||||||
@ -3509,11 +3606,11 @@ Object {
|
|||||||
"content": Object {
|
"content": Object {
|
||||||
"application/json": Object {
|
"application/json": Object {
|
||||||
"schema": Object {
|
"schema": Object {
|
||||||
"$ref": "#/components/schemas/createStrategySchema",
|
"$ref": "#/components/schemas/createFeatureStrategySchema",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"description": "createStrategySchema",
|
"description": "createFeatureStrategySchema",
|
||||||
"required": true,
|
"required": true,
|
||||||
},
|
},
|
||||||
"responses": Object {
|
"responses": Object {
|
||||||
@ -3535,7 +3632,7 @@ Object {
|
|||||||
},
|
},
|
||||||
"/api/admin/projects/{projectId}/features/{featureName}/environments/{environment}/strategies/{strategyId}": Object {
|
"/api/admin/projects/{projectId}/features/{featureName}/environments/{environment}/strategies/{strategyId}": Object {
|
||||||
"delete": Object {
|
"delete": Object {
|
||||||
"operationId": "deleteStrategy",
|
"operationId": "deleteFeatureStrategy",
|
||||||
"parameters": Array [
|
"parameters": Array [
|
||||||
Object {
|
Object {
|
||||||
"in": "path",
|
"in": "path",
|
||||||
@ -3580,7 +3677,7 @@ Object {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
"get": Object {
|
"get": Object {
|
||||||
"operationId": "getStrategy",
|
"operationId": "getFeatureStrategy",
|
||||||
"parameters": Array [
|
"parameters": Array [
|
||||||
Object {
|
Object {
|
||||||
"in": "path",
|
"in": "path",
|
||||||
@ -3632,7 +3729,7 @@ Object {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
"patch": Object {
|
"patch": Object {
|
||||||
"operationId": "patchStrategy",
|
"operationId": "patchFeatureStrategy",
|
||||||
"parameters": Array [
|
"parameters": Array [
|
||||||
Object {
|
Object {
|
||||||
"in": "path",
|
"in": "path",
|
||||||
@ -3695,7 +3792,7 @@ Object {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
"put": Object {
|
"put": Object {
|
||||||
"operationId": "updateStrategy",
|
"operationId": "updateFeatureStrategy",
|
||||||
"parameters": Array [
|
"parameters": Array [
|
||||||
Object {
|
Object {
|
||||||
"in": "path",
|
"in": "path",
|
||||||
@ -3734,11 +3831,11 @@ Object {
|
|||||||
"content": Object {
|
"content": Object {
|
||||||
"application/json": Object {
|
"application/json": Object {
|
||||||
"schema": Object {
|
"schema": Object {
|
||||||
"$ref": "#/components/schemas/updateStrategySchema",
|
"$ref": "#/components/schemas/updateFeatureStrategySchema",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"description": "updateStrategySchema",
|
"description": "updateFeatureStrategySchema",
|
||||||
"required": true,
|
"required": true,
|
||||||
},
|
},
|
||||||
"responses": Object {
|
"responses": Object {
|
||||||
@ -3999,6 +4096,179 @@ Object {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"/api/admin/strategies": Object {
|
||||||
|
"get": Object {
|
||||||
|
"operationId": "getAllStrategies",
|
||||||
|
"responses": Object {
|
||||||
|
"200": Object {
|
||||||
|
"content": Object {
|
||||||
|
"application/json": Object {
|
||||||
|
"schema": Object {
|
||||||
|
"$ref": "#/components/schemas/strategiesSchema",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"description": "strategiesSchema",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"tags": Array [
|
||||||
|
"admin",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"post": Object {
|
||||||
|
"operationId": "createStrategy",
|
||||||
|
"requestBody": Object {
|
||||||
|
"content": Object {
|
||||||
|
"application/json": Object {
|
||||||
|
"schema": Object {
|
||||||
|
"$ref": "#/components/schemas/upsertStrategySchema",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"description": "upsertStrategySchema",
|
||||||
|
"required": true,
|
||||||
|
},
|
||||||
|
"responses": Object {
|
||||||
|
"201": Object {
|
||||||
|
"description": "emptyResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"tags": Array [
|
||||||
|
"admin",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"/api/admin/strategies/{name}": Object {
|
||||||
|
"delete": Object {
|
||||||
|
"operationId": "removeStrategy",
|
||||||
|
"parameters": Array [
|
||||||
|
Object {
|
||||||
|
"in": "path",
|
||||||
|
"name": "name",
|
||||||
|
"required": true,
|
||||||
|
"schema": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"responses": Object {
|
||||||
|
"200": Object {
|
||||||
|
"description": "emptyResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"tags": Array [
|
||||||
|
"admin",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"get": Object {
|
||||||
|
"operationId": "getStrategy",
|
||||||
|
"parameters": Array [
|
||||||
|
Object {
|
||||||
|
"in": "path",
|
||||||
|
"name": "name",
|
||||||
|
"required": true,
|
||||||
|
"schema": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"responses": Object {
|
||||||
|
"200": Object {
|
||||||
|
"content": Object {
|
||||||
|
"application/json": Object {
|
||||||
|
"schema": Object {
|
||||||
|
"$ref": "#/components/schemas/strategySchema",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"description": "strategySchema",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"tags": Array [
|
||||||
|
"admin",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"/api/admin/strategies/{strategyName}": Object {
|
||||||
|
"put": Object {
|
||||||
|
"operationId": "updateStrategy",
|
||||||
|
"parameters": Array [
|
||||||
|
Object {
|
||||||
|
"in": "path",
|
||||||
|
"name": "strategyName",
|
||||||
|
"required": true,
|
||||||
|
"schema": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"requestBody": Object {
|
||||||
|
"content": Object {
|
||||||
|
"application/json": Object {
|
||||||
|
"schema": Object {
|
||||||
|
"$ref": "#/components/schemas/upsertStrategySchema",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"description": "upsertStrategySchema",
|
||||||
|
"required": true,
|
||||||
|
},
|
||||||
|
"responses": Object {
|
||||||
|
"200": Object {
|
||||||
|
"description": "emptyResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"tags": Array [
|
||||||
|
"admin",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"/api/admin/strategies/{strategyName}/deprecate": Object {
|
||||||
|
"post": Object {
|
||||||
|
"operationId": "deprecateStrategy",
|
||||||
|
"parameters": Array [
|
||||||
|
Object {
|
||||||
|
"in": "path",
|
||||||
|
"name": "strategyName",
|
||||||
|
"required": true,
|
||||||
|
"schema": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"responses": Object {
|
||||||
|
"200": Object {
|
||||||
|
"description": "emptyResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"tags": Array [
|
||||||
|
"admin",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"/api/admin/strategies/{strategyName}/reactivate": Object {
|
||||||
|
"post": Object {
|
||||||
|
"operationId": "reactivateStrategy",
|
||||||
|
"parameters": Array [
|
||||||
|
Object {
|
||||||
|
"in": "path",
|
||||||
|
"name": "strategyName",
|
||||||
|
"required": true,
|
||||||
|
"schema": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"responses": Object {
|
||||||
|
"200": Object {
|
||||||
|
"description": "emptyResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"tags": Array [
|
||||||
|
"admin",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
"/api/admin/tag-types": Object {
|
"/api/admin/tag-types": Object {
|
||||||
"get": Object {
|
"get": Object {
|
||||||
"operationId": "getTagTypes",
|
"operationId": "getTagTypes",
|
||||||
|
@ -43,7 +43,7 @@ test('Apps registered should be announced', async () => {
|
|||||||
color: faker.internet.color(),
|
color: faker.internet.color(),
|
||||||
};
|
};
|
||||||
const differentClient = {
|
const differentClient = {
|
||||||
appName: faker.lorem.slug(2),
|
appName: faker.datatype.uuid(),
|
||||||
instanceId: faker.datatype.uuid(),
|
instanceId: faker.datatype.uuid(),
|
||||||
strategies: ['default'],
|
strategies: ['default'],
|
||||||
started: Date.now(),
|
started: Date.now(),
|
||||||
|
@ -2,8 +2,8 @@ import FeatureToggleService from '../../../lib/services/feature-toggle-service';
|
|||||||
import { createTestConfig } from '../../config/test-config';
|
import { createTestConfig } from '../../config/test-config';
|
||||||
import dbInit from '../helpers/database-init';
|
import dbInit from '../helpers/database-init';
|
||||||
import { DEFAULT_ENV } from '../../../lib/util/constants';
|
import { DEFAULT_ENV } from '../../../lib/util/constants';
|
||||||
import { StrategySchema } from '../../../lib/openapi/spec/strategy-schema';
|
|
||||||
import { SegmentService } from '../../../lib/services/segment-service';
|
import { SegmentService } from '../../../lib/services/segment-service';
|
||||||
|
import { FeatureStrategySchema } from '../../../lib/openapi/spec/feature-strategy-schema';
|
||||||
|
|
||||||
let stores;
|
let stores;
|
||||||
let db;
|
let db;
|
||||||
@ -30,7 +30,7 @@ afterAll(async () => {
|
|||||||
test('Should create feature toggle strategy configuration', async () => {
|
test('Should create feature toggle strategy configuration', async () => {
|
||||||
const projectId = 'default';
|
const projectId = 'default';
|
||||||
const username = 'feature-toggle';
|
const username = 'feature-toggle';
|
||||||
const config: Omit<StrategySchema, 'id'> = {
|
const config: Omit<FeatureStrategySchema, 'id'> = {
|
||||||
name: 'default',
|
name: 'default',
|
||||||
constraints: [],
|
constraints: [],
|
||||||
parameters: {},
|
parameters: {},
|
||||||
@ -58,7 +58,7 @@ test('Should be able to update existing strategy configuration', async () => {
|
|||||||
const projectId = 'default';
|
const projectId = 'default';
|
||||||
const username = 'existing-strategy';
|
const username = 'existing-strategy';
|
||||||
const featureName = 'update-existing-strategy';
|
const featureName = 'update-existing-strategy';
|
||||||
const config: Omit<StrategySchema, 'id'> = {
|
const config: Omit<FeatureStrategySchema, 'id'> = {
|
||||||
name: 'default',
|
name: 'default',
|
||||||
constraints: [],
|
constraints: [],
|
||||||
parameters: {},
|
parameters: {},
|
||||||
@ -93,7 +93,7 @@ test('Should be able to get strategy by id', async () => {
|
|||||||
const projectId = 'default';
|
const projectId = 'default';
|
||||||
|
|
||||||
const userName = 'strategy';
|
const userName = 'strategy';
|
||||||
const config: Omit<StrategySchema, 'id'> = {
|
const config: Omit<FeatureStrategySchema, 'id'> = {
|
||||||
name: 'default',
|
name: 'default',
|
||||||
constraints: [],
|
constraints: [],
|
||||||
parameters: {},
|
parameters: {},
|
||||||
|
2
src/test/fixtures/fake-strategies-store.ts
vendored
2
src/test/fixtures/fake-strategies-store.ts
vendored
@ -12,7 +12,7 @@ export default class FakeStrategiesStore implements IStrategyStore {
|
|||||||
description: 'default strategy',
|
description: 'default strategy',
|
||||||
displayName: 'Default',
|
displayName: 'Default',
|
||||||
editable: false,
|
editable: false,
|
||||||
parameters: {},
|
parameters: [],
|
||||||
deprecated: false,
|
deprecated: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user