Skip to content

Commit a70b34c

Browse files
feat: entry.patch handle releaseId [DX-209] (#2725)
* feat: entry.patch w/ releaseId alias for release.entry.patch * chore: add unit tests * chore: run prettier * chore: add integration tests for entry.patch with releaseId * chore: run lint * chore: cleanup code * chore: more cleanup * chore(test): remove redundant assertion * chore: cleanup params, console logs * chore: handle undefined headers for typescript * chore: use typecasting to pass param
1 parent de873be commit a70b34c

File tree

6 files changed

+389
-49
lines changed

6 files changed

+389
-49
lines changed

lib/adapters/REST/endpoints/entry.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,23 @@ import type { OpPatch } from 'json-patch'
55
import type { SetOptional } from 'type-fest'
66
import type {
77
CollectionProp,
8-
GetReleaseEntryParams,
8+
GetEntryParams,
9+
GetManyEntryParams,
910
GetSpaceEnvironmentParams,
1011
KeyValueMap,
12+
PatchEntryParams,
13+
PatchReleaseEntryParams,
1114
QueryParams,
1215
} from '../../../common-types'
1316
import type { CreateEntryProps, EntryProps, EntryReferenceProps } from '../../../entities/entry'
1417
import type { RestEndpoint } from '../types'
1518
import * as raw from './raw'
1619
import { normalizeSelect } from './utils'
20+
import * as releaseEntry from './release-entry'
1721

1822
export const get: RestEndpoint<'Entry', 'get'> = <T extends KeyValueMap = KeyValueMap>(
1923
http: AxiosInstance,
20-
params: GetReleaseEntryParams & QueryParams,
24+
params: GetEntryParams & QueryParams,
2125
rawData?: unknown,
2226
headers?: RawAxiosRequestHeaders
2327
) => {
@@ -55,7 +59,7 @@ export const getPublished: RestEndpoint<'Entry', 'getPublished'> = <
5559

5660
export const getMany: RestEndpoint<'Entry', 'getMany'> = <T extends KeyValueMap = KeyValueMap>(
5761
http: AxiosInstance,
58-
params: GetSpaceEnvironmentParams & QueryParams & { releaseId?: string },
62+
params: GetManyEntryParams & QueryParams,
5963
rawData?: unknown,
6064
headers?: RawAxiosRequestHeaders
6165
) => {
@@ -75,10 +79,14 @@ export const getMany: RestEndpoint<'Entry', 'getMany'> = <T extends KeyValueMap
7579

7680
export const patch: RestEndpoint<'Entry', 'patch'> = <T extends KeyValueMap = KeyValueMap>(
7781
http: AxiosInstance,
78-
params: GetSpaceEnvironmentParams & { entryId: string; version: number },
82+
params: PatchEntryParams & QueryParams,
7983
data: OpPatch[],
8084
headers?: RawAxiosRequestHeaders
8185
) => {
86+
if (params.releaseId) {
87+
return releaseEntry.patch(http, params as PatchReleaseEntryParams, data, headers ?? {})
88+
}
89+
8290
return raw.patch<EntryProps<T>>(
8391
http,
8492
`/spaces/${params.spaceId}/environments/${params.environmentId}/entries/${params.entryId}`,

lib/common-types.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,15 +1604,15 @@ export type MRActions = {
16041604
return: CollectionProp<EntryProps<any>>
16051605
}
16061606
getMany: {
1607-
params: GetSpaceEnvironmentParams & QueryParams & { releaseId?: string }
1607+
params: GetManyEntryParams & QueryParams
16081608
return: CollectionProp<EntryProps<any, any>>
16091609
}
16101610
get: {
1611-
params: GetReleaseEntryParams & QueryParams
1611+
params: GetEntryParams & QueryParams
16121612
return: EntryProps<any, any>
16131613
}
16141614
patch: {
1615-
params: GetSpaceEnvironmentParams & { entryId: string; version: number }
1615+
params: PatchEntryParams & QueryParams
16161616
payload: OpPatch[]
16171617
headers?: RawAxiosRequestHeaders
16181618
return: EntryProps<any>
@@ -2385,7 +2385,13 @@ export type GetCommentParams = (GetEntryParams | GetCommentParentEntityParams) &
23852385
}
23862386
export type GetContentTypeParams = GetSpaceEnvironmentParams & { contentTypeId: string }
23872387
export type GetEditorInterfaceParams = GetSpaceEnvironmentParams & { contentTypeId: string }
2388-
export type GetEntryParams = GetSpaceEnvironmentParams & { entryId: string }
2388+
export type GetEntryParams = GetSpaceEnvironmentParams & { entryId: string; releaseId?: string }
2389+
export type GetManyEntryParams = GetSpaceEnvironmentParams & { releaseId?: string }
2390+
export type PatchEntryParams = GetSpaceEnvironmentParams & {
2391+
entryId: string
2392+
version: number
2393+
releaseId?: string
2394+
}
23892395
export type GetExtensionParams = GetSpaceEnvironmentParams & { extensionId: string }
23902396
export type GetEnvironmentTemplateParams = GetOrganizationParams & { environmentTemplateId: string }
23912397
export type GetFunctionParams = GetAppDefinitionParams & { functionId: string }

lib/plain/common-types.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import type {
88
EnvironmentTemplateParams,
99
GetBulkActionParams,
1010
GetContentTypeParams,
11+
GetEntryParams,
1112
GetEnvironmentTemplateParams,
13+
GetManyEntryParams,
1214
GetManyReleaseEntryParams,
1315
GetOrganizationMembershipParams,
1416
GetOrganizationParams,
@@ -19,6 +21,7 @@ import type {
1921
GetSpaceEnvironmentParams,
2022
GetSpaceParams,
2123
KeyValueMap,
24+
PatchEntryParams,
2225
PatchReleaseEntryParams,
2326
QueryParams,
2427
ReleaseEnvironmentParams,
@@ -279,7 +282,7 @@ export type PlainClientAPI = {
279282
headers?: RawAxiosRequestHeaders
280283
): Promise<CollectionProp<EntryProps<T>>>
281284
getMany<T extends KeyValueMap = KeyValueMap>(
282-
params: OptionalDefaults<GetSpaceEnvironmentParams & QueryParams & { releaseId?: string }>,
285+
params: OptionalDefaults<GetManyEntryParams & QueryParams>,
283286
rawData?: unknown,
284287
headers?: RawAxiosRequestHeaders
285288
): Promise<
@@ -299,7 +302,7 @@ export type PlainClientAPI = {
299302
>
300303
>
301304
get<T extends KeyValueMap = KeyValueMap>(
302-
params: OptionalDefaults<GetReleaseEntryParams>,
305+
params: OptionalDefaults<GetEntryParams & QueryParams>,
303306
rawData?: unknown,
304307
headers?: RawAxiosRequestHeaders
305308
): Promise<
@@ -322,7 +325,7 @@ export type PlainClientAPI = {
322325
headers?: RawAxiosRequestHeaders
323326
): Promise<EntryProps<T>>
324327
patch<T extends KeyValueMap = KeyValueMap>(
325-
params: OptionalDefaults<GetSpaceEnvironmentParams & { entryId: string; version?: number }>,
328+
params: OptionalDefaults<PatchEntryParams & QueryParams>,
326329
rawData: OpPatch[],
327330
headers?: RawAxiosRequestHeaders
328331
): Promise<EntryProps<T>>

test/integration/entry-integration.test.ts

Lines changed: 75 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ import type {
2020
Space,
2121
} from '../../lib/export-types'
2222
import { TestDefaults } from '../defaults'
23-
import { makeLink } from '../utils'
23+
import {
24+
createEmptyRelease,
25+
createTestEntry,
26+
updateReleaseWithEntries,
27+
updateReleaseEntryTitle,
28+
} from './utils/release-entry.utils'
2429

2530
describe('Entry Api', () => {
2631
afterAll(async () => await timeoutToCalmRateLimiting())
@@ -718,7 +723,7 @@ describe('Entry Api', () => {
718723
})
719724

720725
//test releasev2 entry logic
721-
describe('read plainClientApi with releaseId', () => {
726+
describe('plainClientApi with releaseId', () => {
722727
let entry: EntryProps
723728
let entry2: EntryProps
724729
let release: ReleaseProps
@@ -733,45 +738,25 @@ describe('Entry Api', () => {
733738
}
734739
createEntryClient = initPlainClient(defaultParams)
735740

736-
entry = await createEntryClient.entry.create(
737-
{ ...defaultParams, contentTypeId: TestDefaults.contentType.withCrossSpaceReferenceId },
738-
{
739-
fields: {
740-
title: {
741-
'en-US': 'Test Entry for Release',
742-
},
743-
},
744-
}
745-
)
741+
// create release
742+
release = await createEmptyRelease(createEntryClient, defaultParams)
746743

747-
entry2 = await createEntryClient.entry.create(
748-
{ ...defaultParams, contentTypeId: TestDefaults.contentType.withCrossSpaceReferenceId },
749-
{
750-
fields: {
751-
title: {
752-
'en-US': 'Test Entry for Release',
753-
},
754-
},
755-
}
744+
// create entries to add to release
745+
entry = await createTestEntry(
746+
createEntryClient,
747+
defaultParams,
748+
TestDefaults.contentType.withCrossSpaceReferenceId
749+
)
750+
entry2 = await createTestEntry(
751+
createEntryClient,
752+
defaultParams,
753+
TestDefaults.contentType.withCrossSpaceReferenceId
756754
)
755+
// add entries to release
756+
release = await updateReleaseWithEntries(createEntryClient, release, [entry, entry2])
757757

758-
release = await createEntryClient.release.create(defaultParams, {
759-
title: 'Test Release',
760-
entities: {
761-
sys: { type: 'Array' },
762-
items: [
763-
{
764-
entity: makeLink('Entry', entry.sys.id),
765-
action: 'publish',
766-
},
767-
{
768-
entity: makeLink('Entry', entry2.sys.id),
769-
action: 'publish',
770-
},
771-
],
772-
},
773-
startDate: '2025-08-28T10:00:000Z',
774-
})
758+
// update release entry with title
759+
await updateReleaseEntryTitle(createEntryClient, release, entry)
775760
})
776761

777762
afterAll(async () => {
@@ -811,6 +796,33 @@ describe('Entry Api', () => {
811796
expect(fetchedEntry.sys.id).toEqual(entry.sys.id)
812797
expect(fetchedEntry.sys.release.sys.id).toEqual(release.sys.id)
813798
})
799+
800+
test('entry.patch works', async () => {
801+
const entryToPatch = await createEntryClient.entry.get({
802+
entryId: entry.sys.id,
803+
releaseId: release.sys.id,
804+
})
805+
806+
const patchedEntry = await createEntryClient.entry.patch(
807+
{
808+
entryId: entryToPatch.sys.id,
809+
releaseId: release.sys.id,
810+
version: entryToPatch.sys.version,
811+
},
812+
[
813+
{
814+
op: 'replace' as const,
815+
path: '/fields/title/en-US',
816+
value: 'Entry patched via release',
817+
},
818+
]
819+
)
820+
821+
expect(patchedEntry.sys.id).toEqual(entryToPatch.sys.id)
822+
expect(patchedEntry.fields.title['en-US']).toEqual('Entry patched via release')
823+
expect(patchedEntry.sys.version).toBeGreaterThan(entryToPatch.sys.version)
824+
expect((patchedEntry as any).sys.release.sys.id).toEqual(entryToPatch.sys.release.sys.id)
825+
})
814826
})
815827

816828
describe('releaseId is provided in default params, but not in params', () => {
@@ -841,6 +853,31 @@ describe('Entry Api', () => {
841853
expect(fetchedEntry.sys.id).toEqual(entry.sys.id)
842854
expect(fetchedEntry.sys.release.sys.id).toEqual(release.sys.id)
843855
})
856+
857+
test('entry.patch works', async () => {
858+
const entryToPatch = await createEntryClient.entry.get({
859+
entryId: entry.sys.id,
860+
})
861+
862+
const patchedEntry = await createEntryClient.entry.patch(
863+
{
864+
entryId: entryToPatch.sys.id,
865+
version: entryToPatch.sys.version,
866+
},
867+
[
868+
{
869+
op: 'replace',
870+
path: '/fields/title/en-US',
871+
value: 'Entry patched via default release',
872+
},
873+
]
874+
)
875+
876+
expect(patchedEntry.sys.id).toEqual(entryToPatch.sys.id)
877+
expect(patchedEntry.fields.title['en-US']).toEqual('Entry patched via default release')
878+
expect(patchedEntry.sys.version).toBeGreaterThan(entryToPatch.sys.version)
879+
expect((patchedEntry as any).sys.release.sys.id).toEqual(entryToPatch.sys.release.sys.id)
880+
})
844881
})
845882
})
846883

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import type { PlainClientAPI, ReleaseProps, EntryProps } from '../../../lib/export-types'
2+
import { makeLink } from '../../utils'
3+
4+
/**
5+
* Creates an empty release for testing purposes
6+
*/
7+
export async function createEmptyRelease(
8+
client: PlainClientAPI,
9+
defaultParams: {
10+
environmentId: string
11+
spaceId: string
12+
releaseSchemaVersion: 'Release.v2'
13+
}
14+
): Promise<ReleaseProps> {
15+
return client.release.create(defaultParams, {
16+
title: 'Test Release',
17+
entities: {
18+
sys: { type: 'Array' },
19+
items: [],
20+
},
21+
startDate: '2025-08-28T10:00:000Z',
22+
})
23+
}
24+
25+
/**
26+
* Creates a test entry for release testing
27+
*/
28+
export async function createTestEntry(
29+
client: PlainClientAPI,
30+
defaultParams: {
31+
environmentId: string
32+
spaceId: string
33+
releaseSchemaVersion: 'Release.v2'
34+
},
35+
contentTypeId: string
36+
): Promise<EntryProps> {
37+
return client.entry.create(
38+
{ ...defaultParams, contentTypeId },
39+
{
40+
fields: {
41+
title: {
42+
'en-US': 'Test Entry for Release',
43+
},
44+
},
45+
}
46+
)
47+
}
48+
49+
/**
50+
* Updates a release with the specified entries
51+
*/
52+
export async function updateReleaseWithEntries(
53+
client: PlainClientAPI,
54+
release: ReleaseProps,
55+
entries: EntryProps[]
56+
): Promise<ReleaseProps> {
57+
const { sys, ...releaseData } = release
58+
59+
return client.release.update(
60+
{
61+
spaceId: sys.space.sys.id,
62+
environmentId: sys.environment.sys.id,
63+
releaseId: sys.id,
64+
releaseSchemaVersion: sys.schemaVersion,
65+
version: sys.version,
66+
},
67+
{
68+
...releaseData,
69+
entities: {
70+
sys: { type: 'Array' },
71+
items: entries.map((entry) => ({
72+
entity: makeLink('Entry', entry.sys.id),
73+
action: 'publish' as const,
74+
})),
75+
},
76+
}
77+
)
78+
}
79+
80+
/**
81+
* Updates a release entry with a new title
82+
*/
83+
export async function updateReleaseEntryTitle(
84+
client: PlainClientAPI,
85+
release: ReleaseProps,
86+
entry: EntryProps,
87+
newTitle: string = 'Updated Test Entry for Release'
88+
): Promise<EntryProps> {
89+
// get the release entry
90+
const releaseEntryToUpdate = await client.release.entry.get({
91+
entryId: entry.sys.id,
92+
environmentId: release.sys.environment.sys.id,
93+
spaceId: release.sys.space.sys.id,
94+
releaseId: release.sys.id,
95+
})
96+
97+
// update the release entry with new title
98+
return client.release.entry.update(
99+
{
100+
entryId: releaseEntryToUpdate.sys.id,
101+
environmentId: release.sys.environment.sys.id,
102+
spaceId: release.sys.space.sys.id,
103+
releaseId: release.sys.id,
104+
},
105+
{
106+
sys: releaseEntryToUpdate.sys,
107+
fields: {
108+
title: {
109+
'en-US': newTitle,
110+
},
111+
},
112+
}
113+
)
114+
}

0 commit comments

Comments
 (0)