From bfd45016a16b67f8921793bcd373e8b84d008446 Mon Sep 17 00:00:00 2001 From: Vladimir Razuvaev Date: Fri, 27 Mar 2020 20:44:50 +0700 Subject: [PATCH 1/2] fix(gatsby): Support grouping by reserved keywords --- .../src/schema/__tests__/fixtures/queries.js | 2 + .../gatsby/src/schema/__tests__/queries.js | 389 ++++++++++-------- packages/gatsby/src/schema/resolvers.js | 2 +- 3 files changed, 211 insertions(+), 182 deletions(-) diff --git a/packages/gatsby/src/schema/__tests__/fixtures/queries.js b/packages/gatsby/src/schema/__tests__/fixtures/queries.js index 8e93dbdbd696a..d0697fc474309 100644 --- a/packages/gatsby/src/schema/__tests__/fixtures/queries.js +++ b/packages/gatsby/src/schema/__tests__/fixtures/queries.js @@ -39,6 +39,7 @@ const nodes = [ }, frontmatter: { title: `Markdown File 1`, + tags: [], date: new Date(Date.UTC(2019, 0, 1)), authors: [`author2@example.com`, `author1@example.com`], reviewer___NODE: `author2`, @@ -55,6 +56,7 @@ const nodes = [ }, frontmatter: { title: `Markdown File 2`, + tags: [`constructor`], published: false, authors: [`author1@example.com`], reviewer___NODE: null, diff --git a/packages/gatsby/src/schema/__tests__/queries.js b/packages/gatsby/src/schema/__tests__/queries.js index 8058d9a29b406..e39cfe0fe4b71 100644 --- a/packages/gatsby/src/schema/__tests__/queries.js +++ b/packages/gatsby/src/schema/__tests__/queries.js @@ -654,14 +654,14 @@ describe(`Query schema`, () => { const results = await runQuery(query) expect(results.errors).toBeUndefined() expect(results.data).toMatchInlineSnapshot(` -Object { - "allMarkdown": Object { - "edges": Array [], - "nodes": Array [], - "totalCount": 0, - }, -} -`) + Object { + "allMarkdown": Object { + "edges": Array [], + "nodes": Array [], + "totalCount": 0, + }, + } + `) }) it(`adds nodes field as a convenience shortcut`, async () => { @@ -874,47 +874,47 @@ Object { const results = await runQuery(query) expect(results.errors).toBeUndefined() expect(results.data).toMatchInlineSnapshot(` -Object { - "allMarkdown": Object { - "group": Array [ - Object { - "edges": Array [ Object { - "node": Object { - "frontmatter": Object { - "date": "2019-01-01", - "title": "Markdown File 1", - }, - }, - }, - Object { - "node": Object { - "frontmatter": Object { - "date": null, - "title": "Markdown File 2", - }, - }, - }, - ], - "fieldValue": "Author 1", - }, - Object { - "edges": Array [ - Object { - "node": Object { - "frontmatter": Object { - "date": "2019-01-01", - "title": "Markdown File 1", - }, + "allMarkdown": Object { + "group": Array [ + Object { + "edges": Array [ + Object { + "node": Object { + "frontmatter": Object { + "date": "2019-01-01", + "title": "Markdown File 1", + }, + }, + }, + Object { + "node": Object { + "frontmatter": Object { + "date": null, + "title": "Markdown File 2", + }, + }, + }, + ], + "fieldValue": "Author 1", + }, + Object { + "edges": Array [ + Object { + "node": Object { + "frontmatter": Object { + "date": "2019-01-01", + "title": "Markdown File 1", + }, + }, + }, + ], + "fieldValue": "Author 2", + }, + ], }, - }, - ], - "fieldValue": "Author 2", - }, - ], - }, -} -`) + } + `) }) it(`handles groups added in inline fragment`, async () => { @@ -940,47 +940,47 @@ Object { const results = await runQuery(query) expect(results.errors).toBeUndefined() expect(results.data).toMatchInlineSnapshot(` -Object { - "allMarkdown": Object { - "group": Array [ - Object { - "edges": Array [ - Object { - "node": Object { - "frontmatter": Object { - "date": "2019-01-01", - "title": "Markdown File 1", - }, - }, - }, Object { - "node": Object { - "frontmatter": Object { - "date": null, - "title": "Markdown File 2", - }, - }, - }, - ], - "fieldValue": "Author 1", - }, - Object { - "edges": Array [ - Object { - "node": Object { - "frontmatter": Object { - "date": "2019-01-01", - "title": "Markdown File 1", - }, + "allMarkdown": Object { + "group": Array [ + Object { + "edges": Array [ + Object { + "node": Object { + "frontmatter": Object { + "date": "2019-01-01", + "title": "Markdown File 1", + }, + }, + }, + Object { + "node": Object { + "frontmatter": Object { + "date": null, + "title": "Markdown File 2", + }, + }, + }, + ], + "fieldValue": "Author 1", + }, + Object { + "edges": Array [ + Object { + "node": Object { + "frontmatter": Object { + "date": "2019-01-01", + "title": "Markdown File 1", + }, + }, + }, + ], + "fieldValue": "Author 2", + }, + ], }, - }, - ], - "fieldValue": "Author 2", - }, - ], - }, -} -`) + } + `) }) it(`handles groups added in nested fragment`, async () => { @@ -1012,47 +1012,47 @@ Object { const results = await runQuery(query) expect(results.errors).toBeUndefined() expect(results.data).toMatchInlineSnapshot(` -Object { - "allMarkdown": Object { - "group": Array [ - Object { - "edges": Array [ Object { - "node": Object { - "frontmatter": Object { - "date": "2019-01-01", - "title": "Markdown File 1", - }, - }, - }, - Object { - "node": Object { - "frontmatter": Object { - "date": null, - "title": "Markdown File 2", - }, - }, - }, - ], - "fieldValue": "Author 1", - }, - Object { - "edges": Array [ - Object { - "node": Object { - "frontmatter": Object { - "date": "2019-01-01", - "title": "Markdown File 1", - }, + "allMarkdown": Object { + "group": Array [ + Object { + "edges": Array [ + Object { + "node": Object { + "frontmatter": Object { + "date": "2019-01-01", + "title": "Markdown File 1", + }, + }, + }, + Object { + "node": Object { + "frontmatter": Object { + "date": null, + "title": "Markdown File 2", + }, + }, + }, + ], + "fieldValue": "Author 1", + }, + Object { + "edges": Array [ + Object { + "node": Object { + "frontmatter": Object { + "date": "2019-01-01", + "title": "Markdown File 1", + }, + }, + }, + ], + "fieldValue": "Author 2", + }, + ], }, - }, - ], - "fieldValue": "Author 2", - }, - ], - }, -} -`) + } + `) }) it(`groups null result`, async () => { @@ -1075,12 +1075,39 @@ Object { const results = await runQuery(query) expect(results.errors).toBeUndefined() expect(results.data).toMatchInlineSnapshot(` -Object { - "allMarkdown": Object { - "group": Array [], - }, -} -`) + Object { + "allMarkdown": Object { + "group": Array [], + }, + } + `) + }) + + it(`groups using reserved keywords`, async () => { + const query = ` + { + allMarkdown { + group(field: frontmatter___tags) { + field + fieldValue + } + } + } + ` + const results = await runQuery(query) + expect(results.errors).toBeUndefined() + expect(results.data).toMatchInlineSnapshot(` + Object { + "allMarkdown": Object { + "group": Array [ + Object { + "field": "frontmatter.tags", + "fieldValue": "constructor", + }, + ], + }, + } + `) }) }) @@ -1132,14 +1159,14 @@ Object { const results = await runQuery(query) expect(results.errors).toBeUndefined() expect(results.data).toMatchInlineSnapshot(` -Object { - "allMarkdown": Object { - "distinct": Array [ - "2019-01-01T00:00:00.000Z", - ], - }, -} -`) + Object { + "allMarkdown": Object { + "distinct": Array [ + "2019-01-01T00:00:00.000Z", + ], + }, + } + `) }) it(`handles null result`, async () => { @@ -1157,12 +1184,12 @@ Object { const results = await runQuery(query) expect(results.errors).toBeUndefined() expect(results.data).toMatchInlineSnapshot(` -Object { - "allMarkdown": Object { - "distinct": Array [], - }, -} -`) + Object { + "allMarkdown": Object { + "distinct": Array [], + }, + } + `) }) }) }) @@ -1249,25 +1276,25 @@ Object { const results = await runQuery(query) expect(results.errors).toBeUndefined() expect(results.data).toMatchInlineSnapshot(` -Object { - "allMarkdown": Object { - "nodes": Array [ - Object { - "frontmatter": Object { - "reviewer": Object { - "name": "Author 2", + Object { + "allMarkdown": Object { + "nodes": Array [ + Object { + "frontmatter": Object { + "reviewer": Object { + "name": "Author 2", + }, + }, + }, + Object { + "frontmatter": Object { + "reviewer": null, + }, + }, + ], }, - }, - }, - Object { - "frontmatter": Object { - "reviewer": null, - }, - }, - ], - }, -} -`) + } + `) }) it(`with defined field mappings`, async () => { @@ -1287,25 +1314,25 @@ Object { const results = await runQuery(query) expect(results.errors).toBeUndefined() expect(results.data).toMatchInlineSnapshot(` -Object { - "allMarkdown": Object { - "nodes": Array [ - Object { - "frontmatter": Object { - "reviewerByEmail": Object { - "name": "Author 2", + Object { + "allMarkdown": Object { + "nodes": Array [ + Object { + "frontmatter": Object { + "reviewerByEmail": Object { + "name": "Author 2", + }, + }, + }, + Object { + "frontmatter": Object { + "reviewerByEmail": null, + }, + }, + ], }, - }, - }, - Object { - "frontmatter": Object { - "reviewerByEmail": null, - }, - }, - ], - }, -} -`) + } + `) }) }) diff --git a/packages/gatsby/src/schema/resolvers.js b/packages/gatsby/src/schema/resolvers.js index 81faf07bc7c0c..6ec6ca3b60c96 100644 --- a/packages/gatsby/src/schema/resolvers.js +++ b/packages/gatsby/src/schema/resolvers.js @@ -74,7 +74,7 @@ const group = (source, args, context, info) => { acc[key] = (acc[key] || []).concat(node) }) return acc - }, {}) + }, Object.create(null)) return Object.keys(groupedResults) .sort() .reduce((acc, fieldValue) => { From d843d0c8cba35cdcf86b113a66829356d220b7fb Mon Sep 17 00:00:00 2001 From: Vladimir Razuvaev Date: Thu, 2 Apr 2020 00:22:43 +0700 Subject: [PATCH 2/2] Add comment to clarify the fix --- packages/gatsby/src/schema/resolvers.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/gatsby/src/schema/resolvers.js b/packages/gatsby/src/schema/resolvers.js index 6ec6ca3b60c96..b0cdf234a7ac2 100644 --- a/packages/gatsby/src/schema/resolvers.js +++ b/packages/gatsby/src/schema/resolvers.js @@ -74,7 +74,11 @@ const group = (source, args, context, info) => { acc[key] = (acc[key] || []).concat(node) }) return acc + // Note: using Object.create on purpose: + // object key may be arbitrary string including reserved words (i.e. `constructor`) + // see: https://github.com/gatsbyjs/gatsby/issues/22508 }, Object.create(null)) + return Object.keys(groupedResults) .sort() .reduce((acc, fieldValue) => {