mirror of
https://github.com/Unleash/unleash.git
synced 2025-10-27 11:02:16 +01:00
feat: Gradual schema improvements (#3075)
This commit is contained in:
parent
465e98564f
commit
df3ce70c0d
@ -5,33 +5,51 @@ export const environmentSchema = {
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
required: ['name', 'type', 'enabled'],
|
required: ['name', 'type', 'enabled'],
|
||||||
|
description: 'A definition of the project environment',
|
||||||
properties: {
|
properties: {
|
||||||
name: {
|
name: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
example: 'my-dev-env',
|
||||||
|
description: 'The name of the environment',
|
||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
example: 'development',
|
||||||
|
description: 'The type of the environment',
|
||||||
},
|
},
|
||||||
enabled: {
|
enabled: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
example: true,
|
||||||
|
description:
|
||||||
|
'`true` if the environment is enabled for the project, otherwise `false`.',
|
||||||
},
|
},
|
||||||
protected: {
|
protected: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
},
|
},
|
||||||
sortOrder: {
|
sortOrder: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
example: 3,
|
||||||
|
description:
|
||||||
|
'The sort order of the environment in the environments list',
|
||||||
},
|
},
|
||||||
projectCount: {
|
projectCount: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
example: 10,
|
||||||
|
description: 'The number of projects with this environment',
|
||||||
},
|
},
|
||||||
apiTokenCount: {
|
apiTokenCount: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
example: 6,
|
||||||
|
description: 'The number of API tokens for the project environment',
|
||||||
},
|
},
|
||||||
enabledToggleCount: {
|
enabledToggleCount: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
example: 10,
|
||||||
|
description:
|
||||||
|
'The number of enabled toggles for the project environment',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {},
|
components: {},
|
||||||
|
|||||||
@ -9,24 +9,36 @@ export const featureEnvironmentSchema = {
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
required: ['name', 'enabled'],
|
required: ['name', 'enabled'],
|
||||||
|
description: 'A detailed description of the feature environment',
|
||||||
properties: {
|
properties: {
|
||||||
name: {
|
name: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
example: 'my-dev-env',
|
||||||
|
description: 'The name of the environment',
|
||||||
},
|
},
|
||||||
featureName: {
|
featureName: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
example: 'disable-comments',
|
||||||
},
|
},
|
||||||
environment: {
|
environment: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
example: 'development',
|
||||||
|
description: 'The type of the environment',
|
||||||
},
|
},
|
||||||
enabled: {
|
enabled: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
example: true,
|
||||||
|
description:
|
||||||
|
'`true` if the feature is enabled for the environment, otherwise `false`.',
|
||||||
},
|
},
|
||||||
sortOrder: {
|
sortOrder: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
example: 3,
|
||||||
|
description:
|
||||||
|
'The sort order of the feature environment in the feature environments list',
|
||||||
},
|
},
|
||||||
variantCount: {
|
variantCount: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
@ -36,12 +48,15 @@ export const featureEnvironmentSchema = {
|
|||||||
items: {
|
items: {
|
||||||
$ref: '#/components/schemas/featureStrategySchema',
|
$ref: '#/components/schemas/featureStrategySchema',
|
||||||
},
|
},
|
||||||
|
description:
|
||||||
|
'A list of activation strategies for the feature environment',
|
||||||
},
|
},
|
||||||
variants: {
|
variants: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: {
|
items: {
|
||||||
$ref: '#/components/schemas/variantSchema',
|
$ref: '#/components/schemas/variantSchema',
|
||||||
},
|
},
|
||||||
|
description: 'A list of variants for the feature environment',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
|||||||
@ -15,58 +15,87 @@ export const featureSchema = {
|
|||||||
properties: {
|
properties: {
|
||||||
name: {
|
name: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
example: 'disable-comments',
|
||||||
|
description: 'Unique feature name',
|
||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
example: 'kill-switch',
|
||||||
|
description:
|
||||||
|
'Type of the toggle e.g. experiment, kill-switch, release, operational, permission',
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
example:
|
||||||
|
'Controls disabling of the comments section in case of an incident',
|
||||||
|
description: 'Detailed description of the feature',
|
||||||
},
|
},
|
||||||
archived: {
|
archived: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
example: true,
|
||||||
|
description:
|
||||||
|
'`true` if the feature is archived, otherwise `false`.',
|
||||||
},
|
},
|
||||||
project: {
|
project: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
example: 'dx-squad',
|
||||||
|
description: 'Name of the project the feature belongs to',
|
||||||
},
|
},
|
||||||
enabled: {
|
enabled: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
example: true,
|
||||||
},
|
},
|
||||||
stale: {
|
stale: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
example: false,
|
||||||
|
description:
|
||||||
|
'`true` if the feature is stale based on the age and feature type, otherwise `false`.',
|
||||||
},
|
},
|
||||||
favorite: {
|
favorite: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
example: true,
|
||||||
|
description:
|
||||||
|
'`true` if the feature was favorited, otherwise `false`.',
|
||||||
},
|
},
|
||||||
impressionData: {
|
impressionData: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
example: false,
|
||||||
|
description:
|
||||||
|
'`true` if the impression data collection is enabled for the feature, otherwise `false`.',
|
||||||
},
|
},
|
||||||
createdAt: {
|
createdAt: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
format: 'date-time',
|
format: 'date-time',
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
example: '2023-01-28T15:21:39.975Z',
|
||||||
},
|
},
|
||||||
archivedAt: {
|
archivedAt: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
format: 'date-time',
|
format: 'date-time',
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
example: '2023-01-29T15:21:39.975Z',
|
||||||
},
|
},
|
||||||
lastSeenAt: {
|
lastSeenAt: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
format: 'date-time',
|
format: 'date-time',
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
example: '2023-01-28T16:21:39.975Z',
|
||||||
},
|
},
|
||||||
environments: {
|
environments: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: {
|
items: {
|
||||||
$ref: '#/components/schemas/featureEnvironmentSchema',
|
$ref: '#/components/schemas/featureEnvironmentSchema',
|
||||||
},
|
},
|
||||||
|
description:
|
||||||
|
'The list of environments where the feature can be used',
|
||||||
},
|
},
|
||||||
variants: {
|
variants: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: {
|
items: {
|
||||||
$ref: '#/components/schemas/variantSchema',
|
$ref: '#/components/schemas/variantSchema',
|
||||||
},
|
},
|
||||||
|
description: 'The list of feature variants',
|
||||||
},
|
},
|
||||||
tags: {
|
tags: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
@ -74,6 +103,7 @@ export const featureSchema = {
|
|||||||
$ref: '#/components/schemas/tagSchema',
|
$ref: '#/components/schemas/tagSchema',
|
||||||
},
|
},
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
description: 'The list of feature tags',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
|||||||
@ -14,44 +14,66 @@ export const projectOverviewSchema = {
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
required: ['version', 'name'],
|
required: ['version', 'name'],
|
||||||
|
description:
|
||||||
|
'A high-level overview of a project. It contains information such as project statistics, the name of the project, what members and what features it contains, etc.',
|
||||||
properties: {
|
properties: {
|
||||||
stats: {
|
stats: {
|
||||||
$ref: '#/components/schemas/projectStatsSchema',
|
$ref: '#/components/schemas/projectStatsSchema',
|
||||||
|
description: 'Project statistics',
|
||||||
},
|
},
|
||||||
version: {
|
version: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
example: 1,
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
example: 'dx-squad',
|
||||||
|
description: 'The name of this project',
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
example: 'DX squad feature release',
|
||||||
|
description: 'Additional information about the project',
|
||||||
},
|
},
|
||||||
members: {
|
members: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
example: 4,
|
||||||
|
description: 'The number of members this project has',
|
||||||
},
|
},
|
||||||
health: {
|
health: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
example: 50,
|
||||||
|
description:
|
||||||
|
"An indicator of the [project's health](https://docs.getunleash.io/reference/technical-debt#health-rating) on a scale from 0 to 100",
|
||||||
},
|
},
|
||||||
environments: {
|
environments: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: {
|
items: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
|
example: ['development', 'production'],
|
||||||
|
description: 'The environments that are enabled for this project',
|
||||||
},
|
},
|
||||||
features: {
|
features: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: {
|
items: {
|
||||||
$ref: '#/components/schemas/featureSchema',
|
$ref: '#/components/schemas/featureSchema',
|
||||||
},
|
},
|
||||||
|
description:
|
||||||
|
'The full list of features in this project (excluding archived features)',
|
||||||
},
|
},
|
||||||
updatedAt: {
|
updatedAt: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
format: 'date-time',
|
format: 'date-time',
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
example: '2023-02-10T08:36:35.262Z',
|
||||||
},
|
},
|
||||||
favorite: {
|
favorite: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
example: true,
|
||||||
|
description:
|
||||||
|
'`true` if the project was favorited, otherwise `false`.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
|||||||
@ -10,7 +10,6 @@ test('projectSchema', () => {
|
|||||||
featureCount: 10,
|
featureCount: 10,
|
||||||
memberCount: 3,
|
memberCount: 3,
|
||||||
updatedAt: '2022-06-28T17:33:53.963Z',
|
updatedAt: '2022-06-28T17:33:53.963Z',
|
||||||
changeRequestsEnabled: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
|||||||
@ -5,24 +5,40 @@ export const projectSchema = {
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
required: ['id', 'name'],
|
required: ['id', 'name'],
|
||||||
|
description:
|
||||||
|
'A definition of the project used for projects listing purposes',
|
||||||
properties: {
|
properties: {
|
||||||
id: {
|
id: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
example: 'dx-squad',
|
||||||
|
description: 'The id of this project',
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
example: 'DX-Squad',
|
||||||
|
description: 'The name of this project',
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
example: 'DX squad feature release',
|
||||||
|
description: 'Additional information about the project',
|
||||||
},
|
},
|
||||||
health: {
|
health: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
example: 50,
|
||||||
|
description:
|
||||||
|
"An indicator of the [project's health](https://docs.getunleash.io/reference/technical-debt#health-rating) on a scale from 0 to 100",
|
||||||
},
|
},
|
||||||
featureCount: {
|
featureCount: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
example: 10,
|
||||||
|
description: 'The number of features this project has',
|
||||||
},
|
},
|
||||||
memberCount: {
|
memberCount: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
example: 4,
|
||||||
|
description: 'The number of members this project has',
|
||||||
},
|
},
|
||||||
createdAt: {
|
createdAt: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
@ -33,11 +49,11 @@ export const projectSchema = {
|
|||||||
format: 'date-time',
|
format: 'date-time',
|
||||||
nullable: true,
|
nullable: true,
|
||||||
},
|
},
|
||||||
changeRequestsEnabled: {
|
|
||||||
type: 'boolean',
|
|
||||||
},
|
|
||||||
favorite: {
|
favorite: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
example: true,
|
||||||
|
description:
|
||||||
|
'`true` if the project was favorited, otherwise `false`.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {},
|
components: {},
|
||||||
|
|||||||
@ -15,33 +15,65 @@ export const projectStatsSchema = {
|
|||||||
'projectActivityPastWindow',
|
'projectActivityPastWindow',
|
||||||
'projectMembersAddedCurrentWindow',
|
'projectMembersAddedCurrentWindow',
|
||||||
],
|
],
|
||||||
|
description: `Statistics for a project, including the average time to production, number of features created, the project activity and more.
|
||||||
|
|
||||||
|
Stats are divided into current and previous **windows**.
|
||||||
|
- The **current window** is the past 30 days.
|
||||||
|
- The **previous window** is the 30 days **before** the current window (from 60 to 30 days ago)`,
|
||||||
properties: {
|
properties: {
|
||||||
avgTimeToProdCurrentWindow: {
|
avgTimeToProdCurrentWindow: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
example: 10,
|
||||||
|
description:
|
||||||
|
'The average time from when a feature was created to when it was enabled in the "production" environment during the current window',
|
||||||
},
|
},
|
||||||
avgTimeToProdPastWindow: {
|
avgTimeToProdPastWindow: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
example: 10,
|
||||||
|
description:
|
||||||
|
'The average time from when a feature was created to when it was enabled in the "production" environment during the previous window',
|
||||||
},
|
},
|
||||||
createdCurrentWindow: {
|
createdCurrentWindow: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
example: 15,
|
||||||
|
description:
|
||||||
|
'The number of feature toggles created during the current window',
|
||||||
},
|
},
|
||||||
createdPastWindow: {
|
createdPastWindow: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
example: 15,
|
||||||
|
description:
|
||||||
|
'The number of feature toggles created during the previous window',
|
||||||
},
|
},
|
||||||
archivedCurrentWindow: {
|
archivedCurrentWindow: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
example: 5,
|
||||||
|
description:
|
||||||
|
'The number of feature toggles that were archived during the current window',
|
||||||
},
|
},
|
||||||
archivedPastWindow: {
|
archivedPastWindow: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
example: 5,
|
||||||
|
description:
|
||||||
|
'The number of feature toggles that were archived during the previous window',
|
||||||
},
|
},
|
||||||
projectActivityCurrentWindow: {
|
projectActivityCurrentWindow: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
example: 100,
|
||||||
|
description:
|
||||||
|
'The number of project events that occurred during the current window',
|
||||||
},
|
},
|
||||||
projectActivityPastWindow: {
|
projectActivityPastWindow: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
example: 100,
|
||||||
|
description:
|
||||||
|
'The number of project events that occurred during the previous window',
|
||||||
},
|
},
|
||||||
projectMembersAddedCurrentWindow: {
|
projectMembersAddedCurrentWindow: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
example: 1,
|
||||||
|
description:
|
||||||
|
'The number of members that were added to the project during the current window',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {},
|
components: {},
|
||||||
|
|||||||
@ -6,6 +6,7 @@ export const projectsSchema = {
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
required: ['version', 'projects'],
|
required: ['version', 'projects'],
|
||||||
|
description: 'An overview of all the projects in the Unleash instance',
|
||||||
properties: {
|
properties: {
|
||||||
version: {
|
version: {
|
||||||
type: 'integer',
|
type: 'integer',
|
||||||
@ -15,6 +16,7 @@ export const projectsSchema = {
|
|||||||
items: {
|
items: {
|
||||||
$ref: '#/components/schemas/projectSchema',
|
$ref: '#/components/schemas/projectSchema',
|
||||||
},
|
},
|
||||||
|
description: 'A list of projects in the Unleash instance',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
|||||||
@ -17,8 +17,8 @@ import { serializeDates } from '../../../types/serialize-dates';
|
|||||||
import { createResponseSchema } from '../../../openapi/util/create-response-schema';
|
import { createResponseSchema } from '../../../openapi/util/create-response-schema';
|
||||||
import { IAuthRequest } from '../../unleash-types';
|
import { IAuthRequest } from '../../unleash-types';
|
||||||
import {
|
import {
|
||||||
HealthOverviewSchema,
|
ProjectOverviewSchema,
|
||||||
healthOverviewSchema,
|
projectOverviewSchema,
|
||||||
} from '../../../../lib/openapi';
|
} from '../../../../lib/openapi';
|
||||||
import { IArchivedQuery, IProjectParam } from '../../../types/model';
|
import { IArchivedQuery, IProjectParam } from '../../../types/model';
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ export default class ProjectApi extends Controller {
|
|||||||
|
|
||||||
async getProjectOverview(
|
async getProjectOverview(
|
||||||
req: IAuthRequest<IProjectParam, unknown, unknown, IArchivedQuery>,
|
req: IAuthRequest<IProjectParam, unknown, unknown, IArchivedQuery>,
|
||||||
res: Response<HealthOverviewSchema>,
|
res: Response<ProjectOverviewSchema>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { projectId } = req.params;
|
const { projectId } = req.params;
|
||||||
const { archived } = req.query;
|
const { archived } = req.query;
|
||||||
@ -105,7 +105,7 @@ export default class ProjectApi extends Controller {
|
|||||||
this.openApiService.respondWithValidation(
|
this.openApiService.respondWithValidation(
|
||||||
200,
|
200,
|
||||||
res,
|
res,
|
||||||
healthOverviewSchema.$id,
|
projectOverviewSchema.$id,
|
||||||
serializeDates(overview),
|
serializeDates(overview),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -183,7 +183,7 @@ export interface IProjectOverview {
|
|||||||
health: number;
|
health: number;
|
||||||
favorite?: boolean;
|
favorite?: boolean;
|
||||||
updatedAt?: Date;
|
updatedAt?: Date;
|
||||||
stats: IProjectStats | {};
|
stats?: IProjectStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IProjectHealthReport extends IProjectOverview {
|
export interface IProjectHealthReport extends IProjectOverview {
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
import dbInit, { ITestDb } from '../../../helpers/database-init';
|
import dbInit, { ITestDb } from '../../../helpers/database-init';
|
||||||
import { IUnleashTest, setupApp } from '../../../helpers/test-helper';
|
import {
|
||||||
|
IUnleashTest,
|
||||||
|
setupAppWithCustomConfig,
|
||||||
|
} 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';
|
||||||
|
|
||||||
@ -8,7 +11,13 @@ let db: ITestDb;
|
|||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('project_environments_api_serial', getLogger);
|
db = await dbInit('project_environments_api_serial', getLogger);
|
||||||
app = await setupApp(db.stores);
|
app = await setupAppWithCustomConfig(db.stores, {
|
||||||
|
experimental: {
|
||||||
|
flags: {
|
||||||
|
strictSchemaValidation: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
import dbInit, { ITestDb } from '../../../helpers/database-init';
|
import dbInit, { ITestDb } from '../../../helpers/database-init';
|
||||||
import { IUnleashTest, setupApp } from '../../../helpers/test-helper';
|
import {
|
||||||
|
IUnleashTest,
|
||||||
|
setupAppWithCustomConfig,
|
||||||
|
} 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 {
|
import {
|
||||||
@ -85,7 +88,13 @@ const updateStrategy = async (
|
|||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('feature_strategy_api_serial', getLogger);
|
db = await dbInit('feature_strategy_api_serial', getLogger);
|
||||||
app = await setupApp(db.stores);
|
app = await setupAppWithCustomConfig(db.stores, {
|
||||||
|
experimental: {
|
||||||
|
flags: {
|
||||||
|
strictSchemaValidation: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import dbInit from '../../../helpers/database-init';
|
import dbInit from '../../../helpers/database-init';
|
||||||
import { setupApp } from '../../../helpers/test-helper';
|
import { setupAppWithCustomConfig } from '../../../helpers/test-helper';
|
||||||
import getLogger from '../../../../fixtures/no-logger';
|
import getLogger from '../../../../fixtures/no-logger';
|
||||||
import ProjectStore from '../../../../../lib/db/project-store';
|
import ProjectStore from '../../../../../lib/db/project-store';
|
||||||
|
|
||||||
@ -10,7 +10,13 @@ let projectStore: ProjectStore;
|
|||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('projects_api_serial', getLogger);
|
db = await dbInit('projects_api_serial', getLogger);
|
||||||
app = await setupApp(db.stores);
|
app = await setupAppWithCustomConfig(db.stores, {
|
||||||
|
experimental: {
|
||||||
|
flags: {
|
||||||
|
strictSchemaValidation: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
projectStore = db.stores.projectStore;
|
projectStore = db.stores.projectStore;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import dbInit from '../../helpers/database-init';
|
import dbInit from '../../helpers/database-init';
|
||||||
import { setupApp } from '../../helpers/test-helper';
|
import { setupAppWithCustomConfig } from '../../helpers/test-helper';
|
||||||
import getLogger from '../../../fixtures/no-logger';
|
import getLogger from '../../../fixtures/no-logger';
|
||||||
|
|
||||||
let app;
|
let app;
|
||||||
@ -7,7 +7,13 @@ let db;
|
|||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('strategy_api_serial', getLogger);
|
db = await dbInit('strategy_api_serial', getLogger);
|
||||||
app = await setupApp(db.stores);
|
app = await setupAppWithCustomConfig(db.stores, {
|
||||||
|
experimental: {
|
||||||
|
flags: {
|
||||||
|
strictSchemaValidation: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
|
|||||||
@ -883,22 +883,33 @@ exports[`should serve the OpenAPI spec 1`] = `
|
|||||||
},
|
},
|
||||||
"environmentSchema": {
|
"environmentSchema": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
"description": "A definition of the project environment",
|
||||||
"properties": {
|
"properties": {
|
||||||
"apiTokenCount": {
|
"apiTokenCount": {
|
||||||
|
"description": "The number of API tokens for the project environment",
|
||||||
|
"example": 6,
|
||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"enabled": {
|
"enabled": {
|
||||||
|
"description": "\`true\` if the environment is enabled for the project, otherwise \`false\`.",
|
||||||
|
"example": true,
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
},
|
},
|
||||||
"enabledToggleCount": {
|
"enabledToggleCount": {
|
||||||
|
"description": "The number of enabled toggles for the project environment",
|
||||||
|
"example": 10,
|
||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
|
"description": "The name of the environment",
|
||||||
|
"example": "my-dev-env",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"projectCount": {
|
"projectCount": {
|
||||||
|
"description": "The number of projects with this environment",
|
||||||
|
"example": 10,
|
||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
@ -906,9 +917,13 @@ exports[`should serve the OpenAPI spec 1`] = `
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
},
|
},
|
||||||
"sortOrder": {
|
"sortOrder": {
|
||||||
|
"description": "The sort order of the environment in the environments list",
|
||||||
|
"example": 3,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"type": {
|
"type": {
|
||||||
|
"description": "The type of the environment",
|
||||||
|
"example": "development",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1143,35 +1158,47 @@ exports[`should serve the OpenAPI spec 1`] = `
|
|||||||
},
|
},
|
||||||
"featureEnvironmentSchema": {
|
"featureEnvironmentSchema": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
"description": "A detailed description of the feature environment",
|
||||||
"properties": {
|
"properties": {
|
||||||
"enabled": {
|
"enabled": {
|
||||||
|
"description": "\`true\` if the feature is enabled for the environment, otherwise \`false\`.",
|
||||||
|
"example": true,
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
},
|
},
|
||||||
"environment": {
|
"environment": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"featureName": {
|
"featureName": {
|
||||||
|
"example": "disable-comments",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
|
"description": "The name of the environment",
|
||||||
|
"example": "my-dev-env",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"sortOrder": {
|
"sortOrder": {
|
||||||
|
"description": "The sort order of the feature environment in the feature environments list",
|
||||||
|
"example": 3,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"strategies": {
|
"strategies": {
|
||||||
|
"description": "A list of activation strategies for the feature environment",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/components/schemas/featureStrategySchema",
|
"$ref": "#/components/schemas/featureStrategySchema",
|
||||||
},
|
},
|
||||||
"type": "array",
|
"type": "array",
|
||||||
},
|
},
|
||||||
"type": {
|
"type": {
|
||||||
|
"description": "The type of the environment",
|
||||||
|
"example": "development",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"variantCount": {
|
"variantCount": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"variants": {
|
"variants": {
|
||||||
|
"description": "A list of variants for the feature environment",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/components/schemas/variantSchema",
|
"$ref": "#/components/schemas/variantSchema",
|
||||||
},
|
},
|
||||||
@ -1232,52 +1259,72 @@ exports[`should serve the OpenAPI spec 1`] = `
|
|||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
"archived": {
|
"archived": {
|
||||||
|
"description": "\`true\` if the feature is archived, otherwise \`false\`.",
|
||||||
|
"example": true,
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
},
|
},
|
||||||
"archivedAt": {
|
"archivedAt": {
|
||||||
|
"example": "2023-01-29T15:21:39.975Z",
|
||||||
"format": "date-time",
|
"format": "date-time",
|
||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"createdAt": {
|
"createdAt": {
|
||||||
|
"example": "2023-01-28T15:21:39.975Z",
|
||||||
"format": "date-time",
|
"format": "date-time",
|
||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
|
"description": "Detailed description of the feature",
|
||||||
|
"example": "Controls disabling of the comments section in case of an incident",
|
||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"enabled": {
|
"enabled": {
|
||||||
|
"example": true,
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
},
|
},
|
||||||
"environments": {
|
"environments": {
|
||||||
|
"description": "The list of environments where the feature can be used",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/components/schemas/featureEnvironmentSchema",
|
"$ref": "#/components/schemas/featureEnvironmentSchema",
|
||||||
},
|
},
|
||||||
"type": "array",
|
"type": "array",
|
||||||
},
|
},
|
||||||
"favorite": {
|
"favorite": {
|
||||||
|
"description": "\`true\` if the feature was favorited, otherwise \`false\`.",
|
||||||
|
"example": true,
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
},
|
},
|
||||||
"impressionData": {
|
"impressionData": {
|
||||||
|
"description": "\`true\` if the impression data collection is enabled for the feature, otherwise \`false\`.",
|
||||||
|
"example": false,
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
},
|
},
|
||||||
"lastSeenAt": {
|
"lastSeenAt": {
|
||||||
|
"example": "2023-01-28T16:21:39.975Z",
|
||||||
"format": "date-time",
|
"format": "date-time",
|
||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
|
"description": "Unique feature name",
|
||||||
|
"example": "disable-comments",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"project": {
|
"project": {
|
||||||
|
"description": "Name of the project the feature belongs to",
|
||||||
|
"example": "dx-squad",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"stale": {
|
"stale": {
|
||||||
|
"description": "\`true\` if the feature is stale based on the age and feature type, otherwise \`false\`.",
|
||||||
|
"example": false,
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
},
|
},
|
||||||
"tags": {
|
"tags": {
|
||||||
|
"description": "The list of feature tags",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/components/schemas/tagSchema",
|
"$ref": "#/components/schemas/tagSchema",
|
||||||
},
|
},
|
||||||
@ -1285,9 +1332,12 @@ exports[`should serve the OpenAPI spec 1`] = `
|
|||||||
"type": "array",
|
"type": "array",
|
||||||
},
|
},
|
||||||
"type": {
|
"type": {
|
||||||
|
"description": "Type of the toggle e.g. experiment, kill-switch, release, operational, permission",
|
||||||
|
"example": "kill-switch",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"variants": {
|
"variants": {
|
||||||
|
"description": "The list of feature variants",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/components/schemas/variantSchema",
|
"$ref": "#/components/schemas/variantSchema",
|
||||||
},
|
},
|
||||||
@ -2428,43 +2478,64 @@ exports[`should serve the OpenAPI spec 1`] = `
|
|||||||
},
|
},
|
||||||
"projectOverviewSchema": {
|
"projectOverviewSchema": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
"description": "A high-level overview of a project. It contains information such as project statistics, the name of the project, what members and what features it contains, etc.",
|
||||||
"properties": {
|
"properties": {
|
||||||
"description": {
|
"description": {
|
||||||
|
"description": "Additional information about the project",
|
||||||
|
"example": "DX squad feature release",
|
||||||
|
"nullable": true,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"environments": {
|
"environments": {
|
||||||
|
"description": "The environments that are enabled for this project",
|
||||||
|
"example": [
|
||||||
|
"development",
|
||||||
|
"production",
|
||||||
|
],
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"type": "array",
|
"type": "array",
|
||||||
},
|
},
|
||||||
"favorite": {
|
"favorite": {
|
||||||
|
"description": "\`true\` if the project was favorited, otherwise \`false\`.",
|
||||||
|
"example": true,
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
},
|
},
|
||||||
"features": {
|
"features": {
|
||||||
|
"description": "The full list of features in this project (excluding archived features)",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/components/schemas/featureSchema",
|
"$ref": "#/components/schemas/featureSchema",
|
||||||
},
|
},
|
||||||
"type": "array",
|
"type": "array",
|
||||||
},
|
},
|
||||||
"health": {
|
"health": {
|
||||||
|
"description": "An indicator of the [project's health](https://docs.getunleash.io/reference/technical-debt#health-rating) on a scale from 0 to 100",
|
||||||
|
"example": 50,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"members": {
|
"members": {
|
||||||
|
"description": "The number of members this project has",
|
||||||
|
"example": 4,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
|
"description": "The name of this project",
|
||||||
|
"example": "dx-squad",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"stats": {
|
"stats": {
|
||||||
"$ref": "#/components/schemas/projectStatsSchema",
|
"$ref": "#/components/schemas/projectStatsSchema",
|
||||||
|
"description": "Project statistics",
|
||||||
},
|
},
|
||||||
"updatedAt": {
|
"updatedAt": {
|
||||||
|
"example": "2023-02-10T08:36:35.262Z",
|
||||||
"format": "date-time",
|
"format": "date-time",
|
||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"version": {
|
"version": {
|
||||||
|
"example": 1,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -2476,33 +2547,46 @@ exports[`should serve the OpenAPI spec 1`] = `
|
|||||||
},
|
},
|
||||||
"projectSchema": {
|
"projectSchema": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
"description": "A definition of the project used for projects listing purposes",
|
||||||
"properties": {
|
"properties": {
|
||||||
"changeRequestsEnabled": {
|
|
||||||
"type": "boolean",
|
|
||||||
},
|
|
||||||
"createdAt": {
|
"createdAt": {
|
||||||
"format": "date-time",
|
"format": "date-time",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
|
"description": "Additional information about the project",
|
||||||
|
"example": "DX squad feature release",
|
||||||
|
"nullable": true,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"favorite": {
|
"favorite": {
|
||||||
|
"description": "\`true\` if the project was favorited, otherwise \`false\`.",
|
||||||
|
"example": true,
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
},
|
},
|
||||||
"featureCount": {
|
"featureCount": {
|
||||||
|
"description": "The number of features this project has",
|
||||||
|
"example": 10,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"health": {
|
"health": {
|
||||||
|
"description": "An indicator of the [project's health](https://docs.getunleash.io/reference/technical-debt#health-rating) on a scale from 0 to 100",
|
||||||
|
"example": 50,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
|
"description": "The id of this project",
|
||||||
|
"example": "dx-squad",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"memberCount": {
|
"memberCount": {
|
||||||
|
"description": "The number of members this project has",
|
||||||
|
"example": 4,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
|
"description": "The name of this project",
|
||||||
|
"example": "DX-Squad",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"updatedAt": {
|
"updatedAt": {
|
||||||
@ -2519,32 +2603,55 @@ exports[`should serve the OpenAPI spec 1`] = `
|
|||||||
},
|
},
|
||||||
"projectStatsSchema": {
|
"projectStatsSchema": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
"description": "Statistics for a project, including the average time to production, number of features created, the project activity and more.
|
||||||
|
|
||||||
|
Stats are divided into current and previous **windows**.
|
||||||
|
- The **current window** is the past 30 days.
|
||||||
|
- The **previous window** is the 30 days **before** the current window (from 60 to 30 days ago)",
|
||||||
"properties": {
|
"properties": {
|
||||||
"archivedCurrentWindow": {
|
"archivedCurrentWindow": {
|
||||||
|
"description": "The number of feature toggles that were archived during the current window",
|
||||||
|
"example": 5,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"archivedPastWindow": {
|
"archivedPastWindow": {
|
||||||
|
"description": "The number of feature toggles that were archived during the previous window",
|
||||||
|
"example": 5,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"avgTimeToProdCurrentWindow": {
|
"avgTimeToProdCurrentWindow": {
|
||||||
|
"description": "The average time from when a feature was created to when it was enabled in the "production" environment during the current window",
|
||||||
|
"example": 10,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"avgTimeToProdPastWindow": {
|
"avgTimeToProdPastWindow": {
|
||||||
|
"description": "The average time from when a feature was created to when it was enabled in the "production" environment during the previous window",
|
||||||
|
"example": 10,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"createdCurrentWindow": {
|
"createdCurrentWindow": {
|
||||||
|
"description": "The number of feature toggles created during the current window",
|
||||||
|
"example": 15,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"createdPastWindow": {
|
"createdPastWindow": {
|
||||||
|
"description": "The number of feature toggles created during the previous window",
|
||||||
|
"example": 15,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"projectActivityCurrentWindow": {
|
"projectActivityCurrentWindow": {
|
||||||
|
"description": "The number of project events that occurred during the current window",
|
||||||
|
"example": 100,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"projectActivityPastWindow": {
|
"projectActivityPastWindow": {
|
||||||
|
"description": "The number of project events that occurred during the previous window",
|
||||||
|
"example": 100,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"projectMembersAddedCurrentWindow": {
|
"projectMembersAddedCurrentWindow": {
|
||||||
|
"description": "The number of members that were added to the project during the current window",
|
||||||
|
"example": 1,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -2563,8 +2670,10 @@ exports[`should serve the OpenAPI spec 1`] = `
|
|||||||
},
|
},
|
||||||
"projectsSchema": {
|
"projectsSchema": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
"description": "An overview of all the projects in the Unleash instance",
|
||||||
"properties": {
|
"properties": {
|
||||||
"projects": {
|
"projects": {
|
||||||
|
"description": "A list of projects in the Unleash instance",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/components/schemas/projectSchema",
|
"$ref": "#/components/schemas/projectSchema",
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user