diff --git a/src/lib/openapi/index.ts b/src/lib/openapi/index.ts index 9772f3849b..59b07cf92a 100644 --- a/src/lib/openapi/index.ts +++ b/src/lib/openapi/index.ts @@ -133,6 +133,7 @@ import { upsertSegmentSchema, createStrategySchema, updateStrategySchema, + updateFeatureTypeLifetimeSchema, userSchema, usersGroupsBaseSchema, usersSchema, @@ -337,6 +338,7 @@ export const schemas: UnleashSchemas = { upsertSegmentSchema, createStrategySchema, updateStrategySchema, + updateFeatureTypeLifetimeSchema, userSchema, createUserResponseSchema, usersGroupsBaseSchema, diff --git a/src/lib/openapi/spec/index.ts b/src/lib/openapi/spec/index.ts index d20a8acf8b..f7fce2c17a 100644 --- a/src/lib/openapi/spec/index.ts +++ b/src/lib/openapi/spec/index.ts @@ -150,3 +150,4 @@ export * from './telemetry-settings-schema'; export * from './create-strategy-variant-schema'; export * from './strategy-variant-schema'; export * from './client-segment-schema'; +export * from './update-feature-type-lifetime-schema'; diff --git a/src/lib/openapi/spec/update-feature-type-lifetime-schema.ts b/src/lib/openapi/spec/update-feature-type-lifetime-schema.ts new file mode 100644 index 0000000000..fef35dbda4 --- /dev/null +++ b/src/lib/openapi/spec/update-feature-type-lifetime-schema.ts @@ -0,0 +1,25 @@ +import { FromSchema } from 'json-schema-to-ts'; + +export const updateFeatureTypeLifetimeSchema = { + $id: '#/components/schemas/updateFeatureTypeLifetimeSchema', + type: 'object', + required: ['lifetimeDays'], + description: + 'Data used when updating the lifetime of a [feature toggle type](https://docs.getunleash.io/reference/feature-toggle-types).', + properties: { + lifetimeDays: { + description: + 'The new lifetime (in days) that you want to assign to the feature toggle type. If the value is `null` or `0`, then the feature toggles of that type will never be marked as potentially stale. Otherwise, they will be considered potentially stale after the number of days indicated by this property.', + example: 7, + type: 'integer', + nullable: true, + minimum: 0, + maximum: 2147483647, // Postgres' max integer: https://www.postgresql.org/docs/9.1/datatype-numeric.html + }, + }, + components: {}, +} as const; + +export type UpdateFeatureTypeLifetimeSchema = FromSchema< + typeof updateFeatureTypeLifetimeSchema +>; diff --git a/src/lib/routes/admin-api/feature-type.ts b/src/lib/routes/admin-api/feature-type.ts index 97e518d32b..9ab4f19695 100644 --- a/src/lib/routes/admin-api/feature-type.ts +++ b/src/lib/routes/admin-api/feature-type.ts @@ -4,11 +4,22 @@ import FeatureTypeService from '../../services/feature-type-service'; import { Logger } from '../../logger'; import { IUnleashConfig } from '../../types/option'; import { OpenApiService } from '../../services/openapi-service'; -import { NONE } from '../../types/permissions'; -import { FeatureTypesSchema } from '../../openapi/spec/feature-types-schema'; +import { ADMIN, NONE } from '../../types/permissions'; +import { + featureTypesSchema, + FeatureTypesSchema, +} from '../../openapi/spec/feature-types-schema'; import { createResponseSchema } from '../../openapi/util/create-response-schema'; import Controller from '../controller'; -import { getStandardResponses } from '../../openapi'; +import { + createRequestSchema, + FeatureTypeSchema, + getStandardResponses, + UpdateFeatureTypeLifetimeSchema, +} from '../../openapi'; +import { IAuthRequest } from '../unleash-types'; +import { IFlagResolver } from '../../types'; +import NotImplementedError from '../../error/not-implemented-error'; const version = 1; @@ -19,6 +30,8 @@ export class FeatureTypeController extends Controller { private logger: Logger; + private flagResolver: IFlagResolver; + constructor( config: IUnleashConfig, { @@ -29,6 +42,7 @@ export class FeatureTypeController extends Controller { super(config); this.featureTypeService = featureTypeService; this.openApiService = openApiService; + this.flagResolver = config.flagResolver; this.logger = config.getLogger('/admin-api/feature-type.js'); this.route({ @@ -50,15 +64,61 @@ export class FeatureTypeController extends Controller { }), ], }); + + this.route({ + method: 'put', + path: '/:id/lifetime', + handler: this.updateLifetime, + permission: ADMIN, + middleware: [ + openApiService.validPath({ + tags: ['Unstable'], + operationId: 'updateFeatureTypeLifetime', + summary: 'Update feature type lifetime', + description: `Updates the lifetime configuration for the specified [feature toggle type](https://docs.getunleash.io/reference/feature-toggle-types). The expected lifetime is an integer representing the number of days before Unleash marks a feature toggle of that type as potentially stale. If set to \`null\` or \`0\`, then feature toggles of that particular type will never be marked as potentially stale. + +When a feature toggle type's expected lifetime is changed, this will also cause any feature toggles of this type to be reevaluated for potential staleness.`, + responses: { + 200: createResponseSchema('featureTypeSchema'), + ...getStandardResponses(400, 401, 403, 404, 409, 415), + }, + requestBody: createRequestSchema( + 'updateFeatureTypeLifetimeSchema', + ), + }), + ], + }); } async getAllFeatureTypes( req: Request, res: Response, ): Promise { - res.json({ - version, - types: await this.featureTypeService.getAll(), - }); + this.openApiService.respondWithValidation( + 200, + res, + featureTypesSchema.$id, + { + version, + types: await this.featureTypeService.getAll(), + }, + ); + } + + async updateLifetime( + req: IAuthRequest< + { id: string }, + unknown, + UpdateFeatureTypeLifetimeSchema + >, + res: Response, + ): Promise { + if (this.flagResolver.isEnabled('configurableFeatureTypeLifetimes')) { + throw new NotImplementedError( + "This operation isn't implemented yet", + ); + } else { + res.status(409).end(); + } } }