mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	
							parent
							
								
									00bef41836
								
							
						
					
					
						commit
						1821af8fe7
					
				@ -49,6 +49,7 @@ 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 { tagWithVersionSchema } from './spec/tag-with-version-schema';
 | 
			
		||||
 | 
			
		||||
// All schemas in `openapi/spec` should be listed here.
 | 
			
		||||
export const schemas = {
 | 
			
		||||
@ -87,6 +88,7 @@ export const schemas = {
 | 
			
		||||
    splashSchema,
 | 
			
		||||
    strategySchema,
 | 
			
		||||
    tagSchema,
 | 
			
		||||
    tagWithVersionSchema,
 | 
			
		||||
    tagsSchema,
 | 
			
		||||
    tagTypeSchema,
 | 
			
		||||
    tagTypesSchema,
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										22
									
								
								src/lib/openapi/spec/tag-with-version-schema.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/lib/openapi/spec/tag-with-version-schema.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
			
		||||
import { tagSchema } from './tag-schema';
 | 
			
		||||
import { FromSchema } from 'json-schema-to-ts';
 | 
			
		||||
 | 
			
		||||
export const tagWithVersionSchema = {
 | 
			
		||||
    $id: '#/components/schemas/tagWithVersionSchema',
 | 
			
		||||
    type: 'object',
 | 
			
		||||
    additionalProperties: false,
 | 
			
		||||
    required: ['version', 'tag'],
 | 
			
		||||
    properties: {
 | 
			
		||||
        version: {
 | 
			
		||||
            type: 'integer',
 | 
			
		||||
        },
 | 
			
		||||
        tag: {
 | 
			
		||||
            $ref: '#/components/schemas/tagSchema',
 | 
			
		||||
        },
 | 
			
		||||
    },
 | 
			
		||||
    components: {
 | 
			
		||||
        schemas: { tagSchema },
 | 
			
		||||
    },
 | 
			
		||||
} as const;
 | 
			
		||||
 | 
			
		||||
export type TagWithVersionSchema = FromSchema<typeof tagWithVersionSchema>;
 | 
			
		||||
@ -72,9 +72,9 @@ test('should get all tags added', () => {
 | 
			
		||||
        });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test('should be able to get single tag by type and value', () => {
 | 
			
		||||
test('should be able to get single tag by type and value', async () => {
 | 
			
		||||
    expect.assertions(1);
 | 
			
		||||
    tagStore.createTag({ value: 'TeamRed', type: 'simple' });
 | 
			
		||||
    await tagStore.createTag({ value: 'TeamRed', type: 'simple' });
 | 
			
		||||
    return request
 | 
			
		||||
        .get(`${base}/api/admin/tags/simple/TeamRed`)
 | 
			
		||||
        .expect('Content-Type', /json/)
 | 
			
		||||
 | 
			
		||||
@ -6,9 +6,18 @@ import { Logger } from '../../logger';
 | 
			
		||||
 | 
			
		||||
import Controller from '../controller';
 | 
			
		||||
 | 
			
		||||
import { UPDATE_FEATURE } from '../../types/permissions';
 | 
			
		||||
import { NONE, UPDATE_FEATURE } from '../../types/permissions';
 | 
			
		||||
import { extractUsername } from '../../util/extract-user';
 | 
			
		||||
import { IAuthRequest } from '../unleash-types';
 | 
			
		||||
import { createRequestSchema, createResponseSchema } from '../../openapi';
 | 
			
		||||
import { emptyResponse } from '../../openapi/spec/empty-response';
 | 
			
		||||
import { tagsSchema, TagsSchema } from '../../openapi/spec/tags-schema';
 | 
			
		||||
import { TagSchema } from '../../openapi/spec/tag-schema';
 | 
			
		||||
import { OpenApiService } from '../../services/openapi-service';
 | 
			
		||||
import {
 | 
			
		||||
    tagWithVersionSchema,
 | 
			
		||||
    TagWithVersionSchema,
 | 
			
		||||
} from '../../openapi/spec/tag-with-version-schema';
 | 
			
		||||
 | 
			
		||||
const version = 1;
 | 
			
		||||
 | 
			
		||||
@ -17,44 +26,147 @@ class TagController extends Controller {
 | 
			
		||||
 | 
			
		||||
    private tagService: TagService;
 | 
			
		||||
 | 
			
		||||
    private openApiService: OpenApiService;
 | 
			
		||||
 | 
			
		||||
    constructor(
 | 
			
		||||
        config: IUnleashConfig,
 | 
			
		||||
        { tagService }: Pick<IUnleashServices, 'tagService'>,
 | 
			
		||||
        {
 | 
			
		||||
            tagService,
 | 
			
		||||
            openApiService,
 | 
			
		||||
        }: Pick<IUnleashServices, 'tagService' | 'openApiService'>,
 | 
			
		||||
    ) {
 | 
			
		||||
        super(config);
 | 
			
		||||
        this.tagService = tagService;
 | 
			
		||||
        this.openApiService = openApiService;
 | 
			
		||||
        this.logger = config.getLogger('/admin-api/tag.js');
 | 
			
		||||
 | 
			
		||||
        this.get('/', this.getTags);
 | 
			
		||||
        this.post('/', this.createTag, UPDATE_FEATURE);
 | 
			
		||||
        this.get('/:type', this.getTagsByType);
 | 
			
		||||
        this.get('/:type/:value', this.getTag);
 | 
			
		||||
        this.delete('/:type/:value', this.deleteTag, UPDATE_FEATURE);
 | 
			
		||||
        this.route({
 | 
			
		||||
            method: 'get',
 | 
			
		||||
            path: '',
 | 
			
		||||
            handler: this.getTags,
 | 
			
		||||
            permission: NONE,
 | 
			
		||||
            middleware: [
 | 
			
		||||
                openApiService.validPath({
 | 
			
		||||
                    tags: ['admin'],
 | 
			
		||||
                    operationId: 'getTags',
 | 
			
		||||
                    responses: { 200: createResponseSchema('tagsSchema') },
 | 
			
		||||
                }),
 | 
			
		||||
            ],
 | 
			
		||||
        });
 | 
			
		||||
        this.route({
 | 
			
		||||
            method: 'post',
 | 
			
		||||
            path: '',
 | 
			
		||||
            handler: this.createTag,
 | 
			
		||||
            permission: UPDATE_FEATURE,
 | 
			
		||||
            middleware: [
 | 
			
		||||
                openApiService.validPath({
 | 
			
		||||
                    tags: ['admin'],
 | 
			
		||||
                    operationId: 'createTag',
 | 
			
		||||
                    responses: {
 | 
			
		||||
                        201: emptyResponse,
 | 
			
		||||
                    },
 | 
			
		||||
                    requestBody: createRequestSchema('tagSchema'),
 | 
			
		||||
                }),
 | 
			
		||||
            ],
 | 
			
		||||
        });
 | 
			
		||||
        this.route({
 | 
			
		||||
            method: 'get',
 | 
			
		||||
            path: '/:type',
 | 
			
		||||
            handler: this.getTagsByType,
 | 
			
		||||
            permission: NONE,
 | 
			
		||||
            middleware: [
 | 
			
		||||
                openApiService.validPath({
 | 
			
		||||
                    tags: ['admin'],
 | 
			
		||||
                    operationId: 'getTagsByType',
 | 
			
		||||
                    responses: {
 | 
			
		||||
                        200: createResponseSchema('tagsSchema'),
 | 
			
		||||
                    },
 | 
			
		||||
                }),
 | 
			
		||||
            ],
 | 
			
		||||
        });
 | 
			
		||||
        this.route({
 | 
			
		||||
            method: 'get',
 | 
			
		||||
            path: '/:type/:value',
 | 
			
		||||
            handler: this.getTag,
 | 
			
		||||
            permission: NONE,
 | 
			
		||||
            middleware: [
 | 
			
		||||
                openApiService.validPath({
 | 
			
		||||
                    tags: ['admin'],
 | 
			
		||||
                    operationId: 'getTag',
 | 
			
		||||
                    responses: {
 | 
			
		||||
                        200: createResponseSchema('tagWithVersionSchema'),
 | 
			
		||||
                    },
 | 
			
		||||
                }),
 | 
			
		||||
            ],
 | 
			
		||||
        });
 | 
			
		||||
        this.route({
 | 
			
		||||
            method: 'delete',
 | 
			
		||||
            path: '/:type/:value',
 | 
			
		||||
            handler: this.deleteTag,
 | 
			
		||||
            acceptAnyContentType: true,
 | 
			
		||||
            permission: UPDATE_FEATURE,
 | 
			
		||||
            middleware: [
 | 
			
		||||
                openApiService.validPath({
 | 
			
		||||
                    tags: ['admin'],
 | 
			
		||||
                    operationId: 'deleteTag',
 | 
			
		||||
                    responses: {
 | 
			
		||||
                        200: emptyResponse,
 | 
			
		||||
                    },
 | 
			
		||||
                }),
 | 
			
		||||
            ],
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async getTags(req: Request, res: Response): Promise<void> {
 | 
			
		||||
    async getTags(req: Request, res: Response<TagsSchema>): Promise<void> {
 | 
			
		||||
        const tags = await this.tagService.getTags();
 | 
			
		||||
        res.json({ version, tags });
 | 
			
		||||
        this.openApiService.respondWithValidation<TagsSchema>(
 | 
			
		||||
            200,
 | 
			
		||||
            res,
 | 
			
		||||
            tagsSchema.$id,
 | 
			
		||||
            { version, tags },
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async getTagsByType(req: Request, res: Response): Promise<void> {
 | 
			
		||||
    async getTagsByType(
 | 
			
		||||
        req: Request,
 | 
			
		||||
        res: Response<TagsSchema>,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const tags = await this.tagService.getTagsByType(req.params.type);
 | 
			
		||||
        res.json({ version, tags });
 | 
			
		||||
        this.openApiService.respondWithValidation<TagsSchema>(
 | 
			
		||||
            200,
 | 
			
		||||
            res,
 | 
			
		||||
            tagsSchema.$id,
 | 
			
		||||
            { version, tags },
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async getTag(req: Request, res: Response): Promise<void> {
 | 
			
		||||
    async getTag(
 | 
			
		||||
        req: Request<TagSchema>,
 | 
			
		||||
        res: Response<TagWithVersionSchema>,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const { type, value } = req.params;
 | 
			
		||||
        const tag = await this.tagService.getTag({ type, value });
 | 
			
		||||
        res.json({ version, tag });
 | 
			
		||||
        this.openApiService.respondWithValidation<TagWithVersionSchema>(
 | 
			
		||||
            200,
 | 
			
		||||
            res,
 | 
			
		||||
            tagWithVersionSchema.$id,
 | 
			
		||||
            { version, tag },
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async createTag(req: IAuthRequest, res: Response): Promise<void> {
 | 
			
		||||
    async createTag(
 | 
			
		||||
        req: IAuthRequest<unknown, unknown, TagSchema>,
 | 
			
		||||
        res: Response,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const userName = extractUsername(req);
 | 
			
		||||
        await this.tagService.createTag(req.body, userName);
 | 
			
		||||
        res.status(201).end();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async deleteTag(req: IAuthRequest, res: Response): Promise<void> {
 | 
			
		||||
    async deleteTag(
 | 
			
		||||
        req: IAuthRequest<TagSchema>,
 | 
			
		||||
        res: Response,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const { type, value } = req.params;
 | 
			
		||||
        const userName = extractUsername(req);
 | 
			
		||||
        await this.tagService.deleteTag({ type, value }, userName);
 | 
			
		||||
 | 
			
		||||
@ -62,13 +62,13 @@ test('Can create a tag', async () =>
 | 
			
		||||
    app.request
 | 
			
		||||
        .post('/api/admin/tags')
 | 
			
		||||
        .send({
 | 
			
		||||
            id: 1,
 | 
			
		||||
            value: 'TeamRed',
 | 
			
		||||
            type: 'simple',
 | 
			
		||||
        })
 | 
			
		||||
        .expect((res) => {
 | 
			
		||||
            expect(res.status).toBe(201);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
test('Can validate a tag', async () =>
 | 
			
		||||
    app.request
 | 
			
		||||
        .post('/api/admin/tags')
 | 
			
		||||
@ -79,11 +79,8 @@ test('Can validate a tag', async () =>
 | 
			
		||||
        .expect('Content-Type', /json/)
 | 
			
		||||
        .expect(400)
 | 
			
		||||
        .expect((res) => {
 | 
			
		||||
            expect(res.body.details.length).toBe(2);
 | 
			
		||||
            expect(res.body.details.length).toBe(1);
 | 
			
		||||
            expect(res.body.details[0].message).toBe(
 | 
			
		||||
                '"value" must be a string',
 | 
			
		||||
            );
 | 
			
		||||
            expect(res.body.details[1].message).toBe(
 | 
			
		||||
                '"type" must be URL friendly',
 | 
			
		||||
            );
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
@ -950,6 +950,22 @@ Object {
 | 
			
		||||
        ],
 | 
			
		||||
        "type": "object",
 | 
			
		||||
      },
 | 
			
		||||
      "tagWithVersionSchema": Object {
 | 
			
		||||
        "additionalProperties": false,
 | 
			
		||||
        "properties": Object {
 | 
			
		||||
          "tag": Object {
 | 
			
		||||
            "$ref": "#/components/schemas/tagSchema",
 | 
			
		||||
          },
 | 
			
		||||
          "version": Object {
 | 
			
		||||
            "type": "integer",
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        "required": Array [
 | 
			
		||||
          "version",
 | 
			
		||||
          "tag",
 | 
			
		||||
        ],
 | 
			
		||||
        "type": "object",
 | 
			
		||||
      },
 | 
			
		||||
      "tagsSchema": Object {
 | 
			
		||||
        "additionalProperties": false,
 | 
			
		||||
        "properties": Object {
 | 
			
		||||
@ -3208,6 +3224,145 @@ Object {
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    "/api/admin/tags": Object {
 | 
			
		||||
      "get": Object {
 | 
			
		||||
        "operationId": "getTags",
 | 
			
		||||
        "responses": Object {
 | 
			
		||||
          "200": Object {
 | 
			
		||||
            "content": Object {
 | 
			
		||||
              "application/json": Object {
 | 
			
		||||
                "schema": Object {
 | 
			
		||||
                  "$ref": "#/components/schemas/tagsSchema",
 | 
			
		||||
                },
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
            "description": "tagsSchema",
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        "tags": Array [
 | 
			
		||||
          "admin",
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
      "post": Object {
 | 
			
		||||
        "operationId": "createTag",
 | 
			
		||||
        "requestBody": Object {
 | 
			
		||||
          "content": Object {
 | 
			
		||||
            "application/json": Object {
 | 
			
		||||
              "schema": Object {
 | 
			
		||||
                "$ref": "#/components/schemas/tagSchema",
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
          "description": "tagSchema",
 | 
			
		||||
          "required": true,
 | 
			
		||||
        },
 | 
			
		||||
        "responses": Object {
 | 
			
		||||
          "201": Object {
 | 
			
		||||
            "description": "emptyResponse",
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        "tags": Array [
 | 
			
		||||
          "admin",
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    "/api/admin/tags/{type}": Object {
 | 
			
		||||
      "get": Object {
 | 
			
		||||
        "operationId": "getTagsByType",
 | 
			
		||||
        "parameters": Array [
 | 
			
		||||
          Object {
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "name": "type",
 | 
			
		||||
            "required": true,
 | 
			
		||||
            "schema": Object {
 | 
			
		||||
              "type": "string",
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        "responses": Object {
 | 
			
		||||
          "200": Object {
 | 
			
		||||
            "content": Object {
 | 
			
		||||
              "application/json": Object {
 | 
			
		||||
                "schema": Object {
 | 
			
		||||
                  "$ref": "#/components/schemas/tagsSchema",
 | 
			
		||||
                },
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
            "description": "tagsSchema",
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        "tags": Array [
 | 
			
		||||
          "admin",
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    "/api/admin/tags/{type}/{value}": Object {
 | 
			
		||||
      "delete": Object {
 | 
			
		||||
        "operationId": "deleteTag",
 | 
			
		||||
        "parameters": Array [
 | 
			
		||||
          Object {
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "name": "type",
 | 
			
		||||
            "required": true,
 | 
			
		||||
            "schema": Object {
 | 
			
		||||
              "type": "string",
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
          Object {
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "name": "value",
 | 
			
		||||
            "required": true,
 | 
			
		||||
            "schema": Object {
 | 
			
		||||
              "type": "string",
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        "responses": Object {
 | 
			
		||||
          "200": Object {
 | 
			
		||||
            "description": "emptyResponse",
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        "tags": Array [
 | 
			
		||||
          "admin",
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
      "get": Object {
 | 
			
		||||
        "operationId": "getTag",
 | 
			
		||||
        "parameters": Array [
 | 
			
		||||
          Object {
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "name": "type",
 | 
			
		||||
            "required": true,
 | 
			
		||||
            "schema": Object {
 | 
			
		||||
              "type": "string",
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
          Object {
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "name": "value",
 | 
			
		||||
            "required": true,
 | 
			
		||||
            "schema": Object {
 | 
			
		||||
              "type": "string",
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        "responses": Object {
 | 
			
		||||
          "200": Object {
 | 
			
		||||
            "content": Object {
 | 
			
		||||
              "application/json": Object {
 | 
			
		||||
                "schema": Object {
 | 
			
		||||
                  "$ref": "#/components/schemas/tagWithVersionSchema",
 | 
			
		||||
                },
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
            "description": "tagWithVersionSchema",
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        "tags": Array [
 | 
			
		||||
          "admin",
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    "/api/admin/ui-config": Object {
 | 
			
		||||
      "get": Object {
 | 
			
		||||
        "operationId": "getUIConfig",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user