diff --git a/src/lib/openapi/index.ts b/src/lib/openapi/index.ts index 3cf30adc43..f31284fdc3 100644 --- a/src/lib/openapi/index.ts +++ b/src/lib/openapi/index.ts @@ -1,118 +1,120 @@ import { OpenAPIV3 } from 'openapi-types'; +import { addonParameterSchema } from './spec/addon-parameter-schema'; +import { addonSchema } from './spec/addon-schema'; +import { addonsSchema } from './spec/addons-schema'; +import { addonTypeSchema } from './spec/addon-type-schema'; import { apiTokenSchema } from './spec/api-token-schema'; import { apiTokensSchema } from './spec/api-tokens-schema'; +import { applicationSchema } from './spec/application-schema'; +import { applicationsSchema } from './spec/applications-schema'; +import { bootstrapUiSchema } from './spec/bootstrap-ui-schema'; +import { changePasswordSchema } from './spec/change-password-schema'; +import { clientApplicationSchema } from './spec/client-application-schema'; +import { clientFeatureSchema } from './spec/client-feature-schema'; +import { clientFeaturesQuerySchema } from './spec/client-features-query-schema'; +import { clientFeaturesSchema } from './spec/client-features-schema'; +import { clientMetricsSchema } from './spec/client-metrics-schema'; +import { clientVariantSchema } from './spec/client-variant-schema'; import { cloneFeatureSchema } from './spec/clone-feature-schema'; import { constraintSchema } from './spec/constraint-schema'; import { contextFieldSchema } from './spec/context-field-schema'; import { contextFieldsSchema } from './spec/context-fields-schema'; import { createApiTokenSchema } from './spec/create-api-token-schema'; import { createFeatureSchema } from './spec/create-feature-schema'; -import { createUserSchema } from './spec/create-user-schema'; import { createFeatureStrategySchema } from './spec/create-feature-strategy-schema'; +import { createUserSchema } from './spec/create-user-schema'; +import { dateSchema } from './spec/date-schema'; +import { emailSchema } from './spec/email-schema'; import { environmentSchema } from './spec/environment-schema'; import { environmentsSchema } from './spec/environments-schema'; +import { eventSchema } from './spec/event-schema'; +import { eventsSchema } from './spec/events-schema'; +import { exportParametersSchema } from './spec/export-parameters-schema'; +import { featureEnvironmentMetricsSchema } from './spec/feature-environment-metrics-schema'; import { featureEnvironmentSchema } from './spec/feature-environment-schema'; +import { featureEventsSchema } from './spec/feature-events-schema'; +import { featureMetricsSchema } from './spec/feature-metrics-schema'; import { featureSchema } from './spec/feature-schema'; +import { featuresSchema } from './spec/features-schema'; import { featureStrategySchema } from './spec/feature-strategy-schema'; +import { featureStrategySegmentSchema } from './spec/feature-strategy-segment-schema'; +import { featureTagSchema } from './spec/feature-tag-schema'; import { featureTypeSchema } from './spec/feature-type-schema'; import { featureTypesSchema } from './spec/feature-types-schema'; +import { featureUsageSchema } from './spec/feature-usage-schema'; import { featureVariantsSchema } from './spec/feature-variants-schema'; -import { featuresSchema } from './spec/features-schema'; import { feedbackSchema } from './spec/feedback-schema'; import { healthCheckSchema } from './spec/health-check-schema'; import { healthOverviewSchema } from './spec/health-overview-schema'; import { healthReportSchema } from './spec/health-report-schema'; +import { idSchema } from './spec/id-schema'; import { legalValueSchema } from './spec/legal-value-schema'; import { loginSchema } from './spec/login-schema'; -import { idSchema } from './spec/id-schema'; import { mapValues } from '../util/map-values'; -import { nameSchema } from './spec/name-schema'; import { meSchema } from './spec/me-schema'; +import { nameSchema } from './spec/name-schema'; import { omitKeys } from '../util/omit-keys'; import { overrideSchema } from './spec/override-schema'; import { parametersSchema } from './spec/parameters-schema'; import { passwordSchema } from './spec/password-schema'; -import { patchSchema } from './spec/patch-schema'; import { patchesSchema } from './spec/patches-schema'; +import { patchSchema } from './spec/patch-schema'; import { permissionSchema } from './spec/permission-schema'; import { projectEnvironmentSchema } from './spec/project-environment-schema'; import { projectSchema } from './spec/project-schema'; import { projectsSchema } from './spec/projects-schema'; +import { resetPasswordSchema } from './spec/reset-password-schema'; import { roleSchema } from './spec/role-schema'; +import { segmentSchema } from './spec/segment-schema'; import { sortOrderSchema } from './spec/sort-order-schema'; import { splashSchema } from './spec/splash-schema'; +import { stateSchema } from './spec/state-schema'; +import { strategiesSchema } from './spec/strategies-schema'; +import { strategySchema } from './spec/strategy-schema'; import { tagSchema } from './spec/tag-schema'; import { tagsSchema } from './spec/tags-schema'; import { tagTypeSchema } from './spec/tag-type-schema'; import { tagTypesSchema } from './spec/tag-types-schema'; +import { tagWithVersionSchema } from './spec/tag-with-version-schema'; +import { tokenUserSchema } from './spec/token-user-schema'; import { uiConfigSchema } from './spec/ui-config-schema'; +import { updateApiTokenSchema } from './spec/update-api-token-schema'; import { updateFeatureSchema } from './spec/update-feature-schema'; import { updateFeatureStrategySchema } from './spec/update-feature-strategy-schema'; -import { updateApiTokenSchema } from './spec/update-api-token-schema'; import { updateTagTypeSchema } from './spec/update-tag-type-schema'; -import { upsertContextFieldSchema } from './spec/upsert-context-field-schema'; import { updateUserSchema } from './spec/update-user-schema'; +import { upsertContextFieldSchema } from './spec/upsert-context-field-schema'; +import { upsertStrategySchema } from './spec/upsert-strategy-schema'; import { userSchema } from './spec/user-schema'; import { usersSchema } from './spec/users-schema'; import { usersSearchSchema } from './spec/users-search-schema'; +import { validatePasswordSchema } from './spec/validate-password-schema'; import { validateTagTypeSchema } from './spec/validate-tag-type-schema'; import { variantSchema } from './spec/variant-schema'; import { variantsSchema } from './spec/variants-schema'; import { versionSchema } from './spec/version-schema'; -import { featureEnvironmentMetricsSchema } from './spec/feature-environment-metrics-schema'; -import { featureUsageSchema } from './spec/feature-usage-schema'; -import { featureMetricsSchema } from './spec/feature-metrics-schema'; -import { addonSchema } from './spec/addon-schema'; -import { addonsSchema } from './spec/addons-schema'; -import { addonParameterSchema } from './spec/addon-parameter-schema'; -import { addonTypeSchema } from './spec/addon-type-schema'; -import { applicationSchema } from './spec/application-schema'; -import { applicationsSchema } from './spec/applications-schema'; -import { tagWithVersionSchema } from './spec/tag-with-version-schema'; -import { tokenUserSchema } from './spec/token-user-schema'; -import { changePasswordSchema } from './spec/change-password-schema'; -import { validatePasswordSchema } from './spec/validate-password-schema'; -import { resetPasswordSchema } from './spec/reset-password-schema'; -import { featureStrategySegmentSchema } from './spec/feature-strategy-segment-schema'; -import { segmentSchema } from './spec/segment-schema'; -import { stateSchema } from './spec/state-schema'; -import { featureTagSchema } from './spec/feature-tag-schema'; -import { exportParametersSchema } from './spec/export-parameters-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'; -import { clientFeaturesQuerySchema } from './spec/client-features-query-schema'; -import { clientFeatureSchema } from './spec/client-feature-schema'; -import { clientFeaturesSchema } from './spec/client-features-schema'; -import { eventSchema } from './spec/event-schema'; -import { eventsSchema } from './spec/events-schema'; -import { featureEventsSchema } from './spec/feature-events-schema'; -import { clientApplicationSchema } from './spec/client-application-schema'; -import { clientMetricsSchema } from './spec/client-metrics-schema'; -import { dateSchema } from './spec/date-schema'; -import { clientVariantSchema } from './spec/client-variant-schema'; import { IServerOption } from '../types'; import { URL } from 'url'; // All schemas in `openapi/spec` should be listed here. export const schemas = { + addonParameterSchema, addonSchema, addonsSchema, addonTypeSchema, - addonParameterSchema, apiTokenSchema, apiTokensSchema, applicationSchema, applicationsSchema, - clientApplicationSchema, - clientMetricsSchema, - cloneFeatureSchema, - clientFeatureSchema, - clientFeaturesSchema, - clientVariantSchema, - clientFeaturesQuerySchema, + bootstrapUiSchema, changePasswordSchema, + clientApplicationSchema, + clientFeatureSchema, + clientFeaturesQuerySchema, + clientFeaturesSchema, + clientMetricsSchema, + clientVariantSchema, + cloneFeatureSchema, constraintSchema, contextFieldSchema, contextFieldsSchema, @@ -127,33 +129,33 @@ export const schemas = { eventSchema, eventsSchema, exportParametersSchema, - featureEnvironmentSchema, featureEnvironmentMetricsSchema, + featureEnvironmentSchema, featureEventsSchema, - featureSchema, featureMetricsSchema, - featureUsageSchema, + featureSchema, + featuresSchema, featureStrategySchema, featureStrategySegmentSchema, featureTagSchema, featureTypeSchema, featureTypesSchema, + featureUsageSchema, featureVariantsSchema, - featuresSchema, feedbackSchema, healthCheckSchema, healthOverviewSchema, healthReportSchema, + idSchema, legalValueSchema, loginSchema, - nameSchema, - idSchema, meSchema, + nameSchema, overrideSchema, parametersSchema, passwordSchema, - patchSchema, patchesSchema, + patchSchema, permissionSchema, projectEnvironmentSchema, projectSchema, @@ -167,24 +169,24 @@ export const schemas = { strategiesSchema, strategySchema, tagSchema, - tagWithVersionSchema, tagsSchema, tagTypeSchema, tagTypesSchema, + tagWithVersionSchema, tokenUserSchema, uiConfigSchema, + updateApiTokenSchema, updateFeatureSchema, updateFeatureStrategySchema, - updateApiTokenSchema, updateTagTypeSchema, + updateUserSchema, upsertContextFieldSchema, upsertStrategySchema, - validatePasswordSchema, - validateTagTypeSchema, - updateUserSchema, userSchema, usersSchema, usersSearchSchema, + validatePasswordSchema, + validateTagTypeSchema, variantSchema, variantsSchema, versionSchema, diff --git a/src/lib/openapi/spec/bootstrap-ui-schema.test.ts b/src/lib/openapi/spec/bootstrap-ui-schema.test.ts new file mode 100644 index 0000000000..e49209b12d --- /dev/null +++ b/src/lib/openapi/spec/bootstrap-ui-schema.test.ts @@ -0,0 +1,157 @@ +import { validateSchema } from '../validate'; +import { BootstrapUiSchema } from './bootstrap-ui-schema'; + +test('bootstrapUiSchema', () => { + const data: BootstrapUiSchema = { + uiConfig: { + flags: { E: true }, + authenticationType: 'open-source', + unleashUrl: 'http://localhost:4242', + version: '4.14.0-beta.0', + baseUriPath: '', + versionInfo: { + current: { oss: '4.14.0-beta.0', enterprise: '' }, + latest: {}, + isLatest: true, + instanceId: '51c9190a-4ff5-4f47-b73a-7aebe06f9331', + }, + }, + user: { + isAPI: false, + id: 1, + username: 'admin', + imageUrl: + 'https://gravatar.com/avatar/21232f297a57a5a743894a0e4a801fc3?size=42&default=retro', + seenAt: '2022-06-27T12:19:15.838Z', + loginAttempts: 0, + createdAt: '2022-04-08T10:59:25.072Z', + permissions: [ + { permission: 'READ_API_TOKEN' }, + { + project: 'default', + environment: 'staging', + permission: 'CREATE_FEATURE_STRATEGY', + }, + { + project: 'default', + environment: 'staging', + permission: 'UPDATE_FEATURE_STRATEGY', + }, + { project: 'default', permission: 'UPDATE_FEATURE' }, + ], + }, + email: false, + context: [ + { + name: 'appName', + description: 'Allows you to constrain on application name', + stickiness: false, + sortOrder: 2, + legalValues: [], + createdAt: '2022-04-08T10:59:24.374Z', + }, + { + name: 'currentTime', + description: '', + stickiness: false, + sortOrder: 10, + legalValues: [], + createdAt: '2022-05-18T08:15:18.917Z', + }, + { + name: 'environment', + description: + 'Allows you to constrain on application environment', + stickiness: false, + sortOrder: 0, + legalValues: [], + createdAt: '2022-04-08T10:59:24.374Z', + }, + { + name: 'userId', + description: 'Allows you to constrain on userId', + stickiness: false, + sortOrder: 1, + legalValues: [], + createdAt: '2022-04-08T10:59:24.374Z', + }, + ], + featureTypes: [ + { + id: 'release', + name: 'Release', + description: + 'Release feature toggles are used to release new features.', + lifetimeDays: 40, + }, + { + id: 'experiment', + name: 'Experiment', + description: + 'Experiment feature toggles are used to test and verify multiple different versions of a feature.', + lifetimeDays: 40, + }, + { + id: 'operational', + name: 'Operational', + description: + 'Operational feature toggles are used to control aspects of a rollout.', + lifetimeDays: 7, + }, + ], + tagTypes: [ + { + name: 'simple', + description: 'Used to simplify filtering of features', + icon: '#', + }, + { name: 'hashtag', description: '', icon: null }, + ], + strategies: [ + { + displayName: 'Standard', + name: 'default', + editable: false, + description: + 'The standard strategy is strictly on / off for your entire userbase.', + parameters: [], + deprecated: false, + }, + { + displayName: null, + name: 'gradualRolloutRandom', + editable: true, + description: + 'Randomly activate the feature toggle. No stickiness.', + parameters: [ + { + name: 'percentage', + type: 'percentage', + description: '', + required: false, + }, + ], + deprecated: true, + }, + ], + projects: [ + { + name: 'Default', + id: 'default', + description: 'Default project', + health: 74, + featureCount: 10, + memberCount: 3, + updatedAt: '2022-06-28T17:33:53.963Z', + }, + ], + }; + + expect( + validateSchema('#/components/schemas/bootstrapUiSchema', {}), + ).not.toBeUndefined(); + + expect( + validateSchema('#/components/schemas/bootstrapUiSchema', data), + ).toBeUndefined(); +}); diff --git a/src/lib/openapi/spec/bootstrap-ui-schema.ts b/src/lib/openapi/spec/bootstrap-ui-schema.ts new file mode 100644 index 0000000000..b810f0032a --- /dev/null +++ b/src/lib/openapi/spec/bootstrap-ui-schema.ts @@ -0,0 +1,94 @@ +import { FromSchema } from 'json-schema-to-ts'; +import { uiConfigSchema } from './ui-config-schema'; +import { userSchema } from './user-schema'; +import { permissionSchema } from './permission-schema'; +import { featureTypeSchema } from './feature-type-schema'; +import { tagTypeSchema } from './tag-type-schema'; +import { contextFieldSchema } from './context-field-schema'; +import { strategySchema } from './strategy-schema'; +import { projectSchema } from './project-schema'; +import { versionSchema } from './version-schema'; +import { legalValueSchema } from './legal-value-schema'; + +export const bootstrapUiSchema = { + $id: '#/components/schemas/bootstrapUiSchema', + type: 'object', + additionalProperties: false, + required: [ + 'uiConfig', + 'user', + 'email', + 'context', + 'featureTypes', + 'tagTypes', + 'strategies', + 'projects', + ], + properties: { + uiConfig: { + $ref: '#/components/schemas/uiConfigSchema', + }, + user: { + type: 'object', + required: [...userSchema.required], + properties: { + ...userSchema.properties, + permissions: { + type: 'array', + items: { + $ref: '#/components/schemas/permissionSchema', + }, + }, + }, + }, + email: { + type: 'boolean', + }, + context: { + type: 'array', + items: { + $ref: '#/components/schemas/contextFieldSchema', + }, + }, + featureTypes: { + type: 'array', + items: { + $ref: '#/components/schemas/featureTypeSchema', + }, + }, + tagTypes: { + type: 'array', + items: { + $ref: '#/components/schemas/tagTypeSchema', + }, + }, + strategies: { + type: 'array', + items: { + $ref: '#/components/schemas/strategySchema', + }, + }, + projects: { + type: 'array', + items: { + $ref: '#/components/schemas/projectSchema', + }, + }, + }, + components: { + schemas: { + uiConfigSchema, + userSchema, + permissionSchema, + contextFieldSchema, + featureTypeSchema, + tagTypeSchema, + strategySchema, + projectSchema, + versionSchema, + legalValueSchema, + }, + }, +} as const; + +export type BootstrapUiSchema = FromSchema; diff --git a/src/lib/openapi/spec/tag-type-schema.ts b/src/lib/openapi/spec/tag-type-schema.ts index 2c4319edd0..f746ab052f 100644 --- a/src/lib/openapi/spec/tag-type-schema.ts +++ b/src/lib/openapi/spec/tag-type-schema.ts @@ -14,6 +14,7 @@ export const tagTypeSchema = { }, icon: { type: 'string', + nullable: true, }, }, components: {}, diff --git a/src/lib/openapi/spec/ui-config-schema.ts b/src/lib/openapi/spec/ui-config-schema.ts index 3a66c4d0ea..439b81630d 100644 --- a/src/lib/openapi/spec/ui-config-schema.ts +++ b/src/lib/openapi/spec/ui-config-schema.ts @@ -5,15 +5,7 @@ export const uiConfigSchema = { $id: '#/components/schemas/uiConfigSchema', type: 'object', additionalProperties: false, - required: [ - 'version', - 'unleashUrl', - 'baseUriPath', - 'versionInfo', - 'disablePasswordAuth', - 'segmentValuesLimit', - 'strategySegmentsLimit', - ], + required: ['version', 'unleashUrl', 'baseUriPath', 'versionInfo'], properties: { slogan: { type: 'string', diff --git a/src/lib/routes/admin-api/bootstrap-ui.test.ts b/src/lib/routes/admin-api/bootstrap-ui.test.ts new file mode 100644 index 0000000000..3a286d2d34 --- /dev/null +++ b/src/lib/routes/admin-api/bootstrap-ui.test.ts @@ -0,0 +1,63 @@ +import supertest from 'supertest'; +import { createTestConfig } from '../../../test/config/test-config'; +import { randomId } from '../../util/random-id'; + +import createStores from '../../../test/fixtures/store'; +import getApp from '../../app'; +import { createServices } from '../../services'; +const uiConfig = { + headerBackground: 'red', + slogan: 'hello', +}; + +async function getSetup() { + const base = `/random${randomId()}`; + const config = createTestConfig({ + server: { baseUriPath: base }, + ui: uiConfig, + }); + const stores = createStores(); + const services = createServices(stores, config); + + const app = await getApp(config, stores, services); + + return { + base, + request: supertest(app), + destroy: () => { + services.versionService.destroy(); + services.clientInstanceService.destroy(); + services.apiTokenService.destroy(); + }, + }; +} + +let request; +let base; +let destroy; + +beforeEach(async () => { + const setup = await getSetup(); + request = setup.request; + base = setup.base; + destroy = setup.destroy; +}); + +afterEach(() => { + destroy(); +}); + +test('should get ui config', async () => { + const { body } = await request + .get(`${base}/api/admin/ui-bootstrap`) + .expect('Content-Type', /json/) + .expect(200); + + expect(body.uiConfig.slogan).toEqual('hello'); + expect(body.email).toEqual(false); + expect(body.user).toHaveProperty('permissions'); + expect(body.context).toBeInstanceOf(Array); + expect(body.tagTypes).toBeInstanceOf(Array); + expect(body.strategies).toBeInstanceOf(Array); + expect(body.projects).toBeInstanceOf(Array); +}); diff --git a/src/lib/routes/admin-api/bootstrap.ts b/src/lib/routes/admin-api/bootstrap-ui.ts similarity index 66% rename from src/lib/routes/admin-api/bootstrap.ts rename to src/lib/routes/admin-api/bootstrap-ui.ts index 0f27d9d31a..486e805467 100644 --- a/src/lib/routes/admin-api/bootstrap.ts +++ b/src/lib/routes/admin-api/bootstrap-ui.ts @@ -19,8 +19,20 @@ import { ITagType } from '../../types/stores/tag-type-store'; import { IStrategy } from '../../types/stores/strategy-store'; import { IProject } from '../../types/model'; import { IUserPermission } from '../../types/stores/access-store'; +import { OpenApiService } from '../../services/openapi-service'; +import { NONE } from '../../types/permissions'; +import { createResponseSchema } from '../../openapi'; +import { + BootstrapUiSchema, + bootstrapUiSchema, +} from '../../openapi/spec/bootstrap-ui-schema'; +import { serializeDates } from '../../types/serialize-dates'; -class BootstrapController extends Controller { +/** + * Provides admin UI configuration. + * Not to be confused with SDK bootstrapping. + */ +class BootstrapUIController extends Controller { private logger: Logger; private accessService: AccessService; @@ -39,6 +51,8 @@ class BootstrapController extends Controller { private versionService: VersionService; + private openApiService: OpenApiService; + constructor( config: IUnleashConfig, { @@ -50,6 +64,7 @@ class BootstrapController extends Controller { emailService, versionService, featureTypeService, + openApiService, }: Pick< IUnleashServices, | 'contextService' @@ -60,6 +75,7 @@ class BootstrapController extends Controller { | 'emailService' | 'versionService' | 'featureTypeService' + | 'openApiService' >, ) { super(config); @@ -71,15 +87,30 @@ class BootstrapController extends Controller { this.featureTypeService = featureTypeService; this.emailService = emailService; this.versionService = versionService; + this.openApiService = openApiService; - this.logger = config.getLogger( - 'routes/admin-api/bootstrap-controller.ts', - ); - - this.get('/', this.bootstrap); + this.logger = config.getLogger('routes/admin-api/bootstrap-ui.ts'); + this.route({ + method: 'get', + path: '', + handler: this.bootstrap, + permission: NONE, + middleware: [ + openApiService.validPath({ + tags: ['other'], + operationId: 'getBootstrapUiData', + responses: { + 202: createResponseSchema('bootstrapUiSchema'), + }, + }), + ], + }); } - async bootstrap(req: AuthedRequest, res: Response): Promise { + async bootstrap( + req: AuthedRequest, + res: Response, + ): Promise { const jobs: [ Promise, Promise, @@ -117,18 +148,26 @@ class BootstrapController extends Controller { versionInfo, }; - res.json({ - uiConfig, - user: { ...req.user, permissions: userPermissions }, - email: this.emailService.isEnabled(), - context, - featureTypes, - tagTypes, - strategies, - projects, - }); + this.openApiService.respondWithValidation( + 200, + res, + bootstrapUiSchema.$id, + { + uiConfig, + user: { + ...serializeDates(req.user), + permissions: userPermissions, + }, + email: this.emailService.isEnabled(), + context: serializeDates(context), + featureTypes, + tagTypes, + strategies, + projects: serializeDates(projects), + }, + ); } } -export default BootstrapController; -module.exports = BootstrapController; +export default BootstrapUIController; +module.exports = BootstrapUIController; diff --git a/src/lib/routes/admin-api/index.ts b/src/lib/routes/admin-api/index.ts index 3e697dfcd2..dc0f4c72a8 100644 --- a/src/lib/routes/admin-api/index.ts +++ b/src/lib/routes/admin-api/index.ts @@ -12,7 +12,7 @@ import UserController from './user'; import ConfigController from './config'; import { ContextController } from './context'; import ClientMetricsController from './client-metrics'; -import BootstrapController from './bootstrap'; +import BootstrapUIController from './bootstrap-ui'; import StateController from './state'; import TagController from './tag'; import TagTypeController from './tag-type'; @@ -65,7 +65,7 @@ class AdminApi extends Controller { ); this.app.use( '/ui-bootstrap', - new BootstrapController(config, services).router, + new BootstrapUIController(config, services).router, ); this.app.use( '/context', diff --git a/src/test/e2e/api/openapi/__snapshots__/openapi.e2e.test.ts.snap b/src/test/e2e/api/openapi/__snapshots__/openapi.e2e.test.ts.snap index 3b6fbfcfd7..0d6a577bf4 100644 --- a/src/test/e2e/api/openapi/__snapshots__/openapi.e2e.test.ts.snap +++ b/src/test/e2e/api/openapi/__snapshots__/openapi.e2e.test.ts.snap @@ -293,6 +293,111 @@ Object { }, "type": "object", }, + "bootstrapUiSchema": Object { + "additionalProperties": false, + "properties": Object { + "context": Object { + "items": Object { + "$ref": "#/components/schemas/contextFieldSchema", + }, + "type": "array", + }, + "email": Object { + "type": "boolean", + }, + "featureTypes": Object { + "items": Object { + "$ref": "#/components/schemas/featureTypeSchema", + }, + "type": "array", + }, + "projects": Object { + "items": Object { + "$ref": "#/components/schemas/projectSchema", + }, + "type": "array", + }, + "strategies": Object { + "items": Object { + "$ref": "#/components/schemas/strategySchema", + }, + "type": "array", + }, + "tagTypes": Object { + "items": Object { + "$ref": "#/components/schemas/tagTypeSchema", + }, + "type": "array", + }, + "uiConfig": Object { + "$ref": "#/components/schemas/uiConfigSchema", + }, + "user": Object { + "properties": Object { + "createdAt": Object { + "format": "date-time", + "type": "string", + }, + "email": Object { + "type": "string", + }, + "emailSent": Object { + "type": "boolean", + }, + "id": Object { + "type": "number", + }, + "imageUrl": Object { + "type": "string", + }, + "inviteLink": Object { + "type": "string", + }, + "isAPI": Object { + "type": "boolean", + }, + "loginAttempts": Object { + "type": "number", + }, + "name": Object { + "type": "string", + }, + "permissions": Object { + "items": Object { + "$ref": "#/components/schemas/permissionSchema", + }, + "type": "array", + }, + "rootRole": Object { + "type": "number", + }, + "seenAt": Object { + "format": "date-time", + "nullable": true, + "type": "string", + }, + "username": Object { + "type": "string", + }, + }, + "required": Array [ + "id", + ], + "type": "object", + }, + }, + "required": Array [ + "uiConfig", + "user", + "email", + "context", + "featureTypes", + "tagTypes", + "strategies", + "projects", + ], + "type": "object", + }, "changePasswordSchema": Object { "additionalProperties": false, "properties": Object { @@ -1962,6 +2067,7 @@ Object { "type": "string", }, "icon": Object { + "nullable": true, "type": "string", }, "name": Object { @@ -2122,9 +2228,6 @@ Object { "unleashUrl", "baseUriPath", "versionInfo", - "disablePasswordAuth", - "segmentValuesLimit", - "strategySegmentsLimit", ], "type": "object", }, @@ -5167,6 +5270,26 @@ If the provided project does not exist, the list of events will be empty.", ], }, }, + "/api/admin/ui-bootstrap": Object { + "get": Object { + "operationId": "getBootstrapUiData", + "responses": Object { + "202": Object { + "content": Object { + "application/json": Object { + "schema": Object { + "$ref": "#/components/schemas/bootstrapUiSchema", + }, + }, + }, + "description": "bootstrapUiSchema", + }, + }, + "tags": Array [ + "other", + ], + }, + }, "/api/admin/ui-config": Object { "get": Object { "operationId": "getUIConfig",