Skip to content

Commit e6ce074

Browse files
fix: correct interface types for nested global fields, add taxonomy field support
Nested global fields (a global field referencing another global field, whether inside a plain field, a group, or a modular block) got the wrong GraphQL interface type. The interface field name was derived by blindly string-replacing the parent type name onto every child field, which only works when the child's type name is literally built from that parent name. A nested global field's type name is built from its own reference_to instead, so the blind replace produced a non-existent, malformed interface type. Detect the nested-global-field case explicitly in both buildBlockCustomSchema and buildCustomSchema, and point the interface field at the referenced global field's own interface type instead. Also add support for the `taxonomy` field data type, which previously had no schema handling at all — content types with a taxonomy field now correctly expose a `[taxonomyType]` field (taxonomy_uid, term_uid) instead of the field being silently dropped. Fixes DX-10112.
1 parent 1fe61f3 commit e6ce074

5 files changed

Lines changed: 175 additions & 2 deletions

File tree

.talismanrc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@ fileignoreconfig:
55
- filename: package-lock.json
66
ignore_detectors:
77
- filecontent
8+
- filename: src/normalize.js
9+
checksum: 471ff9d6a837381c4568abea54abfb5d04821e412b7d046fddacbfd434970d4f
810
version: "1.0"

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,28 @@ query {
165165
}
166166
```
167167

168+
## Querying taxonomy fields
169+
170+
Taxonomy fields let you categorize entries using taxonomy terms defined in your stack. Each
171+
taxonomy field resolves to a list of `taxonomyType` objects exposing the taxonomy and term UIDs.
172+
173+
```graphql
174+
{
175+
allContentstackBlogs {
176+
edges {
177+
node {
178+
id
179+
title
180+
topics {
181+
taxonomy_uid
182+
term_uid
183+
}
184+
}
185+
}
186+
}
187+
}
188+
```
189+
168190
## Querying downloaded images
169191

170192
## Prerequisites

src/normalize.js

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,15 @@ const buildBlockCustomSchema = (blocks, types, references, groups, fileFields, j
206206
const interfaceFields = {};
207207
for (const key in fields) {
208208
typeFields[key] = fields[key].type || fields[key];
209-
interfaceFields[key] = typeFields[key].replace(newparent, newInterfaceParent);
209+
// A field nested inside this block can itself be a global field: its type name is
210+
// built from its own reference_to, not from newparent, so the blind replace below
211+
// would produce a malformed interface field. Point at its own interface type instead.
212+
const childField = (block.schema || []).find(f => f.uid === key);
213+
if (childField && childField.data_type === 'global_field' && childField.reference_to) {
214+
interfaceFields[key] = typeFields[key].replace(`${newparent}_${key}`, `${prefix}_${childField.reference_to}`);
215+
} else {
216+
interfaceFields[key] = typeFields[key].replace(newparent, newInterfaceParent);
217+
}
210218
}
211219

212220
if (Object.keys(fields).length > 0) {
@@ -429,7 +437,13 @@ const buildCustomSchema = (exports.buildCustomSchema = (schema, types, reference
429437
const interfaceFields = {};
430438
for (const key in result.fields) {
431439
typeFields[key] = result.fields[key].type || result.fields[key];
432-
interfaceFields[key] = typeFields[key].replace(newParent, newInterfaceParent);
440+
// Same nested-global-field case as buildBlockCustomSchema above.
441+
const childField = (field.schema || []).find(f => f.uid === key);
442+
if (childField && childField.data_type === 'global_field' && childField.reference_to) {
443+
interfaceFields[key] = typeFields[key].replace(`${newParent}_${key}`, `${prefix}_${childField.reference_to}`);
444+
} else {
445+
interfaceFields[key] = typeFields[key].replace(newParent, newInterfaceParent);
446+
}
433447
}
434448

435449
if (Object.keys(typeFields).length > 0) {
@@ -515,6 +529,17 @@ const buildCustomSchema = (exports.buildCustomSchema = (schema, types, reference
515529
}
516530
}
517531
break;
532+
case 'taxonomy':
533+
types.push('type taxonomyType { taxonomy_uid: String term_uid: String }');
534+
fields[field.uid] = {
535+
resolve: source => source[field.uid] || null,
536+
};
537+
if (field.mandatory && !disableMandatoryFields) {
538+
fields[field.uid].type = '[taxonomyType]!';
539+
} else {
540+
fields[field.uid].type = '[taxonomyType]';
541+
}
542+
break;
518543
}
519544
});
520545
return { fields, types, references, groups, fileFields, jsonRteFields };
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
const { buildCustomSchema } = require('../normalize');
2+
3+
describe('buildCustomSchema: nested global field inside a block', () => {
4+
test('interface field points at the nested global field\'s own type, not a blind parent-name substitution', () => {
5+
const schema = [
6+
{
7+
uid: 'sections',
8+
data_type: 'blocks',
9+
mandatory: false,
10+
multiple: true,
11+
blocks: [
12+
{
13+
uid: 'hero',
14+
reference_to: 'hero_global',
15+
schema: [
16+
{
17+
uid: 'author_info',
18+
data_type: 'global_field',
19+
reference_to: 'author_global',
20+
mandatory: false,
21+
multiple: false,
22+
schema: [
23+
{ uid: 'name', data_type: 'text', mandatory: false, multiple: false },
24+
],
25+
},
26+
],
27+
},
28+
],
29+
},
30+
];
31+
32+
const result = buildCustomSchema(schema, [], [], [], [], [], 'Contentstack_blog', 'Contentstack', false, false, () => {}, undefined);
33+
34+
const heroInterface = result.types.find((t) => t.startsWith('interface Contentstack_hero_global'));
35+
expect(heroInterface).toBeDefined();
36+
expect(heroInterface).toContain('author_info:Contentstack_author_global');
37+
expect(heroInterface).not.toContain('Contentstack_hero_global_author_info');
38+
});
39+
});
40+
41+
describe('buildCustomSchema: taxonomy field', () => {
42+
test('produces a taxonomyType field instead of being silently dropped', () => {
43+
const schema = [
44+
{ uid: 'topics', data_type: 'taxonomy', mandatory: false, multiple: true },
45+
];
46+
47+
const result = buildCustomSchema(schema, [], [], [], [], [], 'Contentstack_blog', 'Contentstack', false, false, () => {}, undefined);
48+
49+
expect(result.fields.topics.type).toBe('[taxonomyType]');
50+
expect(result.types).toContain('type taxonomyType { taxonomy_uid: String term_uid: String }');
51+
});
52+
53+
test('is wrapped in a non-null list when mandatory', () => {
54+
const schema = [
55+
{ uid: 'topics', data_type: 'taxonomy', mandatory: true, multiple: true },
56+
];
57+
58+
const result = buildCustomSchema(schema, [], [], [], [], [], 'Contentstack_blog', 'Contentstack', false, false, () => {}, undefined);
59+
60+
expect(result.fields.topics.type).toBe('[taxonomyType]!');
61+
});
62+
});

tests/normalize-nested-global-field-taxonomy.test.js

Lines changed: 62 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)