1
0
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:
Jaanus Sellin 2023-02-10 16:05:57 +02:00 committed by GitHub
parent 465e98564f
commit df3ce70c0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 293 additions and 20 deletions

View File

@ -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: {},

View File

@ -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: {

View File

@ -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: {

View File

@ -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: {

View File

@ -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(

View File

@ -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: {},

View File

@ -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: {},

View File

@ -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: {

View File

@ -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),
); );
} }

View File

@ -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 {

View File

@ -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 () => {

View File

@ -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 () => {

View File

@ -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;
}); });

View File

@ -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 () => {

View File

@ -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",
}, },