Skip to content
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
8 changes: 8 additions & 0 deletions .talismanrc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ fileignoreconfig:
- filename: package-lock.json
ignore_detectors:
- filecontent
# The four entries below are false positives on the literal string "api_key" — a
# Contentstack plugin option name used throughout this codebase, not a credential.
# Each checksum pins the exact file content at the time it was reviewed: any future
# edit to these files changes the checksum, so Talisman re-scans automatically and a
# real secret added later would still be caught. Narrower per-line ignoring isn't
# supported by this detector, so a whole-file checksum is the tightest scope available.
- filename: src/normalize.js
checksum: 471ff9d6a837381c4568abea54abfb5d04821e412b7d046fddacbfd434970d4f
Comment thread
abhishek-ezhava-cstk marked this conversation as resolved.
- filename: src/create-schema-customization.js
checksum: f4745db1b4f868844d5668b56c1401c8c9d87fb82a8742d2a9ac97f37dc5c587
- filename: src/tests/exclude-content-types.test.js
Expand Down
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,28 @@ query {
}
```

## Querying taxonomy fields

Taxonomy fields let you categorize entries using taxonomy terms defined in your stack. Each
taxonomy field resolves to a list of `taxonomyType` objects exposing the taxonomy and term UIDs.

```graphql
{
allContentstackBlogs {
edges {
node {
id
title
topics {
taxonomy_uid
term_uid
}
}
}
}
}
```

## Querying downloaded images

## Prerequisites
Expand Down
33 changes: 31 additions & 2 deletions src/normalize.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,18 @@ const buildBlockCustomSchema = (blocks, types, references, groups, fileFields, j

const typeFields = {};
const interfaceFields = {};
const blockSchemaByUid = new Map((block.schema || []).map(f => [f.uid, f]));
for (const key in fields) {
typeFields[key] = fields[key].type || fields[key];
interfaceFields[key] = typeFields[key].replace(newparent, newInterfaceParent);
// A field nested inside this block can itself be a global field: its type name is
// built from its own reference_to, not from newparent, so the blind replace below
// would produce a malformed interface field. Point at its own interface type instead.
const childField = blockSchemaByUid.get(key);
if (childField && childField.data_type === 'global_field' && childField.reference_to) {
interfaceFields[key] = typeFields[key].replace(`${newparent}_${key}`, `${prefix}_${childField.reference_to}`);
} else {
interfaceFields[key] = typeFields[key].replace(newparent, newInterfaceParent);
}
}

if (Object.keys(fields).length > 0) {
Expand Down Expand Up @@ -427,9 +436,16 @@ const buildCustomSchema = (exports.buildCustomSchema = (schema, types, reference

const typeFields = {};
const interfaceFields = {};
const fieldSchemaByUid = new Map((field.schema || []).map(f => [f.uid, f]));
for (const key in result.fields) {
typeFields[key] = result.fields[key].type || result.fields[key];
interfaceFields[key] = typeFields[key].replace(newParent, newInterfaceParent);
// Same nested-global-field case as buildBlockCustomSchema above.
const childField = fieldSchemaByUid.get(key);
if (childField && childField.data_type === 'global_field' && childField.reference_to) {
interfaceFields[key] = typeFields[key].replace(`${newParent}_${key}`, `${prefix}_${childField.reference_to}`);
} else {
interfaceFields[key] = typeFields[key].replace(newParent, newInterfaceParent);
}
}

if (Object.keys(typeFields).length > 0) {
Expand Down Expand Up @@ -515,6 +531,19 @@ const buildCustomSchema = (exports.buildCustomSchema = (schema, types, reference
}
}
break;
case 'taxonomy':
if (!types.includes('type taxonomyType { taxonomy_uid: String term_uid: String }')) {
types.push('type taxonomyType { taxonomy_uid: String term_uid: String }');
}
fields[field.uid] = {
resolve: source => source[field.uid] || null,
};
if (field.mandatory && !disableMandatoryFields) {
fields[field.uid].type = '[taxonomyType]!';
} else {
fields[field.uid].type = '[taxonomyType]';
}
break;
Comment thread
abhishek-ezhava-cstk marked this conversation as resolved.
}
});
return { fields, types, references, groups, fileFields, jsonRteFields };
Expand Down
76 changes: 76 additions & 0 deletions src/tests/normalize-nested-global-field-taxonomy.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
const { buildCustomSchema } = require('../normalize');

describe('buildCustomSchema: nested global field inside a block', () => {
test('interface field points at the nested global field\'s own type, not a blind parent-name substitution', () => {
const schema = [
{
uid: 'sections',
data_type: 'blocks',
mandatory: false,
multiple: true,
blocks: [
{
uid: 'hero',
reference_to: 'hero_global',
schema: [
{
uid: 'author_info',
data_type: 'global_field',
reference_to: 'author_global',
mandatory: false,
multiple: false,
schema: [
{ uid: 'name', data_type: 'text', mandatory: false, multiple: false },
],
},
],
},
],
},
];

const result = buildCustomSchema(schema, [], [], [], [], [], 'Contentstack_blog', 'Contentstack', false, false, () => {}, undefined);

const heroInterface = result.types.find((t) => t.startsWith('interface Contentstack_hero_global'));
expect(heroInterface).toBeDefined();
expect(heroInterface).toContain('author_info:Contentstack_author_global');
expect(heroInterface).not.toContain('Contentstack_hero_global_author_info');
});
});

describe('buildCustomSchema: taxonomy field', () => {
test('produces a taxonomyType field instead of being silently dropped', () => {
const schema = [
{ uid: 'topics', data_type: 'taxonomy', mandatory: false, multiple: true },
];

const result = buildCustomSchema(schema, [], [], [], [], [], 'Contentstack_blog', 'Contentstack', false, false, () => {}, undefined);

expect(result.fields.topics.type).toBe('[taxonomyType]');
expect(result.types).toContain('type taxonomyType { taxonomy_uid: String term_uid: String }');
});

test('is wrapped in a non-null list when mandatory', () => {
const schema = [
{ uid: 'topics', data_type: 'taxonomy', mandatory: true, multiple: true },
];

const result = buildCustomSchema(schema, [], [], [], [], [], 'Contentstack_blog', 'Contentstack', false, false, () => {}, undefined);

expect(result.fields.topics.type).toBe('[taxonomyType]!');
});

test('multiple taxonomy fields on one content type do not push duplicate taxonomyType definitions', () => {
const schema = [
{ uid: 'topics', data_type: 'taxonomy', mandatory: false, multiple: true },
{ uid: 'regions', data_type: 'taxonomy', mandatory: false, multiple: true },
];

const result = buildCustomSchema(schema, [], [], [], [], [], 'Contentstack_blog', 'Contentstack', false, false, () => {}, undefined);

const taxonomyTypeDefs = result.types.filter((t) => t === 'type taxonomyType { taxonomy_uid: String term_uid: String }');
expect(taxonomyTypeDefs).toHaveLength(1);
expect(result.fields.topics.type).toBe('[taxonomyType]');
expect(result.fields.regions.type).toBe('[taxonomyType]');
});
});
82 changes: 82 additions & 0 deletions tests/normalize-nested-global-field-taxonomy.test.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading