From 91ee6248863b6a842f50942c80a0bd1c247c64a5 Mon Sep 17 00:00:00 2001 From: Chase Poirier Date: Fri, 20 Feb 2026 11:56:49 -0700 Subject: [PATCH 1/2] feat(componentType): add unpublish method for plain client Implement componentType.unpublish wrapping the Bridge API endpoint DELETE /spaces/:space_id/environments/:environment_id/component_types/:component_id/published Co-authored-by: Cursor --- lib/adapters/REST/endpoints/component-type.ts | 16 +++++++- lib/common-types.ts | 6 +++ lib/plain/entities/component-type.ts | 23 ++++++++++- lib/plain/plain-client.ts | 1 + .../REST/endpoints/component-type.test.ts | 38 +++++++++++++++++++ 5 files changed, 82 insertions(+), 2 deletions(-) diff --git a/lib/adapters/REST/endpoints/component-type.ts b/lib/adapters/REST/endpoints/component-type.ts index 83c3094ed6..2c49abdab4 100644 --- a/lib/adapters/REST/endpoints/component-type.ts +++ b/lib/adapters/REST/endpoints/component-type.ts @@ -1,6 +1,10 @@ import type { RawAxiosRequestHeaders } from 'axios' import type { AxiosInstance } from 'contentful-sdk-core' -import type { CollectionProp, GetSpaceEnvironmentParams } from '../../../common-types' +import type { + CollectionProp, + GetComponentTypeParams, + GetSpaceEnvironmentParams, +} from '../../../common-types' import type { ComponentTypeProps, ComponentTypeQueryOptions, @@ -21,3 +25,13 @@ export const getMany: RestEndpoint<'ComponentType', 'getMany'> = ( headers, }) } + +export const unpublish: RestEndpoint<'ComponentType', 'unpublish'> = ( + http: AxiosInstance, + params: GetComponentTypeParams, + headers?: RawAxiosRequestHeaders, +) => { + return raw.del(http, `${getBaseUrl(params)}/${params.componentTypeId}/published`, { + headers, + }) +} diff --git a/lib/common-types.ts b/lib/common-types.ts index 1b7f431b6e..3d15f50686 100644 --- a/lib/common-types.ts +++ b/lib/common-types.ts @@ -575,6 +575,7 @@ type MRInternal = { (opts: MROpts<'Comment', 'delete', UA>): MRReturn<'Comment', 'delete'> (opts: MROpts<'ComponentType', 'getMany', UA>): MRReturn<'ComponentType', 'getMany'> + (opts: MROpts<'ComponentType', 'unpublish', UA>): MRReturn<'ComponentType', 'unpublish'> (opts: MROpts<'Concept', 'get', UA>): MRReturn<'Concept', 'get'> (opts: MROpts<'Concept', 'getMany', UA>): MRReturn<'Concept', 'getMany'> @@ -1473,6 +1474,10 @@ export type MRActions = { params: GetSpaceEnvironmentParams & { query: ComponentTypeQueryOptions } return: CollectionProp } + unpublish: { + params: GetComponentTypeParams + return: ComponentTypeProps + } } Concept: { create: { @@ -2605,6 +2610,7 @@ export type GetBulkActionParams = GetSpaceEnvironmentParams & { bulkActionId: st export type GetCommentParams = (GetEntryParams | GetCommentParentEntityParams) & { commentId: string } +export type GetComponentTypeParams = GetSpaceEnvironmentParams & { componentTypeId: string } export type GetContentTypeParams = GetSpaceEnvironmentParams & { contentTypeId: string } export type GetEditorInterfaceParams = GetSpaceEnvironmentParams & { contentTypeId: string } export type GetEntryParams = GetSpaceEnvironmentParams & { entryId: string } diff --git a/lib/plain/entities/component-type.ts b/lib/plain/entities/component-type.ts index 60d03a07ea..e3b013d1ff 100644 --- a/lib/plain/entities/component-type.ts +++ b/lib/plain/entities/component-type.ts @@ -1,4 +1,8 @@ -import type { GetSpaceEnvironmentParams, CollectionProp } from '../../common-types' +import type { + GetSpaceEnvironmentParams, + GetComponentTypeParams, + CollectionProp, +} from '../../common-types' import type { ComponentTypeQueryOptions, ComponentTypeProps } from '../../entities/component-type' import type { OptionalDefaults } from '../wrappers/wrap' @@ -28,4 +32,21 @@ export type ComponentTypePlainClientAPI = { getMany( params: OptionalDefaults, ): Promise> + + /** + * Unpublishes a component type + * @param params the space, environment, and component type IDs + * @returns the unpublished component type + * @throws if the request fails, or the component type is not found + * @internal - Experimental endpoint, subject to breaking changes without notice + * @example + * ```javascript + * const componentType = await client.componentType.unpublish({ + * spaceId: '', + * environmentId: '', + * componentTypeId: '', + * }); + * ``` + */ + unpublish(params: OptionalDefaults): Promise } diff --git a/lib/plain/plain-client.ts b/lib/plain/plain-client.ts index 68442b39f9..43eecedb1f 100644 --- a/lib/plain/plain-client.ts +++ b/lib/plain/plain-client.ts @@ -225,6 +225,7 @@ export const createPlainClient = ( }, componentType: { getMany: wrap(wrapParams, 'ComponentType', 'getMany'), + unpublish: wrap(wrapParams, 'ComponentType', 'unpublish'), }, contentType: { get: wrap(wrapParams, 'ContentType', 'get'), diff --git a/test/unit/adapters/REST/endpoints/component-type.test.ts b/test/unit/adapters/REST/endpoints/component-type.test.ts index 1cddcbc062..999e4862ce 100644 --- a/test/unit/adapters/REST/endpoints/component-type.test.ts +++ b/test/unit/adapters/REST/endpoints/component-type.test.ts @@ -75,4 +75,42 @@ describe('Rest ComponentType', { concurrent: true }, () => { }) }) }) + + test('unpublish calls correct URL with DELETE method', async () => { + const mockResponse = { + sys: { + id: 'ct123', + type: 'ComponentType', + version: 2, + space: { sys: { type: 'Link', linkType: 'Space', id: 'space123' } }, + environment: { sys: { type: 'Link', linkType: 'Environment', id: 'master' } }, + }, + name: 'Test Component', + description: 'A test component type', + viewports: [], + contentProperties: [], + designProperties: [], + dimensionKeyMap: { designProperties: {} }, + } + + const { httpMock, adapterMock } = setupRestAdapter(Promise.resolve({ data: mockResponse })) + + return adapterMock + .makeRequest({ + entityType: 'ComponentType', + action: 'unpublish', + userAgent: 'mocked', + params: { + spaceId: 'space123', + environmentId: 'master', + componentTypeId: 'ct123', + }, + }) + .then((r) => { + expect(r).to.eql(mockResponse) + expect(httpMock.delete.mock.calls[0][0]).to.eql( + '/spaces/space123/environments/master/component_types/ct123/published', + ) + }) + }) }) From 3035a104ac0281c20a2199cb24cbf925c2ad2afa Mon Sep 17 00:00:00 2001 From: Michael Pham Date: Mon, 23 Feb 2026 13:40:07 -0700 Subject: [PATCH 2/2] fix: update unpublish method to include version parameter --- lib/adapters/REST/endpoints/component-type.ts | 7 +++++-- lib/common-types.ts | 2 +- lib/plain/entities/component-type.ts | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/adapters/REST/endpoints/component-type.ts b/lib/adapters/REST/endpoints/component-type.ts index 2c49abdab4..b8014c7ab3 100644 --- a/lib/adapters/REST/endpoints/component-type.ts +++ b/lib/adapters/REST/endpoints/component-type.ts @@ -28,10 +28,13 @@ export const getMany: RestEndpoint<'ComponentType', 'getMany'> = ( export const unpublish: RestEndpoint<'ComponentType', 'unpublish'> = ( http: AxiosInstance, - params: GetComponentTypeParams, + params: GetComponentTypeParams & { version: number }, headers?: RawAxiosRequestHeaders, ) => { return raw.del(http, `${getBaseUrl(params)}/${params.componentTypeId}/published`, { - headers, + headers: { + 'X-Contentful-Version': params.version, + ...headers, + }, }) } diff --git a/lib/common-types.ts b/lib/common-types.ts index 3d15f50686..7a8c323bef 100644 --- a/lib/common-types.ts +++ b/lib/common-types.ts @@ -1475,7 +1475,7 @@ export type MRActions = { return: CollectionProp } unpublish: { - params: GetComponentTypeParams + params: GetComponentTypeParams & { version: number } return: ComponentTypeProps } } diff --git a/lib/plain/entities/component-type.ts b/lib/plain/entities/component-type.ts index e3b013d1ff..1ca72e1d7d 100644 --- a/lib/plain/entities/component-type.ts +++ b/lib/plain/entities/component-type.ts @@ -48,5 +48,7 @@ export type ComponentTypePlainClientAPI = { * }); * ``` */ - unpublish(params: OptionalDefaults): Promise + unpublish( + params: OptionalDefaults, + ): Promise }