diff --git a/packages/optimizely-cms-sdk/src/graph/__test__/createQuery.test.ts b/packages/optimizely-cms-sdk/src/graph/__test__/createQuery.test.ts index 3b2b367..80cc6e9 100644 --- a/packages/optimizely-cms-sdk/src/graph/__test__/createQuery.test.ts +++ b/packages/optimizely-cms-sdk/src/graph/__test__/createQuery.test.ts @@ -553,3 +553,36 @@ describe('createFragment() empty objects', () => { `); }); }); + +describe('createFragment() with component properties', () => { + test('simple case', async () => { + const ctBlock = contentType({ + key: 'ctBlock', + baseType: '_component', + properties: { + p1: { type: 'string' }, + }, + }); + const ct1 = contentType({ + key: 'ct1', + baseType: '_page', + properties: { + p1: { type: 'component', contentType: ctBlock }, + }, + }); + initContentTypeRegistry([ct1, ctBlock]); + const result = await createFragment('ct1'); + expect(result).toMatchInlineSnapshot(` + [ + "fragment MediaMetadata on MediaMetadata { mimeType thumbnail content }", + "fragment ItemMetadata on ItemMetadata { changeset displayOption }", + "fragment InstanceMetadata on InstanceMetadata { changeset locales expired container owner routeSegment lastModifiedBy path createdBy }", + "fragment ContentUrl on ContentUrl { type default hierarchical internal graph base }", + "fragment IContentMetadata on IContentMetadata { key locale fallbackForLocale version displayName url {...ContentUrl} types published status created lastModified sortOrder variation ...MediaMetadata ...ItemMetadata ...InstanceMetadata }", + "fragment _IContent on _IContent { _id _metadata {...IContentMetadata} }", + "fragment ctBlockProperty on ctBlockProperty { __typename ctBlockProperty__p1:p1 }", + "fragment ct1 on ct1 { __typename ct1__p1:p1 { ...ctBlockProperty } ..._IContent }", + ] + `); + }); +}); diff --git a/packages/optimizely-cms-sdk/src/graph/__test__/index.test.ts b/packages/optimizely-cms-sdk/src/graph/__test__/index.test.ts new file mode 100644 index 0000000..a5d3882 --- /dev/null +++ b/packages/optimizely-cms-sdk/src/graph/__test__/index.test.ts @@ -0,0 +1,78 @@ +import { describe, expect, test } from 'vitest'; +import { removeTypePrefix } from '../index.js'; + +describe('removeTypePrefix()', () => { + test('basic functionality', () => { + const input = { + __typename: 'T', + T__p1: 'p1', + T__p2: 42, + T__p3: ['p3', 32], + T__p4: { nested: 'p4' }, + T__p5: { nested: ['p5'] }, + ShouldBeKept__p6: 'p6', + }; + const expected = { + __typename: 'T', + p1: 'p1', + p2: 42, + p3: ['p3', 32], + p4: { nested: 'p4' }, + p5: { nested: ['p5'] }, + ShouldBeKept__p6: 'p6', + }; + expect(removeTypePrefix(input)).toStrictEqual(expected); + }); + + test('should remove prefixes only in the same level', () => { + const input = { + __typename: 'T', + T__p1: { T_shouldBeKept: 'shouldBeKept' }, + }; + const expected = { + __typename: 'T', + p1: { T_shouldBeKept: 'shouldBeKept' }, + }; + expect(removeTypePrefix(input)).toStrictEqual(expected); + }); + + test('should work for nested objects', () => { + const input = { + __typename: 'T', + T__p1: { + __typename: 'U', + U__p1: 'p1', + U__p2: { + __typename: 'V', + V__p1: 'p1', + }, + }, + T__p2: [{ __typename: 'U', U__p1: 'p1' }], + }; + const expected = { + __typename: 'T', + p1: { + __typename: 'U', + p1: 'p1', + p2: { + __typename: 'V', + p1: 'p1', + }, + }, + p2: [{ __typename: 'U', p1: 'p1' }], + }; + expect(removeTypePrefix(input)).toStrictEqual(expected); + }); + + test('should not do anything if __typename is not found', () => { + const input = { + T__p1: 'hello', + T__p2: 42, + T__p3: ['hello', 32], + T__p4: { nested: 'nested' }, + T__p5: { nested: ['hello'] }, + }; + + expect(removeTypePrefix(input)).toStrictEqual(input); + }); +}); diff --git a/packages/optimizely-cms-sdk/src/graph/createQuery.ts b/packages/optimizely-cms-sdk/src/graph/createQuery.ts index ce13ee3..708d6fd 100644 --- a/packages/optimizely-cms-sdk/src/graph/createQuery.ts +++ b/packages/optimizely-cms-sdk/src/graph/createQuery.ts @@ -69,9 +69,16 @@ function convertProperty( name: string, property: AnyProperty, rootName: string, + suffix: string, visited: Set ): { fields: string[]; extraFragments: string[] } { - const result = convertPropertyField(name, property, rootName, visited); + const result = convertPropertyField( + name, + property, + rootName, + suffix, + visited + ); // logs warnings if the fragment generation causes potential issues const warningMessage = checkTypeConstraintIssues(rootName, property, result); @@ -95,12 +102,13 @@ function convertPropertyField( name: string, property: AnyProperty, rootName: string, + suffix: string, visited: Set ): { fields: string[]; extraFragments: string[] } { const fields: string[] = []; const subfields: string[] = []; const extraFragments: string[] = []; - const nameInFragment = `${rootName}__${name}:${name}`; + const nameInFragment = `${rootName}${suffix}__${name}:${name}`; if (property.type === 'component') { const key = property.contentType.key; @@ -146,7 +154,7 @@ function convertPropertyField( extraFragments.push(CONTENT_URL_FRAGMENT); fields.push(`${nameInFragment} { key url { ...ContentUrl }}`); } else if (property.type === 'array') { - const f = convertProperty(name, property.items, rootName, visited); + const f = convertProperty(name, property.items, rootName, suffix, visited); fields.push(...f.fields); extraFragments.push(...f.extraFragments); } else { @@ -237,6 +245,7 @@ export function createFragment( propKey, prop, contentTypeName, + suffix, visited ); fields.push(...f); diff --git a/packages/optimizely-cms-sdk/src/graph/index.ts b/packages/optimizely-cms-sdk/src/graph/index.ts index e016e77..8323cf1 100644 --- a/packages/optimizely-cms-sdk/src/graph/index.ts +++ b/packages/optimizely-cms-sdk/src/graph/index.ts @@ -144,8 +144,11 @@ type GetLinksResponse = { * * @param obj - The object to process (typically a GraphQL response) * @returns A new object with prefixes removed, or the original value for primitives + * + * Note: this function is exported only on this level for testing purposes. + * It should not be exported in the user-facing API */ -function removeTypePrefix(obj: any): any { +export function removeTypePrefix(obj: any): any { if (Array.isArray(obj)) { return obj.map((e) => removeTypePrefix(e)); }