From 8f5bda6b8a0a89ed3e8908dee38b701512378907 Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Thu, 13 Jul 2023 13:27:52 +0200 Subject: [PATCH] openapi: document operations for admin ui feedback (#4226) This PR updates the endpoint documentation and schemas related to Admin UI feedback. --- src/lib/openapi/index.ts | 8 +++- .../openapi/spec/feedback-create-schema.ts | 24 +++++++++++ ...-schema.ts => feedback-response-schema.ts} | 24 +++++------ .../openapi/spec/feedback-update-schema.ts | 30 ++++++++++++++ src/lib/openapi/spec/index.ts | 4 +- src/lib/openapi/spec/me-schema.ts | 6 +-- src/lib/routes/admin-api/user-feedback.ts | 40 ++++++++++++------- src/test/e2e/api/admin/feedback.e2e.test.ts | 8 +++- 8 files changed, 111 insertions(+), 33 deletions(-) create mode 100644 src/lib/openapi/spec/feedback-create-schema.ts rename src/lib/openapi/spec/{feedback-schema.ts => feedback-response-schema.ts} (63%) create mode 100644 src/lib/openapi/spec/feedback-update-schema.ts diff --git a/src/lib/openapi/index.ts b/src/lib/openapi/index.ts index 67a7ab0084..0094a677b0 100644 --- a/src/lib/openapi/index.ts +++ b/src/lib/openapi/index.ts @@ -54,7 +54,9 @@ import { featureTypesSchema, featureUsageSchema, featureVariantsSchema, - feedbackSchema, + feedbackCreateSchema, + feedbackUpdateSchema, + feedbackResponseSchema, groupSchema, groupsSchema, groupUserModelSchema, @@ -254,7 +256,9 @@ export const schemas: UnleashSchemas = { featureTypesSchema, featureUsageSchema, featureVariantsSchema, - feedbackSchema, + feedbackCreateSchema, + feedbackUpdateSchema, + feedbackResponseSchema, groupSchema, groupsSchema, groupUserModelSchema, diff --git a/src/lib/openapi/spec/feedback-create-schema.ts b/src/lib/openapi/spec/feedback-create-schema.ts new file mode 100644 index 0000000000..fc1b485ee2 --- /dev/null +++ b/src/lib/openapi/spec/feedback-create-schema.ts @@ -0,0 +1,24 @@ +import { FromSchema } from 'json-schema-to-ts'; + +export const feedbackCreateSchema = { + $id: '#/components/schemas/feedbackCreateSchema', + required: ['feedbackId'], + type: 'object', + description: 'User feedback information to be created.', + properties: { + neverShow: { + description: + '`true` if the user has asked never to see this feedback questionnaire again. Defaults to `false`.', + type: 'boolean', + example: false, + }, + feedbackId: { + description: 'The name of the feedback session', + type: 'string', + example: 'pnps', + }, + }, + components: {}, +} as const; + +export type FeedbackCreateSchema = FromSchema; diff --git a/src/lib/openapi/spec/feedback-schema.ts b/src/lib/openapi/spec/feedback-response-schema.ts similarity index 63% rename from src/lib/openapi/spec/feedback-schema.ts rename to src/lib/openapi/spec/feedback-response-schema.ts index 777b741bb8..ce707fb90c 100644 --- a/src/lib/openapi/spec/feedback-schema.ts +++ b/src/lib/openapi/spec/feedback-response-schema.ts @@ -1,24 +1,19 @@ import { FromSchema } from 'json-schema-to-ts'; -export const feedbackSchema = { - $id: '#/components/schemas/feedbackSchema', - type: 'object', +export const feedbackResponseSchema = { + $id: '#/components/schemas/feedbackResponseSchema', additionalProperties: false, - description: 'User feedback information', + type: 'object', + description: 'User feedback information about a particular feedback item.', properties: { userId: { - description: 'Identifier of the current user giving feedback', + description: 'The ID of the user that gave the feedback.', type: 'integer', example: 2, }, - feedbackId: { - description: 'The name of the feedback session', - type: 'string', - example: 'pnps', - }, neverShow: { description: - '`true` when user opts-in to never show the feedback again.', + '`true` if the user has asked never to see this feedback questionnaire again.', type: 'boolean', example: false, }, @@ -29,8 +24,13 @@ export const feedbackSchema = { nullable: true, example: '2023-07-06T08:29:21.282Z', }, + feedbackId: { + description: 'The name of the feedback session', + type: 'string', + example: 'pnps', + }, }, components: {}, } as const; -export type FeedbackSchema = FromSchema; +export type FeedbackResponseSchema = FromSchema; diff --git a/src/lib/openapi/spec/feedback-update-schema.ts b/src/lib/openapi/spec/feedback-update-schema.ts new file mode 100644 index 0000000000..b566b71775 --- /dev/null +++ b/src/lib/openapi/spec/feedback-update-schema.ts @@ -0,0 +1,30 @@ +import { FromSchema } from 'json-schema-to-ts'; + +export const feedbackUpdateSchema = { + $id: '#/components/schemas/feedbackUpdateSchema', + type: 'object', + description: 'User feedback information to be updated.', + properties: { + userId: { + description: 'The ID of the user that gave the feedback.', + type: 'integer', + example: 2, + }, + neverShow: { + description: + '`true` if the user has asked never to see this feedback questionnaire again.', + type: 'boolean', + example: false, + }, + given: { + description: 'When this feedback was given', + type: 'string', + format: 'date-time', + nullable: true, + example: '2023-07-06T08:29:21.282Z', + }, + }, + components: {}, +} as const; + +export type FeedbackUpdateSchema = FromSchema; diff --git a/src/lib/openapi/spec/index.ts b/src/lib/openapi/spec/index.ts index 38c34099bc..35f8f64c58 100644 --- a/src/lib/openapi/spec/index.ts +++ b/src/lib/openapi/spec/index.ts @@ -32,7 +32,9 @@ export * from './variant-schema'; export * from './variant-flag-schema'; export * from './version-schema'; export * from './features-schema'; -export * from './feedback-schema'; +export * from './feedback-create-schema'; +export * from './feedback-update-schema'; +export * from './feedback-response-schema'; export * from './override-schema'; export * from './password-schema'; export * from './projects-schema'; diff --git a/src/lib/openapi/spec/me-schema.ts b/src/lib/openapi/spec/me-schema.ts index 7b6cedf144..c24fddbd4d 100644 --- a/src/lib/openapi/spec/me-schema.ts +++ b/src/lib/openapi/spec/me-schema.ts @@ -1,7 +1,7 @@ import { FromSchema } from 'json-schema-to-ts'; import { userSchema } from './user-schema'; import { permissionSchema } from './permission-schema'; -import { feedbackSchema } from './feedback-schema'; +import { feedbackResponseSchema } from './feedback-response-schema'; export const meSchema = { $id: '#/components/schemas/meSchema', @@ -24,7 +24,7 @@ export const meSchema = { description: 'User feedback information', type: 'array', items: { - $ref: '#/components/schemas/feedbackSchema', + $ref: '#/components/schemas/feedbackResponseSchema', }, }, splash: { @@ -39,7 +39,7 @@ export const meSchema = { schemas: { userSchema, permissionSchema, - feedbackSchema, + feedbackResponseSchema, }, }, } as const; diff --git a/src/lib/routes/admin-api/user-feedback.ts b/src/lib/routes/admin-api/user-feedback.ts index 5813a199b4..abed606e87 100644 --- a/src/lib/routes/admin-api/user-feedback.ts +++ b/src/lib/routes/admin-api/user-feedback.ts @@ -7,15 +7,15 @@ import UserFeedbackService from '../../services/user-feedback-service'; import { IAuthRequest } from '../unleash-types'; import { NONE } from '../../types/permissions'; import { OpenApiService } from '../../services/openapi-service'; -import { - feedbackSchema, - FeedbackSchema, -} from '../../openapi/spec/feedback-schema'; +import { FeedbackCreateSchema } from '../../openapi/spec/feedback-create-schema'; +import { FeedbackUpdateSchema } from '../../openapi/spec/feedback-update-schema'; +import { FeedbackResponseSchema } from '../../openapi/spec/feedback-response-schema'; import { serializeDates } from '../../types/serialize-dates'; import { parseISO } from 'date-fns'; import { createRequestSchema } from '../../openapi/util/create-request-schema'; import { createResponseSchema } from '../../openapi/util/create-response-schema'; import BadDataError from '../../error/bad-data-error'; +import { feedbackResponseSchema, getStandardResponses } from '../../openapi'; class UserFeedbackController extends Controller { private logger: Logger; @@ -45,8 +45,14 @@ class UserFeedbackController extends Controller { openApiService.validPath({ tags: ['Admin UI'], operationId: 'createFeedback', - requestBody: createRequestSchema('feedbackSchema'), - responses: { 200: createResponseSchema('feedbackSchema') }, + summary: 'Send Unleash feedback', + description: + 'Sends feedback gathered from the Unleash UI to the Unleash server. Must be called with a token with an identifiable user (either from being sent from the UI or from using a [PAT](https://docs.getunleash.io/reference/api-tokens-and-client-keys#personal-access-tokens)).', + requestBody: createRequestSchema('feedbackCreateSchema'), + responses: { + 200: createResponseSchema('feedbackResponseSchema'), + ...getStandardResponses(400, 401, 415), + }, }), ], }); @@ -60,16 +66,22 @@ class UserFeedbackController extends Controller { openApiService.validPath({ tags: ['Admin UI'], operationId: 'updateFeedback', - requestBody: createRequestSchema('feedbackSchema'), - responses: { 200: createResponseSchema('feedbackSchema') }, + summary: 'Update Unleash feedback', + description: + 'Updates the feedback with the provided ID. Only provided fields are updated. Fields left out are left untouched. Must be called with a token with an identifiable user (either from being sent from the UI or from using a [PAT](https://docs.getunleash.io/reference/api-tokens-and-client-keys#personal-access-tokens)).', + requestBody: createRequestSchema('feedbackUpdateSchema'), + responses: { + 200: createResponseSchema('feedbackResponseSchema'), + ...getStandardResponses(400, 401, 415), + }, }), ], }); } private async createFeedback( - req: IAuthRequest, - res: Response, + req: IAuthRequest, + res: Response, ): Promise { if (!req.body.feedbackId) { throw new BadDataError('Missing feedbackId'); @@ -85,14 +97,14 @@ class UserFeedbackController extends Controller { this.openApiService.respondWithValidation( 200, res, - feedbackSchema.$id, + feedbackResponseSchema.$id, serializeDates(updated), ); } private async updateFeedback( - req: IAuthRequest<{ id: string }, unknown, FeedbackSchema>, - res: Response, + req: IAuthRequest<{ id: string }, unknown, FeedbackUpdateSchema>, + res: Response, ): Promise { const updated = await this.userFeedbackService.updateFeedback({ feedbackId: req.params.id, @@ -104,7 +116,7 @@ class UserFeedbackController extends Controller { this.openApiService.respondWithValidation( 200, res, - feedbackSchema.$id, + feedbackResponseSchema.$id, serializeDates(updated), ); } diff --git a/src/test/e2e/api/admin/feedback.e2e.test.ts b/src/test/e2e/api/admin/feedback.e2e.test.ts index 3596712437..cc57a597df 100644 --- a/src/test/e2e/api/admin/feedback.e2e.test.ts +++ b/src/test/e2e/api/admin/feedback.e2e.test.ts @@ -33,7 +33,13 @@ beforeAll(async () => { ); }; - app = await setupAppWithCustomAuth(stores, preHook); + app = await setupAppWithCustomAuth(stores, preHook, { + experimental: { + flags: { + strictSchemaValidation: true, + }, + }, + }); }); afterAll(async () => {