Skip to content

feat: ensure entity.sys is selected when withContentSourceMaps is enabled #2219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions lib/create-contentful-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
} from './utils/validate-params'
import validateSearchParameters from './utils/validate-search-parameters'
import validateTimestamp from './utils/validate-timestamp'
import getQuerySelectionSet from './utils/query-selection-set'

const ASSET_KEY_MAX_LIFETIME = 48 * 60 * 60

Expand Down Expand Up @@ -107,6 +108,15 @@ export default function createContentfulApi<OptionType extends ChainOptions>(

if (areAllowed) {
query.includeContentSourceMaps = true

// Ensure that content source maps and required attributes are selected
if (query.select) {
const selection = getQuerySelectionSet(query)

selection.add('sys')

query.select = Array.from(selection).join(',')
}
}

return query
Expand Down
10 changes: 3 additions & 7 deletions lib/utils/normalize-select.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import getQuerySelectionSet from './query-selection-set'

/*
* sdk relies heavily on sys metadata
* so we cannot omit the sys property on sdk level entirely
Expand All @@ -9,13 +11,7 @@ export default function normalizeSelect(query) {
return query
}

// The selection of fields for the query is limited
// Get the different parts that are listed for selection
const allSelects = Array.isArray(query.select)
? query.select
: query.select.split(',').map((q) => q.trim())
// Move the parts into a set for easy access and deduplication
const selectedSet = new Set(allSelects)
const selectedSet = getQuerySelectionSet(query)

// If we already select all of `sys` we can just return
// since we're anyway fetching everything that is needed
Expand Down
14 changes: 14 additions & 0 deletions lib/utils/query-selection-set.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default function getQuerySelectionSet(query: Record<string, any>): Set<string> {
if (!query.select) {
return new Set()
}

// The selection of fields for the query is limited
// Get the different parts that are listed for selection
const allSelects = Array.isArray(query.select)
? query.select
: query.select.split(',').map((q) => q.trim())

// Move the parts into a set for easy access and deduplication
return new Set(allSelects)
}
53 changes: 36 additions & 17 deletions test/integration/getAssets.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,32 +47,51 @@ describe('getAssets', () => {
await expect(invalidClient.getAssets()).rejects.toThrow(ValidationError)
})

test('preview client', async () => {
const response = await previewClient.getAssets()
describe('preview client', () => {
it('requests content source maps', async () => {
const response = await previewClient.getAssets()

expect(response.items).not.toHaveLength(0)
expect(response.items).not.toHaveLength(0)

response.items.forEach((item) => {
expect(item.sys.type).toEqual('Asset')
expect(item.fields).toBeDefined()
expect(typeof item.fields.title).toBe('string')
response.items.forEach((item) => {
expect(item.sys.type).toEqual('Asset')
expect(item.fields).toBeDefined()
expect(typeof item.fields.title).toBe('string')
})

expect(response.sys?.contentSourceMapsLookup).toBeDefined()
})

expect(response.sys?.contentSourceMapsLookup).toBeDefined()
})
it('enforces selection of sys if query.select is present', async () => {
const response = await previewClient.getAssets({
select: ['fields.title', 'sys.id', 'sys.type'],
})

test('preview client withAllLocales modifier', async () => {
const response = await previewClient.withAllLocales.getAssets()
expect(response.items).not.toHaveLength(0)

expect(response.items).not.toHaveLength(0)
response.items.forEach((item) => {
expect(item.sys.type).toEqual('Asset')
expect(item.fields).toBeDefined()
expect(typeof item.fields.title).toBe('string')
expect(item.sys.contentSourceMaps).toBeDefined()
})

response.items.forEach((item) => {
expect(item.sys.type).toEqual('Asset')
expect(item.fields).toBeDefined()
expect(typeof item.fields.title).toBe('object')
expect(response.sys?.contentSourceMapsLookup).toBeDefined()
})

expect(response.sys?.contentSourceMapsLookup).toBeDefined()
it('works with withAllLocales modifier', async () => {
const response = await previewClient.withAllLocales.getAssets()

expect(response.items).not.toHaveLength(0)

response.items.forEach((item) => {
expect(item.sys.type).toEqual('Asset')
expect(item.fields).toBeDefined()
expect(typeof item.fields.title).toBe('object')
})

expect(response.sys?.contentSourceMapsLookup).toBeDefined()
})
})
})
})
10 changes: 10 additions & 0 deletions test/integration/getEntries.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,16 @@ describe('getEntries via client chain modifiers', () => {
assertCSMEntriesResponse(response)
})

test('enforces entry.sys when query.select is defined', async () => {
const response = await previewClient.getEntries({
include: 5,
'sys.id': entryWithResolvableLink,
select: ['fields', 'metadata.tags'],
})

assertCSMEntriesResponse(response)
})

test('withAllLocales modifier', async () => {
const response = await previewClient.withAllLocales.getEntries({
include: 5,
Expand Down