Skip to content
16 changes: 12 additions & 4 deletions lib/adapters/REST/endpoints/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,23 @@ import type { OpPatch } from 'json-patch'
import type { SetOptional } from 'type-fest'
import type {
CollectionProp,
GetReleaseEntryParams,
GetEntryParams,
GetManyEntryParams,
GetSpaceEnvironmentParams,
KeyValueMap,
PatchEntryParams,
PatchReleaseEntryParams,
QueryParams,
} from '../../../common-types'
import type { CreateEntryProps, EntryProps, EntryReferenceProps } from '../../../entities/entry'
import type { RestEndpoint } from '../types'
import * as raw from './raw'
import { normalizeSelect } from './utils'
import * as releaseEntry from './release-entry'

export const get: RestEndpoint<'Entry', 'get'> = <T extends KeyValueMap = KeyValueMap>(
http: AxiosInstance,
params: GetReleaseEntryParams & QueryParams,
params: GetEntryParams & QueryParams,
rawData?: unknown,
headers?: RawAxiosRequestHeaders
) => {
Expand Down Expand Up @@ -55,7 +59,7 @@ export const getPublished: RestEndpoint<'Entry', 'getPublished'> = <

export const getMany: RestEndpoint<'Entry', 'getMany'> = <T extends KeyValueMap = KeyValueMap>(
http: AxiosInstance,
params: GetSpaceEnvironmentParams & QueryParams & { releaseId?: string },
params: GetManyEntryParams & QueryParams,
rawData?: unknown,
headers?: RawAxiosRequestHeaders
) => {
Expand All @@ -75,10 +79,14 @@ export const getMany: RestEndpoint<'Entry', 'getMany'> = <T extends KeyValueMap

export const patch: RestEndpoint<'Entry', 'patch'> = <T extends KeyValueMap = KeyValueMap>(
http: AxiosInstance,
params: GetSpaceEnvironmentParams & { entryId: string; version: number },
params: PatchEntryParams & QueryParams,
data: OpPatch[],
headers?: RawAxiosRequestHeaders
) => {
if (params.releaseId) {
return releaseEntry.patch(http, params as PatchReleaseEntryParams, data, headers ?? {})
}

return raw.patch<EntryProps<T>>(
http,
`/spaces/${params.spaceId}/environments/${params.environmentId}/entries/${params.entryId}`,
Expand Down
14 changes: 10 additions & 4 deletions lib/common-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1604,15 +1604,15 @@ export type MRActions = {
return: CollectionProp<EntryProps<any>>
}
getMany: {
params: GetSpaceEnvironmentParams & QueryParams & { releaseId?: string }
params: GetManyEntryParams & QueryParams
return: CollectionProp<EntryProps<any, any>>
}
get: {
params: GetReleaseEntryParams & QueryParams
params: GetEntryParams & QueryParams
return: EntryProps<any, any>
}
patch: {
params: GetSpaceEnvironmentParams & { entryId: string; version: number }
params: PatchEntryParams & QueryParams
payload: OpPatch[]
headers?: RawAxiosRequestHeaders
return: EntryProps<any>
Expand Down Expand Up @@ -2385,7 +2385,13 @@ export type GetCommentParams = (GetEntryParams | GetCommentParentEntityParams) &
}
export type GetContentTypeParams = GetSpaceEnvironmentParams & { contentTypeId: string }
export type GetEditorInterfaceParams = GetSpaceEnvironmentParams & { contentTypeId: string }
export type GetEntryParams = GetSpaceEnvironmentParams & { entryId: string }
export type GetEntryParams = GetSpaceEnvironmentParams & { entryId: string; releaseId?: string }
export type GetManyEntryParams = GetSpaceEnvironmentParams & { releaseId?: string }
export type PatchEntryParams = GetSpaceEnvironmentParams & {
entryId: string
version: number
releaseId?: string
}
export type GetExtensionParams = GetSpaceEnvironmentParams & { extensionId: string }
export type GetEnvironmentTemplateParams = GetOrganizationParams & { environmentTemplateId: string }
export type GetFunctionParams = GetAppDefinitionParams & { functionId: string }
Expand Down
9 changes: 6 additions & 3 deletions lib/plain/common-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import type {
EnvironmentTemplateParams,
GetBulkActionParams,
GetContentTypeParams,
GetEntryParams,
GetEnvironmentTemplateParams,
GetManyEntryParams,
GetManyReleaseEntryParams,
GetOrganizationMembershipParams,
GetOrganizationParams,
Expand All @@ -19,6 +21,7 @@ import type {
GetSpaceEnvironmentParams,
GetSpaceParams,
KeyValueMap,
PatchEntryParams,
PatchReleaseEntryParams,
QueryParams,
ReleaseEnvironmentParams,
Expand Down Expand Up @@ -279,7 +282,7 @@ export type PlainClientAPI = {
headers?: RawAxiosRequestHeaders
): Promise<CollectionProp<EntryProps<T>>>
getMany<T extends KeyValueMap = KeyValueMap>(
params: OptionalDefaults<GetSpaceEnvironmentParams & QueryParams & { releaseId?: string }>,
params: OptionalDefaults<GetManyEntryParams & QueryParams>,
rawData?: unknown,
headers?: RawAxiosRequestHeaders
): Promise<
Expand All @@ -299,7 +302,7 @@ export type PlainClientAPI = {
>
>
get<T extends KeyValueMap = KeyValueMap>(
params: OptionalDefaults<GetReleaseEntryParams>,
params: OptionalDefaults<GetEntryParams & QueryParams>,
rawData?: unknown,
headers?: RawAxiosRequestHeaders
): Promise<
Expand All @@ -322,7 +325,7 @@ export type PlainClientAPI = {
headers?: RawAxiosRequestHeaders
): Promise<EntryProps<T>>
patch<T extends KeyValueMap = KeyValueMap>(
params: OptionalDefaults<GetSpaceEnvironmentParams & { entryId: string; version?: number }>,
params: OptionalDefaults<PatchEntryParams & QueryParams>,
rawData: OpPatch[],
headers?: RawAxiosRequestHeaders
): Promise<EntryProps<T>>
Expand Down
113 changes: 75 additions & 38 deletions test/integration/entry-integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ import type {
Space,
} from '../../lib/export-types'
import { TestDefaults } from '../defaults'
import { makeLink } from '../utils'
import {
createEmptyRelease,
createTestEntry,
updateReleaseWithEntries,
updateReleaseEntryTitle,
} from './utils/release-entry.utils'

describe('Entry Api', () => {
afterAll(async () => await timeoutToCalmRateLimiting())
Expand Down Expand Up @@ -718,7 +723,7 @@ describe('Entry Api', () => {
})

//test releasev2 entry logic
describe('read plainClientApi with releaseId', () => {
describe('plainClientApi with releaseId', () => {
let entry: EntryProps
let entry2: EntryProps
let release: ReleaseProps
Expand All @@ -733,45 +738,25 @@ describe('Entry Api', () => {
}
createEntryClient = initPlainClient(defaultParams)

entry = await createEntryClient.entry.create(
{ ...defaultParams, contentTypeId: TestDefaults.contentType.withCrossSpaceReferenceId },
{
fields: {
title: {
'en-US': 'Test Entry for Release',
},
},
}
)
// create release
release = await createEmptyRelease(createEntryClient, defaultParams)

entry2 = await createEntryClient.entry.create(
{ ...defaultParams, contentTypeId: TestDefaults.contentType.withCrossSpaceReferenceId },
{
fields: {
title: {
'en-US': 'Test Entry for Release',
},
},
}
// create entries to add to release
entry = await createTestEntry(
createEntryClient,
defaultParams,
TestDefaults.contentType.withCrossSpaceReferenceId
)
entry2 = await createTestEntry(
createEntryClient,
defaultParams,
TestDefaults.contentType.withCrossSpaceReferenceId
)
// add entries to release
release = await updateReleaseWithEntries(createEntryClient, release, [entry, entry2])

release = await createEntryClient.release.create(defaultParams, {
title: 'Test Release',
entities: {
sys: { type: 'Array' },
items: [
{
entity: makeLink('Entry', entry.sys.id),
action: 'publish',
},
{
entity: makeLink('Entry', entry2.sys.id),
action: 'publish',
},
],
},
startDate: '2025-08-28T10:00:000Z',
})
// update release entry with title
await updateReleaseEntryTitle(createEntryClient, release, entry)
})

afterAll(async () => {
Expand Down Expand Up @@ -811,6 +796,33 @@ describe('Entry Api', () => {
expect(fetchedEntry.sys.id).toEqual(entry.sys.id)
expect(fetchedEntry.sys.release.sys.id).toEqual(release.sys.id)
})

test('entry.patch works', async () => {
const entryToPatch = await createEntryClient.entry.get({
entryId: entry.sys.id,
releaseId: release.sys.id,
})

const patchedEntry = await createEntryClient.entry.patch(
{
entryId: entryToPatch.sys.id,
releaseId: release.sys.id,
version: entryToPatch.sys.version,
},
[
{
op: 'replace' as const,
path: '/fields/title/en-US',
value: 'Entry patched via release',
},
]
)

expect(patchedEntry.sys.id).toEqual(entryToPatch.sys.id)
expect(patchedEntry.fields.title['en-US']).toEqual('Entry patched via release')
expect(patchedEntry.sys.version).toBeGreaterThan(entryToPatch.sys.version)
expect((patchedEntry as any).sys.release.sys.id).toEqual(entryToPatch.sys.release.sys.id)
})
})

describe('releaseId is provided in default params, but not in params', () => {
Expand Down Expand Up @@ -841,6 +853,31 @@ describe('Entry Api', () => {
expect(fetchedEntry.sys.id).toEqual(entry.sys.id)
expect(fetchedEntry.sys.release.sys.id).toEqual(release.sys.id)
})

test('entry.patch works', async () => {
const entryToPatch = await createEntryClient.entry.get({
entryId: entry.sys.id,
})

const patchedEntry = await createEntryClient.entry.patch(
{
entryId: entryToPatch.sys.id,
version: entryToPatch.sys.version,
},
[
{
op: 'replace',
path: '/fields/title/en-US',
value: 'Entry patched via default release',
},
]
)

expect(patchedEntry.sys.id).toEqual(entryToPatch.sys.id)
expect(patchedEntry.fields.title['en-US']).toEqual('Entry patched via default release')
expect(patchedEntry.sys.version).toBeGreaterThan(entryToPatch.sys.version)
expect((patchedEntry as any).sys.release.sys.id).toEqual(entryToPatch.sys.release.sys.id)
})
})
})

Expand Down
114 changes: 114 additions & 0 deletions test/integration/utils/release-entry.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import type { PlainClientAPI, ReleaseProps, EntryProps } from '../../../lib/export-types'
import { makeLink } from '../../utils'

/**
* Creates an empty release for testing purposes
*/
export async function createEmptyRelease(
client: PlainClientAPI,
defaultParams: {
environmentId: string
spaceId: string
releaseSchemaVersion: 'Release.v2'
}
): Promise<ReleaseProps> {
return client.release.create(defaultParams, {
title: 'Test Release',
entities: {
sys: { type: 'Array' },
items: [],
},
startDate: '2025-08-28T10:00:000Z',
})
}

/**
* Creates a test entry for release testing
*/
export async function createTestEntry(
client: PlainClientAPI,
defaultParams: {
environmentId: string
spaceId: string
releaseSchemaVersion: 'Release.v2'
},
contentTypeId: string
): Promise<EntryProps> {
return client.entry.create(
{ ...defaultParams, contentTypeId },
{
fields: {
title: {
'en-US': 'Test Entry for Release',
},
},
}
)
}

/**
* Updates a release with the specified entries
*/
export async function updateReleaseWithEntries(
client: PlainClientAPI,
release: ReleaseProps,
entries: EntryProps[]
): Promise<ReleaseProps> {
const { sys, ...releaseData } = release

return client.release.update(
{
spaceId: sys.space.sys.id,
environmentId: sys.environment.sys.id,
releaseId: sys.id,
releaseSchemaVersion: sys.schemaVersion,
version: sys.version,
},
{
...releaseData,
entities: {
sys: { type: 'Array' },
items: entries.map((entry) => ({
entity: makeLink('Entry', entry.sys.id),
action: 'publish' as const,
})),
},
}
)
}

/**
* Updates a release entry with a new title
*/
export async function updateReleaseEntryTitle(
client: PlainClientAPI,
release: ReleaseProps,
entry: EntryProps,
newTitle: string = 'Updated Test Entry for Release'
): Promise<EntryProps> {
// get the release entry
const releaseEntryToUpdate = await client.release.entry.get({
entryId: entry.sys.id,
environmentId: release.sys.environment.sys.id,
spaceId: release.sys.space.sys.id,
releaseId: release.sys.id,
})

// update the release entry with new title
return client.release.entry.update(
{
entryId: releaseEntryToUpdate.sys.id,
environmentId: release.sys.environment.sys.id,
spaceId: release.sys.space.sys.id,
releaseId: release.sys.id,
},
{
sys: releaseEntryToUpdate.sys,
fields: {
title: {
'en-US': newTitle,
},
},
}
)
}
Loading