diff --git a/frontend/src/component/project/Project/ProjectFeatureToggles/ProjectFeaturesBatchActions/ManageTags.tsx b/frontend/src/component/project/Project/ProjectFeatureToggles/ProjectFeaturesBatchActions/ManageTags.tsx index a3a280fe81..46ebd41571 100644 --- a/frontend/src/component/project/Project/ProjectFeatureToggles/ProjectFeaturesBatchActions/ManageTags.tsx +++ b/frontend/src/component/project/Project/ProjectFeatureToggles/ProjectFeaturesBatchActions/ManageTags.tsx @@ -59,7 +59,7 @@ export const ManageTags: VFC = ({ projectId, data }) => { const features = data.map(({ name }) => name); const payload = { features, tags: { addedTags, removedTags } }; try { - await bulkUpdateTags(payload); + await bulkUpdateTags(payload, projectId); refetch(); const added = addedTags.length ? `Added tags: ${addedTags diff --git a/frontend/src/hooks/api/actions/useTagApi/useTagApi.ts b/frontend/src/hooks/api/actions/useTagApi/useTagApi.ts index 84facaeaa1..350838e08d 100644 --- a/frontend/src/hooks/api/actions/useTagApi/useTagApi.ts +++ b/frontend/src/hooks/api/actions/useTagApi/useTagApi.ts @@ -20,8 +20,11 @@ const useTagApi = () => { } }; - const bulkUpdateTags = async (payload: TagsBulkAddSchema) => { - const path = `api/admin/tags/features`; + const bulkUpdateTags = async ( + payload: TagsBulkAddSchema, + projectId: string + ) => { + const path = `api/admin/projects/${projectId}/tags`; const req = createRequest(path, { method: 'PUT', body: JSON.stringify(payload), diff --git a/src/lib/routes/admin-api/project/project-features.ts b/src/lib/routes/admin-api/project/project-features.ts index 48360cff37..1e33abc491 100644 --- a/src/lib/routes/admin-api/project/project-features.ts +++ b/src/lib/routes/admin-api/project/project-features.ts @@ -36,10 +36,16 @@ import { getStandardResponses, ParametersSchema, SetStrategySortOrderSchema, + TagsBulkAddSchema, + TagSchema, UpdateFeatureSchema, UpdateFeatureStrategySchema, } from '../../../openapi'; -import { OpenApiService, FeatureToggleService } from '../../../services'; +import { + OpenApiService, + FeatureToggleService, + FeatureTagService, +} from '../../../services'; import { querySchema } from '../../../schema/feature-schema'; import { BatchStaleSchema } from '../../../openapi/spec/batch-stale-schema'; import { @@ -85,6 +91,7 @@ export interface IFeatureProjectUserParams extends ProjectParam { const PATH = '/:projectId/features'; const PATH_STALE = '/:projectId/stale'; +const PATH_TAGS = `/:projectId/tags`; const PATH_FEATURE = `${PATH}/:featureName`; const PATH_FEATURE_CLONE = `${PATH_FEATURE}/clone`; const PATH_ENV = `${PATH_FEATURE}/environments/:environment`; @@ -98,11 +105,14 @@ type ProjectFeaturesServices = Pick< | 'projectHealthService' | 'openApiService' | 'transactionalFeatureToggleService' + | 'featureTagService' >; export default class ProjectFeaturesController extends Controller { private featureService: FeatureToggleService; + private featureTagService: FeatureTagService; + private transactionalFeatureToggleService: ( db: UnleashTransaction, ) => FeatureToggleService; @@ -121,6 +131,7 @@ export default class ProjectFeaturesController extends Controller { featureToggleServiceV2, openApiService, transactionalFeatureToggleService, + featureTagService, }: ProjectFeaturesServices, startTransaction: TransactionCreator, ) { @@ -130,6 +141,7 @@ export default class ProjectFeaturesController extends Controller { transactionalFeatureToggleService; this.startTransaction = startTransaction; this.openApiService = openApiService; + this.featureTagService = featureTagService; this.flagResolver = config.flagResolver; this.logger = config.getLogger('/admin-api/project/features.ts'); @@ -480,6 +492,21 @@ export default class ProjectFeaturesController extends Controller { }), ], }); + + this.route({ + method: 'put', + path: PATH_TAGS, + handler: this.updateFeaturesTags, + permission: UPDATE_FEATURE, + middleware: [ + openApiService.validPath({ + tags: ['Tags'], + operationId: 'addTagToFeatures', + requestBody: createRequestSchema('tagsBulkAddSchema'), + responses: { 200: emptyResponse }, + }), + ], + }); } async getFeatures( @@ -976,6 +1003,21 @@ export default class ProjectFeaturesController extends Controller { res.status(200).json(updatedStrategy); } + async updateFeaturesTags( + req: IAuthRequest, + res: Response, + ): Promise { + const { features, tags } = req.body; + const userName = extractUsername(req); + await this.featureTagService.updateTags( + features, + tags.addedTags, + tags.removedTags, + userName, + ); + res.status(200).end(); + } + async getStrategyParameters( req: Request, res: Response, diff --git a/src/lib/routes/admin-api/tag.ts b/src/lib/routes/admin-api/tag.ts index 1f8c02b240..3bc171e2c9 100644 --- a/src/lib/routes/admin-api/tag.ts +++ b/src/lib/routes/admin-api/tag.ts @@ -23,7 +23,6 @@ import { } from '../../openapi/spec/tag-with-version-schema'; import { emptyResponse } from '../../openapi/util/standard-responses'; import FeatureTagService from 'lib/services/feature-tag-service'; -import { TagsBulkAddSchema } from '../../openapi/spec/tags-bulk-add-schema'; import { IFlagResolver } from '../../types'; const version = 1; @@ -74,7 +73,7 @@ class TagController extends Controller { method: 'post', path: '', handler: this.createTag, - permission: UPDATE_FEATURE, + permission: NONE, middleware: [ openApiService.validPath({ tags: ['Tags'], @@ -88,20 +87,7 @@ class TagController extends Controller { }), ], }); - this.route({ - method: 'put', - path: '/features', - handler: this.updateFeaturesTags, - permission: UPDATE_FEATURE, - middleware: [ - openApiService.validPath({ - tags: ['Tags'], - operationId: 'addTagToFeatures', - requestBody: createRequestSchema('tagsBulkAddSchema'), - responses: { 200: emptyResponse }, - }), - ], - }); + this.route({ method: 'get', path: '/:type', @@ -208,20 +194,5 @@ class TagController extends Controller { await this.tagService.deleteTag({ type, value }, userName); res.status(200).end(); } - - async updateFeaturesTags( - req: IAuthRequest, - res: Response, - ): Promise { - const { features, tags } = req.body; - const userName = extractUsername(req); - await this.featureTagService.updateTags( - features, - tags.addedTags, - tags.removedTags, - userName, - ); - res.status(200).end(); - } } export default TagController; diff --git a/src/test/e2e/api/admin/tags.e2e.test.ts b/src/test/e2e/api/admin/tags.e2e.test.ts index fc13372ff0..3a5dc88344 100644 --- a/src/test/e2e/api/admin/tags.e2e.test.ts +++ b/src/test/e2e/api/admin/tags.e2e.test.ts @@ -143,7 +143,7 @@ test('Can tag features', async () => { strategies: [{ name: 'default' }], }); - await app.request.put('/api/admin/tags/features').send({ + await app.request.put('/api/admin/projects/default/tags').send({ features: [featureName, featureName2], tags: { addedTags: [addedTag], @@ -185,7 +185,7 @@ test('Can bulk remove tags', async () => { }); await app.request - .put('/api/admin/tags/features') + .put('/api/admin/projects/default/tags') .send({ features: [featureName, featureName2], tags: { @@ -196,7 +196,7 @@ test('Can bulk remove tags', async () => { .expect(200); await app.request - .put('/api/admin/tags/features') + .put('/api/admin/projects/default/tags') .send({ features: [featureName, featureName2], tags: {