diff --git a/packages/gatsby-source-contentful/src/__tests__/data.json b/packages/gatsby-source-contentful/src/__tests__/data.json index b7d048db85054..23c0cb3178939 100644 --- a/packages/gatsby-source-contentful/src/__tests__/data.json +++ b/packages/gatsby-source-contentful/src/__tests__/data.json @@ -44,47 +44,9 @@ "tags": [] }, "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "6m5AJ9vMPKc8OUoQeoCS4o", - "type": "Asset", - "createdAt": "2017-06-27T09:35:36.172Z", - "updatedAt": "2017-06-27T09:35:36.172Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 1 - }, - "fields": { - "title": { - "en-US": "Home and Kitchen" - }, - "description": { - "en-US": "category icon" - }, - "file": { - "en-US": { - "url": "//images.ctfassets.net/rocybtov1ozk/6m5AJ9vMPKc8OUoQeoCS4o/e782e3b291ff2b0287546a563af4683c/1418244847_Streamline-18-256.png", - "details": { - "size": 2977, - "image": { - "width": 256, - "height": 256 - } - }, - "fileName": "1418244847_Streamline-18-256.png", - "contentType": "image/png" - } - } + "type": "Link", + "linkType": "Asset", + "id": "6m5AJ9vMPKc8OUoQeoCS4o" } } }, @@ -298,47 +260,9 @@ "tags": [] }, "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "wtrHxeu3zEoEce2MokCSi", - "type": "Asset", - "createdAt": "2017-06-27T09:35:36.037Z", - "updatedAt": "2017-06-27T09:35:36.037Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 1 - }, - "fields": { - "title": { - "en-US": "Playsam Streamliner" - }, - "description": { - "en-US": "Merchandise photo" - }, - "file": { - "en-US": { - "url": "//images.ctfassets.net/rocybtov1ozk/wtrHxeu3zEoEce2MokCSi/73dce36715f16e27cf5ff0d2d97d7dff/quwowooybuqbl6ntboz3.jpg", - "details": { - "size": 27187, - "image": { - "width": 600, - "height": 446 - } - }, - "fileName": "quwowooybuqbl6ntboz3.jpg", - "contentType": "image/jpeg" - } - } + "type": "Link", + "linkType": "Asset", + "id": "wtrHxeu3zEoEce2MokCSi" } } ] @@ -353,92 +277,9 @@ "tags": [] }, "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "24DPGBDeGEaYy8ms4Y8QMQ", - "type": "Entry", - "createdAt": "2017-06-27T09:35:44.992Z", - "updatedAt": "2017-06-27T09:46:43.477Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 2, - "contentType": { - "sys": { - "type": "Link", - "linkType": "ContentType", - "id": "6XwpTaSiiI2Ak2Ww0oi6qa" - } - } - }, - "fields": { - "title": { - "de": "Spielzeug", - "en-US": "Toys" - }, - "icon": { - "en-US": { - "metadata": { - "tags": [] - }, - "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "6t4HKjytPi0mYgs240wkG", - "type": "Asset", - "createdAt": "2017-06-27T09:35:36.633Z", - "updatedAt": "2017-06-27T09:35:36.633Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 1 - }, - "fields": { - "title": { - "en-US": "Toys" - }, - "description": { - "en-US": "Category icon set" - }, - "file": { - "en-US": { - "url": "//images.ctfassets.net/rocybtov1ozk/6t4HKjytPi0mYgs240wkG/6e730b1e6c2a46929239019240c037e6/toys_512pxGREY.png", - "details": { - "size": 6744, - "image": { - "width": 128, - "height": 128 - } - }, - "fileName": "toys_512pxGREY.png", - "contentType": "image/png" - } - } - } - } - }, - "categoryDescription": { - "de": "Spielzeugladen, Spiele, Lernhilfen", - "en-US": "Shop for toys, games, educational aids" - } + "type": "Link", + "linkType": "Entry", + "id": "24DPGBDeGEaYy8ms4Y8QMQ" } } ] @@ -453,94 +294,9 @@ "tags": [] }, "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "JrePkDVYomE8AwcuCUyMi", - "type": "Entry", - "createdAt": "2017-06-27T09:35:44.988Z", - "updatedAt": "2017-06-27T09:50:36.937Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 2, - "contentType": { - "sys": { - "type": "Link", - "linkType": "ContentType", - "id": "sFzTZbSuM8coEwygeUYes" - } - } - }, - "fields": { - "companyName": { - "en-US": "Playsam" - }, - "logo": { - "en-US": { - "metadata": { - "tags": [] - }, - "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "4zj1ZOfHgQ8oqgaSKm4Qo2", - "type": "Asset", - "createdAt": "2017-06-27T09:35:36.168Z", - "updatedAt": "2017-06-27T09:35:36.168Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 1 - }, - "fields": { - "title": { - "en-US": "Playsam" - }, - "description": { - "en-US": "Brand logo" - }, - "file": { - "en-US": { - "url": "//images.ctfassets.net/rocybtov1ozk/4zj1ZOfHgQ8oqgaSKm4Qo2/5d967c9c48d67eabff71a9a0232d4378/playsam.jpg", - "details": { - "size": 7003, - "image": { - "width": 100, - "height": 100 - } - }, - "fileName": "playsam.jpg", - "contentType": "image/jpeg" - } - } - } - } - }, - "companyDescription": { - "de": "Playsam ist die führende skandinavische Designfirma für Executive Holzspielzeug Geschenk. Skandinavisches Design spielerische Kreativität, Integrität und Raffinesse sind Playsam. Skandinavisches Design und hölzernes Spielzeug macht Playsam Geschenk schön in die Welt des Designs seit 1984.", - "en-US": "Playsam is the leading Scandinavian design company for executive wooden toy gift. Scandinavian design playful creativity, integrity and sophistication are Playsam. Scandinavian design and wooden toy makes Playsam gift lovely to the world of design since 1984." - }, - "website": { - "en-US": "http://playsam.com/" - } + "type": "Link", + "linkType": "Entry", + "id": "JrePkDVYomE8AwcuCUyMi" } } }, @@ -597,47 +353,9 @@ "tags": [] }, "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "3wtvPBbBjiMKqKKga8I2Cu", - "type": "Asset", - "createdAt": "2017-06-27T09:35:37.178Z", - "updatedAt": "2017-06-27T09:35:37.178Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 1 - }, - "fields": { - "title": { - "en-US": "Normann Copenhagen" - }, - "description": { - "en-US": "Brand logo" - }, - "file": { - "en-US": { - "url": "//images.ctfassets.net/rocybtov1ozk/3wtvPBbBjiMKqKKga8I2Cu/c65cb9cce1107c2e7e63c17072fe7932/zJYzDlGk.jpeg", - "details": { - "size": 12302, - "image": { - "width": 353, - "height": 353 - } - }, - "fileName": "zJYzDlGk.jpeg", - "contentType": "image/jpeg" - } - } + "type": "Link", + "linkType": "Asset", + "id": "3wtvPBbBjiMKqKKga8I2Cu" } } }, @@ -713,47 +431,9 @@ "tags": [] }, "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "Xc0ny7GWsMEMCeASWO2um", - "type": "Asset", - "createdAt": "2017-06-27T09:35:37.027Z", - "updatedAt": "2017-06-27T09:35:37.027Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 1 - }, - "fields": { - "title": { - "en-US": "Hudson Wall Cup " - }, - "description": { - "en-US": "Merchandise image" - }, - "file": { - "en-US": { - "url": "//images.ctfassets.net/rocybtov1ozk/Xc0ny7GWsMEMCeASWO2um/af8e29320c04af689798afe96e2345c7/jqvtazcyfwseah9fmysz.jpg", - "details": { - "size": 48751, - "image": { - "width": 600, - "height": 600 - } - }, - "fileName": "jqvtazcyfwseah9fmysz.jpg", - "contentType": "image/jpeg" - } - } + "type": "Link", + "linkType": "Asset", + "id": "Xc0ny7GWsMEMCeASWO2um" } } ] @@ -768,92 +448,9 @@ "tags": [] }, "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "7LAnCobuuWYSqks6wAwY2a", - "type": "Entry", - "createdAt": "2017-06-27T09:35:44.000Z", - "updatedAt": "2020-06-30T11:22:54.201Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 4, - "contentType": { - "sys": { - "type": "Link", - "linkType": "ContentType", - "id": "6XwpTaSiiI2Ak2Ww0oi6qa" - } - } - }, - "fields": { - "title": { - "de": "Haus & Küche", - "en-US": "Home & Kitchen" - }, - "icon": { - "en-US": { - "metadata": { - "tags": [] - }, - "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "6m5AJ9vMPKc8OUoQeoCS4o", - "type": "Asset", - "createdAt": "2017-06-27T09:35:36.172Z", - "updatedAt": "2017-06-27T09:35:36.172Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 1 - }, - "fields": { - "title": { - "en-US": "Home and Kitchen" - }, - "description": { - "en-US": "category icon" - }, - "file": { - "en-US": { - "url": "//images.ctfassets.net/rocybtov1ozk/6m5AJ9vMPKc8OUoQeoCS4o/e782e3b291ff2b0287546a563af4683c/1418244847_Streamline-18-256.png", - "details": { - "size": 2977, - "image": { - "width": 256, - "height": 256 - } - }, - "fileName": "1418244847_Streamline-18-256.png", - "contentType": "image/png" - } - } - } - } - }, - "categoryDescription": { - "de": "Shop für Möbel, Bettwäsche, Bad, Staubsauger, Küchenprodukte und vieles mehr", - "en-US": "Shop for furniture, bedding, bath, vacuums, kitchen products, and more" - } + "type": "Link", + "linkType": "Entry", + "id": "7LAnCobuuWYSqks6wAwY2a" } } ] @@ -868,103 +465,9 @@ "tags": [] }, "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "651CQ8rLoIYCeY6G0QG22q", - "type": "Entry", - "createdAt": "2017-06-27T09:35:43.997Z", - "updatedAt": "2017-06-27T09:55:16.820Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 2, - "contentType": { - "sys": { - "type": "Link", - "linkType": "ContentType", - "id": "sFzTZbSuM8coEwygeUYes" - } - } - }, - "fields": { - "companyName": { - "en-US": "Normann Copenhagen" - }, - "logo": { - "en-US": { - "metadata": { - "tags": [] - }, - "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "3wtvPBbBjiMKqKKga8I2Cu", - "type": "Asset", - "createdAt": "2017-06-27T09:35:37.178Z", - "updatedAt": "2017-06-27T09:35:37.178Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 1 - }, - "fields": { - "title": { - "en-US": "Normann Copenhagen" - }, - "description": { - "en-US": "Brand logo" - }, - "file": { - "en-US": { - "url": "//images.ctfassets.net/rocybtov1ozk/3wtvPBbBjiMKqKKga8I2Cu/c65cb9cce1107c2e7e63c17072fe7932/zJYzDlGk.jpeg", - "details": { - "size": 12302, - "image": { - "width": 353, - "height": 353 - } - }, - "fileName": "zJYzDlGk.jpeg", - "contentType": "image/jpeg" - } - } - } - } - }, - "companyDescription": { - "de": "Normann Kopenhagen ist eine Art zu leben - eine Denkweise. Wir lieben es, die konventionellen Designregeln herauszufordern. Aus diesem Grund finden Sie traditionelle Materialien, die in untraditionelle Verwendung wie ein Steinhaken aus isländischen Steinen, eine Vase aus Silizium und last but not least ein Hund aus Kunststoff.", - "en-US": "Normann Copenhagen is a way of living - a mindset. We love to challenge the conventional design rules. This is why you will find traditional materials put into untraditional use such as a Stone Hook made of Icelandic stones, a vase made out of silicon and last but not least a dog made out of plastic." - }, - "website": { - "en-US": "http://www.normann-copenhagen.com/" - }, - "twitter": { - "en-US": "https://twitter.com/NormannCPH" - }, - "email": { - "en-US": "normann@normann-copenhagen.com" - }, - "phone": { - "en-US": ["+45 35 55 44 59"] - } + "type": "Link", + "linkType": "Entry", + "id": "651CQ8rLoIYCeY6G0QG22q" } } }, @@ -1033,47 +536,9 @@ "tags": [] }, "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "10TkaLheGeQG6qQGqWYqUI", - "type": "Asset", - "createdAt": "2017-06-27T09:35:36.032Z", - "updatedAt": "2017-06-27T09:35:36.032Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 1 - }, - "fields": { - "title": { - "en-US": "Whisk beaters" - }, - "description": { - "en-US": "Merchandise photo" - }, - "file": { - "en-US": { - "url": "//images.ctfassets.net/rocybtov1ozk/10TkaLheGeQG6qQGqWYqUI/f997e8e13c8c83c145e976d0905e64b7/ryugj83mqwa1asojwtwb.jpg", - "details": { - "size": 28435, - "image": { - "width": 450, - "height": 600 - } - }, - "fileName": "ryugj83mqwa1asojwtwb.jpg", - "contentType": "image/jpeg" - } - } + "type": "Link", + "linkType": "Asset", + "id": "10TkaLheGeQG6qQGqWYqUI" } } ] @@ -1094,92 +559,9 @@ "tags": [] }, "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "7LAnCobuuWYSqks6wAwY2a", - "type": "Entry", - "createdAt": "2017-06-27T09:35:44.000Z", - "updatedAt": "2020-06-30T11:22:54.201Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 4, - "contentType": { - "sys": { - "type": "Link", - "linkType": "ContentType", - "id": "6XwpTaSiiI2Ak2Ww0oi6qa" - } - } - }, - "fields": { - "title": { - "de": "Haus & Küche", - "en-US": "Home & Kitchen" - }, - "icon": { - "en-US": { - "metadata": { - "tags": [] - }, - "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "6m5AJ9vMPKc8OUoQeoCS4o", - "type": "Asset", - "createdAt": "2017-06-27T09:35:36.172Z", - "updatedAt": "2017-06-27T09:35:36.172Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 1 - }, - "fields": { - "title": { - "en-US": "Home and Kitchen" - }, - "description": { - "en-US": "category icon" - }, - "file": { - "en-US": { - "url": "//images.ctfassets.net/rocybtov1ozk/6m5AJ9vMPKc8OUoQeoCS4o/e782e3b291ff2b0287546a563af4683c/1418244847_Streamline-18-256.png", - "details": { - "size": 2977, - "image": { - "width": 256, - "height": 256 - } - }, - "fileName": "1418244847_Streamline-18-256.png", - "contentType": "image/png" - } - } - } - } - }, - "categoryDescription": { - "de": "Shop für Möbel, Bettwäsche, Bad, Staubsauger, Küchenprodukte und vieles mehr", - "en-US": "Shop for furniture, bedding, bath, vacuums, kitchen products, and more" - } + "type": "Link", + "linkType": "Entry", + "id": "7LAnCobuuWYSqks6wAwY2a" } } ] @@ -1194,103 +576,9 @@ "tags": [] }, "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "651CQ8rLoIYCeY6G0QG22q", - "type": "Entry", - "createdAt": "2017-06-27T09:35:43.997Z", - "updatedAt": "2017-06-27T09:55:16.820Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 2, - "contentType": { - "sys": { - "type": "Link", - "linkType": "ContentType", - "id": "sFzTZbSuM8coEwygeUYes" - } - } - }, - "fields": { - "companyName": { - "en-US": "Normann Copenhagen" - }, - "logo": { - "en-US": { - "metadata": { - "tags": [] - }, - "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "3wtvPBbBjiMKqKKga8I2Cu", - "type": "Asset", - "createdAt": "2017-06-27T09:35:37.178Z", - "updatedAt": "2017-06-27T09:35:37.178Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 1 - }, - "fields": { - "title": { - "en-US": "Normann Copenhagen" - }, - "description": { - "en-US": "Brand logo" - }, - "file": { - "en-US": { - "url": "//images.ctfassets.net/rocybtov1ozk/3wtvPBbBjiMKqKKga8I2Cu/c65cb9cce1107c2e7e63c17072fe7932/zJYzDlGk.jpeg", - "details": { - "size": 12302, - "image": { - "width": 353, - "height": 353 - } - }, - "fileName": "zJYzDlGk.jpeg", - "contentType": "image/jpeg" - } - } - } - } - }, - "companyDescription": { - "de": "Normann Kopenhagen ist eine Art zu leben - eine Denkweise. Wir lieben es, die konventionellen Designregeln herauszufordern. Aus diesem Grund finden Sie traditionelle Materialien, die in untraditionelle Verwendung wie ein Steinhaken aus isländischen Steinen, eine Vase aus Silizium und last but not least ein Hund aus Kunststoff.", - "en-US": "Normann Copenhagen is a way of living - a mindset. We love to challenge the conventional design rules. This is why you will find traditional materials put into untraditional use such as a Stone Hook made of Icelandic stones, a vase made out of silicon and last but not least a dog made out of plastic." - }, - "website": { - "en-US": "http://www.normann-copenhagen.com/" - }, - "twitter": { - "en-US": "https://twitter.com/NormannCPH" - }, - "email": { - "en-US": "normann@normann-copenhagen.com" - }, - "phone": { - "en-US": ["+45 35 55 44 59"] - } + "type": "Link", + "linkType": "Entry", + "id": "651CQ8rLoIYCeY6G0QG22q" } } }, @@ -1359,47 +647,9 @@ "tags": [] }, "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "KTRF62Q4gg60q6WCsWKw8", - "type": "Asset", - "createdAt": "2017-06-27T09:35:37.064Z", - "updatedAt": "2017-06-27T09:35:37.064Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 1 - }, - "fields": { - "title": { - "en-US": "SoSo Wall Clock" - }, - "description": { - "en-US": "by Lemnos" - }, - "file": { - "en-US": { - "url": "//images.ctfassets.net/rocybtov1ozk/KTRF62Q4gg60q6WCsWKw8/a8b2e93ac83fbbbb7bf9fba9f92b018e/soso.clock.jpg", - "details": { - "size": 66927, - "image": { - "width": 1000, - "height": 1000 - } - }, - "fileName": "soso.clock.jpg", - "contentType": "image/jpeg" - } - } + "type": "Link", + "linkType": "Asset", + "id": "KTRF62Q4gg60q6WCsWKw8" } } ] @@ -1420,92 +670,9 @@ "tags": [] }, "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "7LAnCobuuWYSqks6wAwY2a", - "type": "Entry", - "createdAt": "2017-06-27T09:35:44.000Z", - "updatedAt": "2020-06-30T11:22:54.201Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 4, - "contentType": { - "sys": { - "type": "Link", - "linkType": "ContentType", - "id": "6XwpTaSiiI2Ak2Ww0oi6qa" - } - } - }, - "fields": { - "title": { - "de": "Haus & Küche", - "en-US": "Home & Kitchen" - }, - "icon": { - "en-US": { - "metadata": { - "tags": [] - }, - "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "6m5AJ9vMPKc8OUoQeoCS4o", - "type": "Asset", - "createdAt": "2017-06-27T09:35:36.172Z", - "updatedAt": "2017-06-27T09:35:36.172Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 1 - }, - "fields": { - "title": { - "en-US": "Home and Kitchen" - }, - "description": { - "en-US": "category icon" - }, - "file": { - "en-US": { - "url": "//images.ctfassets.net/rocybtov1ozk/6m5AJ9vMPKc8OUoQeoCS4o/e782e3b291ff2b0287546a563af4683c/1418244847_Streamline-18-256.png", - "details": { - "size": 2977, - "image": { - "width": 256, - "height": 256 - } - }, - "fileName": "1418244847_Streamline-18-256.png", - "contentType": "image/png" - } - } - } - } - }, - "categoryDescription": { - "de": "Shop für Möbel, Bettwäsche, Bad, Staubsauger, Küchenprodukte und vieles mehr", - "en-US": "Shop for furniture, bedding, bath, vacuums, kitchen products, and more" - } + "type": "Link", + "linkType": "Entry", + "id": "7LAnCobuuWYSqks6wAwY2a" } } ] @@ -1520,100 +687,9 @@ "tags": [] }, "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "4LgMotpNF6W20YKmuemW0a", - "type": "Entry", - "createdAt": "2017-06-27T09:35:44.396Z", - "updatedAt": "2017-06-27T09:51:15.647Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 2, - "contentType": { - "sys": { - "type": "Link", - "linkType": "ContentType", - "id": "sFzTZbSuM8coEwygeUYes" - } - } - }, - "fields": { - "companyName": { - "en-US": "Lemnos" - }, - "logo": { - "en-US": { - "metadata": { - "tags": [] - }, - "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "2Y8LhXLnYAYqKCGEWG4EKI", - "type": "Asset", - "createdAt": "2017-06-27T09:35:37.012Z", - "updatedAt": "2017-06-27T09:35:37.012Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 1 - }, - "fields": { - "title": { - "en-US": "Lemnos" - }, - "description": { - "en-US": "company logo" - }, - "file": { - "en-US": { - "url": "//images.ctfassets.net/rocybtov1ozk/2Y8LhXLnYAYqKCGEWG4EKI/eb29ab3c817906993f65e221523ef252/lemnos-logo.jpg", - "details": { - "size": 7149, - "image": { - "width": 175, - "height": 32 - } - }, - "fileName": "lemnos-logo.jpg", - "contentType": "image/jpeg" - } - } - } - } - }, - "companyDescription": { - "de": "TAKATA Lemnos Inc. wurde im Jahre 1947 als Messing-Casting-Fertigungsindustrie in Takaoka-Stadt, Toyama Prefecture, Japan gegründet und wir starteten seit 1966 mit der Seiko Clock Co., Ltd.\n\nWir haben die Entwicklung für die ursprüngliche Planung ab Ende 1980 eingegangen und \"Lemnos Brand\" wurde als globale Designuhr von einem Meisterwerk \"HOLA\" von Kazuo KAWASAKI entworfen, das 1989 erschien.\n\nDanach machten wir viele Projekte mit namhaften Designern, die in Japan und Übersee tätig waren, wie zB Riki WATANABE, Kazuo KAWASAKI, Shin AZUMI, Tomoko AZUMI, Kanae TSUKAMOTO etc. und wir kündigten ihre Werke in der Kunst an Und prominenten Designs. Darüber hinaus haben wir durch die Zusammenarbeit mit Andrea Branzi, einem bekannten Architekten der Welt, ein besonderes Projekt gemacht.\n\nLemnos Markenprodukte werden nun von den Designläden und den Innenhandelsgeschäften auf der ganzen Welt hoch gelobt.\n\nIn den vergangenen Jahren haben wir auch eine hohe Priorität für die Entwicklung von Innenausstattung, die den traditionellen Techniken des Gründungsherstellers voll ausnutzt, und wir konzentrieren uns immer auf die Entwicklung der neuen Lemnos-Produkte im neuen Markt.\n\nUnsere Lemnos Produkte werden sorgfältig von unseren Handwerkern fein geschliffen geschickten Techniken in Japan gemacht. Sie bringen sicherlich die Attraktivität der Materialien auf das Maximum und schaffen feine Produkte nicht beeinflusst auf die Mode-Trend entsprechend. TAKATA Lemnos Inc. möchte definitiv innovativ sein und ständig vorschlagen, die Schönheit dauert ewig.", - "en-US": "TAKATA Lemnos Inc. was founded in 1947 as a brass casting manufacturing industry in Takaoka-city, Toyama Prefecture, Japan and we launched out into the full-scale business trade with Seiko Clock Co., Ltd. since 1966.\n\nWe entered into the development for the original planning from late 1980 and \"Lemnos Brand\" recognized as the global design clock by a masterpiece \"HOLA\" designed by Kazuo KAWASAKI which released in 1989.\n\nAfterwards, we made a lot of projects with well-known designers who took in active in Japan and overseas such as Riki WATANABE, Kazuo KAWASAKI, Shin AZUMI, Tomoko AZUMI, Kanae TSUKAMOTO etc. and we made announcement of their fine works abounding in artistry and prominent designs. In addition, we realized to make a special project by the collaboration with Andrea Branzi, a well-known architect in the world.\n\nLemnos brand products are now highly praised from the design shops and the interior shops all over the world.\n\nIn recent years, we also have been given high priority to develop interior accessories making full use of our traditional techniques by the founding manufacturer and we always focus our minds on the development for the new Lemnos products in the new market.\n\nOur Lemnos products are made carefully by our craftsmen finely honed skillful techniques in Japan. They surely bring out the attractiveness of the materials to the maximum and create fine products not being influenced on the fashion trend accordingly. TAKATA Lemnos Inc. definitely would like to be innovative and continuously propose the beauty lasts forever." - }, - "website": { - "en-US": "http://www.lemnos.jp/en/" - }, - "email": { - "en-US": "info@acgears.com" - }, - "phone": { - "en-US": ["+1 212 260 2269"] - } + "type": "Link", + "linkType": "Entry", + "id": "4LgMotpNF6W20YKmuemW0a" } } }, @@ -1670,47 +746,9 @@ "tags": [] }, "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "2Y8LhXLnYAYqKCGEWG4EKI", - "type": "Asset", - "createdAt": "2017-06-27T09:35:37.012Z", - "updatedAt": "2017-06-27T09:35:37.012Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 1 - }, - "fields": { - "title": { - "en-US": "Lemnos" - }, - "description": { - "en-US": "company logo" - }, - "file": { - "en-US": { - "url": "//images.ctfassets.net/rocybtov1ozk/2Y8LhXLnYAYqKCGEWG4EKI/eb29ab3c817906993f65e221523ef252/lemnos-logo.jpg", - "details": { - "size": 7149, - "image": { - "width": 175, - "height": 32 - } - }, - "fileName": "lemnos-logo.jpg", - "contentType": "image/jpeg" - } - } + "type": "Link", + "linkType": "Asset", + "id": "2Y8LhXLnYAYqKCGEWG4EKI" } } }, @@ -1771,47 +809,9 @@ "tags": [] }, "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "4zj1ZOfHgQ8oqgaSKm4Qo2", - "type": "Asset", - "createdAt": "2017-06-27T09:35:36.168Z", - "updatedAt": "2017-06-27T09:35:36.168Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 1 - }, - "fields": { - "title": { - "en-US": "Playsam" - }, - "description": { - "en-US": "Brand logo" - }, - "file": { - "en-US": { - "url": "//images.ctfassets.net/rocybtov1ozk/4zj1ZOfHgQ8oqgaSKm4Qo2/5d967c9c48d67eabff71a9a0232d4378/playsam.jpg", - "details": { - "size": 7003, - "image": { - "width": 100, - "height": 100 - } - }, - "fileName": "playsam.jpg", - "contentType": "image/jpeg" - } - } + "type": "Link", + "linkType": "Asset", + "id": "4zj1ZOfHgQ8oqgaSKm4Qo2" } } }, @@ -1867,47 +867,9 @@ "tags": [] }, "sys": { - "space": { - "sys": { - "type": "Link", - "linkType": "Space", - "id": "rocybtov1ozk" - } - }, - "id": "6t4HKjytPi0mYgs240wkG", - "type": "Asset", - "createdAt": "2017-06-27T09:35:36.633Z", - "updatedAt": "2017-06-27T09:35:36.633Z", - "environment": { - "sys": { - "id": "master", - "type": "Link", - "linkType": "Environment" - } - }, - "revision": 1 - }, - "fields": { - "title": { - "en-US": "Toys" - }, - "description": { - "en-US": "Category icon set" - }, - "file": { - "en-US": { - "url": "//images.ctfassets.net/rocybtov1ozk/6t4HKjytPi0mYgs240wkG/6e730b1e6c2a46929239019240c037e6/toys_512pxGREY.png", - "details": { - "size": 6744, - "image": { - "width": 128, - "height": 128 - } - }, - "fileName": "toys_512pxGREY.png", - "contentType": "image/png" - } - } + "type": "Link", + "linkType": "Asset", + "id": "6t4HKjytPi0mYgs240wkG" } } }, diff --git a/packages/gatsby-source-contentful/src/__tests__/gatsby-node.js b/packages/gatsby-source-contentful/src/__tests__/gatsby-node.js index 675e6337adda6..7c3d49fba53f7 100644 --- a/packages/gatsby-source-contentful/src/__tests__/gatsby-node.js +++ b/packages/gatsby-source-contentful/src/__tests__/gatsby-node.js @@ -52,11 +52,10 @@ describe(`gatsby-node`, () => { }, } const parentSpan = {} - const createNodeId = jest.fn(value => value) + const createNodeId = jest.fn(id => id) let currentNodeMap const getNodes = () => Array.from(currentNodeMap.values()) const getNode = id => currentNodeMap.get(id) - const getNodesByType = jest.fn() const getFieldValue = (value, locale, defaultLocale) => value[locale] ?? value[defaultLocale] @@ -74,7 +73,6 @@ describe(`gatsby-node`, () => { actions, getNode, getNodes, - getNodesByType, createNodeId, store, cache, @@ -88,8 +86,9 @@ describe(`gatsby-node`, () => { const testIfContentTypesExists = contentTypeItems => { contentTypeItems.forEach(contentType => { - const contentTypeId = createNodeId(contentType.name) - expect(getNode(contentTypeId)).toMatchObject({ + expect( + getNode(createNodeId(contentType.name.toLowerCase())) + ).toMatchObject({ name: contentType.name, displayField: contentType.displayField, description: contentType.description, @@ -385,13 +384,15 @@ describe(`gatsby-node`, () => { const createdBlogEntry = startersBlogFixture.createBlogPost().currentSyncData.entries[0] const createdBlogEntryIds = locales.map(locale => - makeId({ - spaceId: createdBlogEntry.sys.space.sys.id, - currentLocale: locale, - defaultLocale: locales[0], - id: createdBlogEntry.sys.id, - type: createdBlogEntry.sys.type, - }) + createNodeId( + makeId({ + spaceId: createdBlogEntry.sys.space.sys.id, + currentLocale: locale, + defaultLocale: locales[0], + id: createdBlogEntry.sys.id, + type: createdBlogEntry.sys.type, + }) + ) ) // initial sync @@ -433,13 +434,15 @@ describe(`gatsby-node`, () => { const updatedBlogEntry = startersBlogFixture.updateBlogPost().currentSyncData.entries[0] const updatedBlogEntryIds = locales.map(locale => - makeId({ - spaceId: updatedBlogEntry.sys.space.sys.id, - currentLocale: locale, - defaultLocale: locales[0], - id: updatedBlogEntry.sys.id, - type: updatedBlogEntry.sys.type, - }) + createNodeId( + makeId({ + spaceId: updatedBlogEntry.sys.space.sys.id, + currentLocale: locale, + defaultLocale: locales[0], + id: updatedBlogEntry.sys.id, + type: updatedBlogEntry.sys.type, + }) + ) ) // initial sync @@ -487,13 +490,15 @@ describe(`gatsby-node`, () => { ? removedBlogEntry.sys.type.substring(`Deleted`.length) : removedBlogEntry.sys.type const removedBlogEntryIds = locales.map(locale => - makeId({ - spaceId: removedBlogEntry.sys.space.sys.id, - currentLocale: locale, - defaultLocale: locales[0], - id: removedBlogEntry.sys.id, - type: normalizedType, - }) + createNodeId( + makeId({ + spaceId: removedBlogEntry.sys.space.sys.id, + currentLocale: locale, + defaultLocale: locales[0], + id: removedBlogEntry.sys.id, + type: normalizedType, + }) + ) ) // initial sync @@ -551,13 +556,15 @@ describe(`gatsby-node`, () => { const removedAssetEntry = startersBlogFixture.createBlogPost().currentSyncData.entries[0] const removedAssetEntryIds = locales.map(locale => - makeId({ - spaceId: removedAssetEntry.sys.space.sys.id, - currentLocale: locale, - defaultLocale: locales[0], - id: removedAssetEntry.sys.id, - type: removedAssetEntry.sys.type, - }) + createNodeId( + makeId({ + spaceId: removedAssetEntry.sys.space.sys.id, + currentLocale: locale, + defaultLocale: locales[0], + id: removedAssetEntry.sys.id, + type: removedAssetEntry.sys.type, + }) + ) ) // initial sync diff --git a/packages/gatsby-source-contentful/src/__tests__/normalize.js b/packages/gatsby-source-contentful/src/__tests__/normalize.js index 5dc507141e3c4..be98eb1c88744 100644 --- a/packages/gatsby-source-contentful/src/__tests__/normalize.js +++ b/packages/gatsby-source-contentful/src/__tests__/normalize.js @@ -1,13 +1,14 @@ // @ts-check import { - buildEntryList, - buildResolvableSet, buildForeignReferenceMap, - createNodesForContentType, + createNodes, + createContentTypeNodes, + buildResolvableSet, createAssetNodes, buildFallbackChain, getLocalizedField, makeId, + buildContentTypeMap, } from "../normalize" import { createPluginConfig } from "../plugin-options" @@ -49,39 +50,12 @@ function countCreatedNodeTypesFromMock(mock) { } describe(`generic`, () => { - it(`builds entry list`, () => { - const entryList = buildEntryList({ - mergedSyncData: currentSyncData, - contentTypeItems, - }) - - expect(entryList).toHaveLength(contentTypeItems.length) - - expect(entryList[0][0].sys.contentType.sys.id).toBe( - `6XwpTaSiiI2Ak2Ww0oi6qa` - ) - expect(entryList[0]).toHaveLength(2) - expect(entryList[1][0].sys.contentType.sys.id).toBe(`sFzTZbSuM8coEwygeUYes`) - expect(entryList[1]).toHaveLength(3) - expect(entryList[2][0].sys.contentType.sys.id).toBe( - `2PqfXUJwE8qSYKuM0U6w8M` - ) - expect(entryList[2]).toHaveLength(4) - expect(entryList[3][0].sys.contentType.sys.id).toBe(`jsonTest`) - expect(entryList[3]).toHaveLength(1) - - expect(entryList[4][0].sys.contentType.sys.id).toBe(`remarkTest`) - }) - it(`builds list of resolvable data`, () => { - const entryList = buildEntryList({ - mergedSyncData: currentSyncData, - contentTypeItems, - }) + const { entries } = currentSyncData const resolvable = buildResolvableSet({ assets: currentSyncData.assets, - entryList, + entries, }) const allNodes = [...currentSyncData.entries, ...currentSyncData.assets] @@ -91,23 +65,26 @@ describe(`generic`, () => { ) }) it(`builds foreignReferenceMap`, () => { - const entryList = buildEntryList({ - mergedSyncData: currentSyncData, - contentTypeItems, - }) + const { entries } = currentSyncData const resolvable = buildResolvableSet({ assets: currentSyncData.assets, - entryList, + entries, }) - const foreignReferenceMap = buildForeignReferenceMap({ + const contentTypeMap = buildContentTypeMap({ contentTypeItems, - entryList, + restrictedNodeFields, + conflictFieldPrefix, + useNameForId: true, + }) + + const foreignReferenceMap = buildForeignReferenceMap({ + contentTypeMap, + entries, resolvable, defaultLocale, space, - useNameForId: true, }) const referenceKeys = Object.keys(foreignReferenceMap) const expectedReferenceKeys = [ @@ -145,24 +122,40 @@ describe(`generic`, () => { }) describe(`Process contentful data (by name)`, () => { - it(`builds foreignReferenceMap`, () => { - const entryList = buildEntryList({ - mergedSyncData: currentSyncData, + it(`builds contentTypeMap`, () => { + const contentTypeMap = buildContentTypeMap({ contentTypeItems, + restrictedNodeFields, + conflictFieldPrefix, + useNameForId: true, }) + contentTypeItems.forEach(contentType => { + expect(contentTypeMap.has(contentType.name)) + }) + expect(contentTypeMap.size).toBe(contentTypeItems.length) + }) + + it(`builds foreignReferenceMap`, () => { + const { entries } = currentSyncData const resolvable = buildResolvableSet({ assets: currentSyncData.assets, - entryList, + entries, }) - const foreignReferenceMap = buildForeignReferenceMap({ + const contentTypeMap = buildContentTypeMap({ contentTypeItems, - entryList, + restrictedNodeFields, + conflictFieldPrefix, + useNameForId: true, + }) + + const foreignReferenceMap = buildForeignReferenceMap({ + contentTypeMap, + entries, resolvable, defaultLocale, space, - useNameForId: true, }) expect(foreignReferenceMap[`24DPGBDeGEaYy8ms4Y8QMQ___Entry`][0].name).toBe( @@ -175,46 +168,53 @@ describe(`Process contentful data (by name)`, () => { }) it(`creates nodes for each entry`, () => { - const entryList = buildEntryList({ - mergedSyncData: currentSyncData, - contentTypeItems, - }) + const { entries } = currentSyncData const resolvable = buildResolvableSet({ assets: currentSyncData.assets, - entryList, + entries, }) - const foreignReferenceMap = buildForeignReferenceMap({ + const contentTypeMap = buildContentTypeMap({ contentTypeItems, - entryList, + restrictedNodeFields, + conflictFieldPrefix, + useNameForId: true, + }) + + const foreignReferenceMap = buildForeignReferenceMap({ + contentTypeMap, + entries, resolvable, defaultLocale, space, - useNameForId: true, }) const createNode = jest.fn() const createNodeId = jest.fn(id => id) const getNode = jest.fn(() => undefined) // All nodes are new - contentTypeItems.forEach((contentTypeItem, i) => { - createNodesForContentType({ - contentTypeItem, - restrictedNodeFields, - conflictFieldPrefix, - entries: entryList[i], - createNode, - createNodeId, - getNode, - resolvable, - foreignReferenceMap, - defaultLocale, - locales, - space, - useNameForId: true, - pluginConfig, - unstable_createNodeManifest, - }) + + createContentTypeNodes({ + contentTypeMap, + createNode, + createNodeId, + }) + + createNodes({ + contentTypeMap, + restrictedNodeFields, + conflictFieldPrefix, + entries, + createNode, + createNodeId, + getNode, + resolvable, + foreignReferenceMap, + defaultLocale, + locales, + space, + pluginConfig, + unstable_createNodeManifest: null, }) const nodeTypeCounts = countCreatedNodeTypesFromMock(createNode.mock) @@ -274,27 +274,29 @@ describe(`Process contentful data (by name)`, () => { describe(`Skip existing nodes in warm build`, () => { it(`creates nodes for each entry`, () => { - const entryList = buildEntryList({ - mergedSyncData: currentSyncData, - contentTypeItems, + const createNode = jest.fn() + const createNodeId = jest.fn(id => id) + const { entries, assets } = currentSyncData + const resolvable = buildResolvableSet({ + assets, + entries, }) - const resolvable = buildResolvableSet({ - assets: currentSyncData.assets, - entryList, + const contentTypeMap = buildContentTypeMap({ + contentTypeItems, + restrictedNodeFields, + conflictFieldPrefix, + useNameForId: true, }) const foreignReferenceMap = buildForeignReferenceMap({ - contentTypeItems, - entryList, + contentTypeMap, + entries, resolvable, defaultLocale, space, - useNameForId: true, }) - const createNode = jest.fn() - const createNodeId = jest.fn(id => id) let doReturn = true const getNode = jest.fn(id => { if (doReturn) { @@ -304,30 +306,33 @@ describe(`Skip existing nodes in warm build`, () => { // returned is not relevant to test so update if anything breaks. return { id, - internal: { contentDigest: entryList[0][0].sys.updatedAt }, + internal: { contentDigest: entries[0].sys.updatedAt }, } } // All other nodes are new ("unknown") return undefined }) - contentTypeItems.forEach((contentTypeItem, i) => { - createNodesForContentType({ - contentTypeItem, - restrictedNodeFields, - conflictFieldPrefix, - entries: entryList[i], - createNode, - createNodeId, - getNode, - resolvable, - foreignReferenceMap, - defaultLocale, - locales, - space, - useNameForId: true, - pluginConfig, - unstable_createNodeManifest, - }) + createContentTypeNodes({ + contentTypeMap, + createNode, + createNodeId, + }) + + createNodes({ + contentTypeMap, + restrictedNodeFields, + conflictFieldPrefix, + entries, + createNode, + createNodeId, + getNode, + resolvable, + foreignReferenceMap, + defaultLocale, + locales, + space, + pluginConfig, + unstable_createNodeManifest: null, }) const nodeTypeCounts = countCreatedNodeTypesFromMock(createNode.mock) @@ -363,27 +368,28 @@ describe(`Skip existing nodes in warm build`, () => { describe(`Process existing mutated nodes in warm build`, () => { it(`creates nodes for each entry`, () => { - const entryList = buildEntryList({ - mergedSyncData: currentSyncData, - contentTypeItems, - }) - + const createNode = jest.fn() + const createNodeId = jest.fn(id => id) + const { entries, assets } = currentSyncData const resolvable = buildResolvableSet({ - assets: currentSyncData.assets, - entryList, + assets, + entries, + }) + const contentTypeMap = buildContentTypeMap({ + contentTypeItems, + restrictedNodeFields, + conflictFieldPrefix, + useNameForId: true, }) const foreignReferenceMap = buildForeignReferenceMap({ - contentTypeItems, - entryList, + contentTypeMap, + entries, resolvable, defaultLocale, space, - useNameForId: true, }) - const createNode = jest.fn() - const createNodeId = jest.fn(id => id) let doReturn = true const getNode = jest.fn(id => { if (doReturn) { @@ -394,31 +400,34 @@ describe(`Process existing mutated nodes in warm build`, () => { return { id, internal: { - contentDigest: entryList[0][0].sys.updatedAt + `changed`, + contentDigest: entries[0].sys.updatedAt + `changed`, }, } } // All other nodes are new ("unknown") return undefined }) - contentTypeItems.forEach((contentTypeItem, i) => { - createNodesForContentType({ - contentTypeItem, - restrictedNodeFields, - conflictFieldPrefix, - entries: entryList[i], - createNode, - createNodeId, - getNode, - resolvable, - foreignReferenceMap, - defaultLocale, - locales, - space, - useNameForId: true, - pluginConfig, - unstable_createNodeManifest, - }) + createContentTypeNodes({ + contentTypeMap, + createNode, + createNodeId, + }) + + createNodes({ + contentTypeMap, + restrictedNodeFields, + conflictFieldPrefix, + entries, + createNode, + createNodeId, + getNode, + resolvable, + foreignReferenceMap, + defaultLocale, + locales, + space, + pluginConfig, + unstable_createNodeManifest: null, }) const nodeTypeCounts = countCreatedNodeTypesFromMock(createNode.mock) @@ -457,72 +466,96 @@ describe(`Process existing mutated nodes in warm build`, () => { }) describe(`Process contentful data (by id)`, () => { - it(`builds foreignReferenceMap`, () => { - const entryList = buildEntryList({ - mergedSyncData: currentSyncData, + it(`builds contentTypeMap`, () => { + const contentTypeMap = buildContentTypeMap({ contentTypeItems, + restrictedNodeFields, + conflictFieldPrefix, + useNameForId: true, }) + contentTypeItems.forEach(contentType => { + expect(contentTypeMap.has(contentType.sys.id)) + }) + expect(contentTypeMap.size).toBe(contentTypeItems.length) + }) + + it(`builds foreignReferenceMap`, () => { + const { entries, assets } = currentSyncData const resolvable = buildResolvableSet({ - assets: currentSyncData.assets, - entryList, + assets, + entries, }) - const foreignReferenceMap = buildForeignReferenceMap({ + + const contentTypeMap = buildContentTypeMap({ contentTypeItems, - entryList, + restrictedNodeFields, + conflictFieldPrefix, + useNameForId: false, + }) + + const foreignReferenceMap = buildForeignReferenceMap({ + contentTypeMap, + entries, resolvable, defaultLocale, space, - useNameForId: false, }) expect(foreignReferenceMap[`24DPGBDeGEaYy8ms4Y8QMQ___Entry`][0].name).toBe( - `2pqfxujwe8qsykum0u6w8m___NODE` + `2PqfXUJwE8qSYKuM0U6w8M___NODE` ) expect(foreignReferenceMap[`2Y8LhXLnYAYqKCGEWG4EKI___Asset`][0].name).toBe( - `sfztzbsum8coewygeuyes___NODE` + `sFzTZbSuM8coEwygeUYes___NODE` ) }) it(`creates nodes for each entry`, () => { - const entryList = buildEntryList({ - mergedSyncData: currentSyncData, - contentTypeItems, - }) + const { entries, assets } = currentSyncData const resolvable = buildResolvableSet({ - assets: currentSyncData.assets, - entryList, + assets, + entries, }) - const foreignReferenceMap = buildForeignReferenceMap({ + + const contentTypeMap = buildContentTypeMap({ contentTypeItems, - entryList, + restrictedNodeFields, + conflictFieldPrefix, + useNameForId: false, + }) + + const foreignReferenceMap = buildForeignReferenceMap({ + contentTypeMap, + entries, resolvable, defaultLocale, space, - useNameForId: false, }) - const createNode = jest.fn() const createNodeId = jest.fn(id => id) + const getNode = jest.fn(() => undefined) // All nodes are new - contentTypeItems.forEach((contentTypeItem, i) => { - createNodesForContentType({ - contentTypeItem, - restrictedNodeFields, - conflictFieldPrefix, - entries: entryList[i], - createNode, - createNodeId, - getNode, - resolvable, - foreignReferenceMap, - defaultLocale, - locales, - space, - useNameForId: false, - pluginConfig, - unstable_createNodeManifest, - }) + createContentTypeNodes({ + contentTypeMap, + createNode, + createNodeId, + }) + + createNodes({ + contentTypeMap, + restrictedNodeFields, + conflictFieldPrefix, + entries, + createNode, + createNodeId, + getNode, + resolvable, + foreignReferenceMap, + defaultLocale, + locales, + space, + pluginConfig, + unstable_createNodeManifest: null, }) const nodeTypeCounts = countCreatedNodeTypesFromMock(createNode.mock) diff --git a/packages/gatsby-source-contentful/src/create-schema-customization.js b/packages/gatsby-source-contentful/src/create-schema-customization.js index 0d64eabe6174c..f0c9f70f66fe0 100644 --- a/packages/gatsby-source-contentful/src/create-schema-customization.js +++ b/packages/gatsby-source-contentful/src/create-schema-customization.js @@ -4,7 +4,7 @@ import { fetchContentTypes } from "./fetch" import { createPluginConfig } from "./plugin-options" import { CODES } from "./report" -async function getContentTypesFromContentFul({ +async function getContentTypesFromContentful({ cache, reporter, pluginConfig, @@ -24,11 +24,9 @@ async function getContentTypesFromContentFul({ // Establish identifier for content type // Use `name` if specified, otherwise, use internal id (usually a natural-language constant, // but sometimes a base62 uuid generated by Contentful, hence the option) - let contentTypeItemId + let contentTypeItemId = contentTypeItem.sys.id if (useNameForId) { contentTypeItemId = contentTypeItem.name.toLowerCase() - } else { - contentTypeItemId = contentTypeItem.sys.id.toLowerCase() } if (restrictedContentTypes.includes(contentTypeItemId)) { @@ -66,7 +64,7 @@ export async function createSchemaCustomization( )}` contentTypeItems = await cache.get(`contentful-content-types-${sourceId}`) } else { - contentTypeItems = await getContentTypesFromContentFul({ + contentTypeItems = await getContentTypesFromContentful({ cache, reporter, pluginConfig, diff --git a/packages/gatsby-source-contentful/src/normalize.js b/packages/gatsby-source-contentful/src/normalize.js index 7f9689027885e..556378d0ad58c 100644 --- a/packages/gatsby-source-contentful/src/normalize.js +++ b/packages/gatsby-source-contentful/src/normalize.js @@ -47,6 +47,8 @@ export const makeId = ({ spaceId, id, currentLocale, defaultLocale, type }) => { const normalizedType = type.startsWith(`Deleted`) ? type.substring(`Deleted`.length) : type + + // @todo no need for this extra logic, just keep the locale and remove default return currentLocale === defaultLocale ? `${spaceId}___${id}___${normalizedType}` : `${spaceId}___${id}___${normalizedType}___${currentLocale}` @@ -57,107 +59,80 @@ const makeMakeId = (spaceId, id, type) => createNodeId(makeId({ spaceId, id, currentLocale, defaultLocale, type })) -export const buildEntryList = ({ contentTypeItems, mergedSyncData }) => { - // Create buckets for each type sys.id that we care about (we will always want an array for each, even if its empty) - const map = new Map( - contentTypeItems.map(contentType => [contentType.sys.id, []]) - ) - // Now fill the buckets. Ignore entries for which there exists no bucket. (Not sure if that ever happens) - mergedSyncData.entries.map(entry => { - const arr = map.get(entry.sys.contentType.sys.id) - if (arr) { - arr.push(entry) - } - }) - // Order is relevant, must map 1:1 to contentTypeItems array - return contentTypeItems.map(contentType => map.get(contentType.sys.id)) -} +export const buildResolvableSet = ({ entries = [], assets = [] }) => + new Set([...entries, ...assets].map(n => `${n.sys.id}___${n.sys.type}`)) -export const buildResolvableSet = ({ - entryList, - existingNodes = [], - assets = [], +export const buildContentTypeMap = ({ + contentTypeItems, + restrictedNodeFields, + conflictFieldPrefix, + useNameForId, }) => { - const resolvable = new Set() - existingNodes.forEach(node => { - // We need to add only root level resolvable (assets and entries) - // Derived nodes (markdown or JSON) will be recreated if needed. - resolvable.add(`${node.contentful_id}___${node.sys.type}`) - }) + const contentTypeMap = new Map() + for (let i = 0; i < contentTypeItems.length; i++) { + const contentTypeItem = contentTypeItems[i] - entryList.forEach(entries => { - entries.forEach(entry => - resolvable.add(`${entry.sys.id}___${entry.sys.type}`) - ) - }) + // Establish identifier for content type + // Use `name` if specified, otherwise, use internal id (usually a natural-language constant, + // but sometimes a base62 uuid generated by Contentful, hence the option) + let normalizedContentTypeId = contentTypeItem.sys.id + if (useNameForId) { + normalizedContentTypeId = contentTypeItem.name.toLowerCase() + } - assets.forEach(assetItem => - resolvable.add(`${assetItem.sys.id}___${assetItem.sys.type}`) - ) + // Warn about any field conflicts + const conflictFields = [] + contentTypeItem.fields.forEach(contentTypeItemField => { + const fieldName = contentTypeItemField.id + if (restrictedNodeFields.includes(fieldName)) { + console.log( + `Restricted field found for ContentType ${normalizedContentTypeId} and field ${fieldName}. Prefixing with ${conflictFieldPrefix}.` + ) + conflictFields.push(fieldName) + } + }) - return resolvable + contentTypeMap.set(contentTypeItem.sys.id, { + normalizedContentTypeId, + conflictFields, + contentTypeItem, + }) + } + return contentTypeMap } export const buildForeignReferenceMap = ({ - contentTypeItems, - entryList, + contentTypeMap, + entries, resolvable, defaultLocale, space, - useNameForId, }) => { const foreignReferenceMap = {} - contentTypeItems.forEach((contentTypeItem, i) => { + + entries.forEach(entryItem => { + const entryItemFields = entryItem.fields + // Establish identifier for content type // Use `name` if specified, otherwise, use internal id (usually a natural-language constant, // but sometimes a base62 uuid generated by Contentful, hence the option) - let contentTypeItemId - if (useNameForId) { - contentTypeItemId = contentTypeItem.name.toLowerCase() - } else { - contentTypeItemId = contentTypeItem.sys.id.toLowerCase() - } - - entryList[i].forEach(entryItem => { - const entryItemFields = entryItem.fields - Object.keys(entryItemFields).forEach(entryItemFieldKey => { - if (entryItemFields[entryItemFieldKey]) { - const entryItemFieldValue = - entryItemFields[entryItemFieldKey][defaultLocale] - // If this is an array of single reference object - // add to the reference map, otherwise ignore. - if (Array.isArray(entryItemFieldValue)) { - if ( - entryItemFieldValue[0] && - entryItemFieldValue[0].sys && - entryItemFieldValue[0].sys.type && - entryItemFieldValue[0].sys.id - ) { - entryItemFieldValue.forEach(v => { - const key = `${v.sys.id}___${v.sys.linkType || v.sys.type}` - // Don't create link to an unresolvable field. - if (!resolvable.has(key)) { - return - } - - if (!foreignReferenceMap[key]) { - foreignReferenceMap[key] = [] - } - foreignReferenceMap[key].push({ - name: `${contentTypeItemId}___NODE`, - id: entryItem.sys.id, - spaceId: space.sys.id, - type: entryItem.sys.type, - }) - }) - } - } else if ( - entryItemFieldValue?.sys?.type && - entryItemFieldValue.sys.id - ) { - const key = `${entryItemFieldValue.sys.id}___${ - entryItemFieldValue.sys.linkType || entryItemFieldValue.sys.type - }` + const contentTypeItemId = contentTypeMap.get( + entryItem.sys.contentType.sys.id + ).normalizedContentTypeId + + Object.keys(entryItemFields).forEach(entryItemFieldKey => { + // @todo this seems not to cover translateable reference fields + // loop by locales? + const entryItemFieldValue = + entryItemFields[entryItemFieldKey][defaultLocale] + + // If this is an array of single reference object + // add to the reference map, otherwise ignore. + + if (Array.isArray(entryItemFieldValue)) { + if (entryItemFieldValue[0]?.sys?.type === `Link`) { + entryItemFieldValue.forEach(v => { + const key = `${v.sys.id}___${v.sys.linkType || v.sys.type}` // Don't create link to an unresolvable field. if (!resolvable.has(key)) { return @@ -172,9 +147,27 @@ export const buildForeignReferenceMap = ({ spaceId: space.sys.id, type: entryItem.sys.type, }) - } + }) } - }) + } else if (entryItemFieldValue?.sys?.type === `Link`) { + const key = `${entryItemFieldValue.sys.id}___${ + entryItemFieldValue.sys.linkType || entryItemFieldValue.sys.type + }` + // Don't create link to an unresolvable field. + if (!resolvable.has(key)) { + return + } + + if (!foreignReferenceMap[key]) { + foreignReferenceMap[key] = [] + } + foreignReferenceMap[key].push({ + name: `${contentTypeItemId}___NODE`, + id: entryItem.sys.id, + spaceId: space.sys.id, + type: entryItem.sys.type, + }) + } }) }) @@ -300,8 +293,43 @@ function contentfulCreateNodeManifest({ } } -export const createNodesForContentType = ({ - contentTypeItem, +export const createContentTypeNodes = ({ + contentTypeMap, + createNodeId, + createNode, +}) => { + const createContentTypePromises = [] + for (const { + contentTypeItem, + normalizedContentTypeId, + } of contentTypeMap.values()) { + // Create a node for each content type + const contentTypeNode = { + id: createNodeId(normalizedContentTypeId), + parent: null, + children: [], + contentful_id: contentTypeItem.sys.id, + name: contentTypeItem.name, + displayField: contentTypeItem.displayField, + description: contentTypeItem.description, + internal: { + type: `${makeTypeName(`ContentType`)}`, + }, + sys: { + type: contentTypeItem.sys.type, + }, + } + + // The content of an entry is guaranteed to be updated if and only if the .sys.updatedAt field changed + contentTypeNode.internal.contentDigest = contentTypeItem.sys.updatedAt + + createContentTypePromises.push(createNode(contentTypeNode)) + } + return createContentTypePromises +} + +export const createNodes = ({ + contentTypeMap, restrictedNodeFields, conflictFieldPrefix, entries, @@ -314,42 +342,10 @@ export const createNodesForContentType = ({ defaultLocale, locales, space, - useNameForId, pluginConfig, }) => { - // Establish identifier for content type - // Use `name` if specified, otherwise, use internal id (usually a natural-language constant, - // but sometimes a base62 uuid generated by Contentful, hence the option) - let contentTypeItemId - if (useNameForId) { - contentTypeItemId = contentTypeItem.name - } else { - contentTypeItemId = contentTypeItem.sys.id - } - const createNodePromises = [] - // Create a node for each content type - const contentTypeNode = { - id: createNodeId(contentTypeItemId), - parent: null, - children: [], - name: contentTypeItem.name, - displayField: contentTypeItem.displayField, - description: contentTypeItem.description, - internal: { - type: `${makeTypeName(`ContentType`)}`, - }, - sys: { - type: contentTypeItem.sys.type, - }, - } - - // The content of an entry is guaranteed to be updated if and only if the .sys.updatedAt field changed - contentTypeNode.internal.contentDigest = contentTypeItem.sys.updatedAt - - createNodePromises.push(createNode(contentTypeNode)) - locales.forEach(locale => { const localesFallback = buildFallbackChain(locales) const mId = makeMakeId({ @@ -362,23 +358,13 @@ export const createNodesForContentType = ({ localesFallback, }) - // Warn about any field conflicts - const conflictFields = [] - contentTypeItem.fields.forEach(contentTypeItemField => { - const fieldName = contentTypeItemField.id - if (restrictedNodeFields.includes(fieldName)) { - console.log( - `Restricted field found for ContentType ${contentTypeItemId} and field ${fieldName}. Prefixing with ${conflictFieldPrefix}.` - ) - conflictFields.push(fieldName) - } - }) - const childrenNodes = [] // First create nodes for each of the entries of that content type const entryNodes = entries .map(entryItem => { + const { contentTypeItem, conflictFields, normalizedContentTypeId } = + contentTypeMap.get(entryItem.sys.contentType.sys.id) const entryNodeId = mId( space.sys.id, entryItem.sys.id, @@ -418,12 +404,7 @@ export const createNodesForContentType = ({ if (entryItemFields[entryItemFieldKey]) { const entryItemFieldValue = entryItemFields[entryItemFieldKey] if (Array.isArray(entryItemFieldValue)) { - if ( - entryItemFieldValue[0] && - entryItemFieldValue[0].sys && - entryItemFieldValue[0].sys.type && - entryItemFieldValue[0].sys.id - ) { + if (entryItemFieldValue[0]?.sys?.type === `Link`) { // Check if there are any values in entryItemFieldValue to prevent // creating an empty node field in case when original key field value // is empty due to links to missing entities @@ -447,12 +428,7 @@ export const createNodesForContentType = ({ delete entryItemFields[entryItemFieldKey] } - } else if ( - entryItemFieldValue && - entryItemFieldValue.sys && - entryItemFieldValue.sys.type && - entryItemFieldValue.sys.id - ) { + } else if (entryItemFieldValue?.sys?.type === `Link`) { if ( resolvable.has( `${entryItemFieldValue.sys.id}___${ @@ -512,10 +488,10 @@ export const createNodesForContentType = ({ contentful_id: entryItem.sys.id, createdAt: entryItem.sys.createdAt, updatedAt: entryItem.sys.updatedAt, - parent: contentTypeItemId, + parent: normalizedContentTypeId, children: [], internal: { - type: `${makeTypeName(contentTypeItemId)}`, + type: `${makeTypeName(normalizedContentTypeId)}`, }, sys: { type: entryItem.sys.type, diff --git a/packages/gatsby-source-contentful/src/source-nodes.js b/packages/gatsby-source-contentful/src/source-nodes.js index 79ac70db723bc..70a16194864d3 100644 --- a/packages/gatsby-source-contentful/src/source-nodes.js +++ b/packages/gatsby-source-contentful/src/source-nodes.js @@ -1,17 +1,17 @@ // @ts-check -import { createClient } from "contentful" import isOnline from "is-online" import _ from "lodash" import { downloadContentfulAssets } from "./download-contentful-assets" import { fetchContent } from "./fetch" import { - buildEntryList, buildForeignReferenceMap, - buildResolvableSet, createAssetNodes, - createNodesForContentType, + createNodes, + createContentTypeNodes, makeId, + buildResolvableSet, + buildContentTypeMap, } from "./normalize" import { createPluginConfig } from "./plugin-options" import { CODES } from "./report" @@ -231,61 +231,9 @@ export async function sourceNodes( ) processingActivity.start() - // Store a raw and unresolved copy of the data for caching - const mergedSyncDataRaw = _.cloneDeep(mergedSyncData) - - // Use the JS-SDK to resolve the entries and assets - const res = await createClient({ - space: `none`, - accessToken: `fake-access-token`, - }).parseEntries({ - items: mergedSyncData.entries, - includes: { - assets: mergedSyncData.assets, - entries: mergedSyncData.entries, - }, - }) - - mergedSyncData.entries = res.items - - // Inject raw API output to rich text fields - const richTextFieldMap = new Map() - contentTypeItems.forEach(contentType => { - richTextFieldMap.set( - contentType.sys.id, - contentType.fields - .filter(field => field.type === `RichText`) - .map(field => field.id) - ) - }) - - const rawEntries = new Map() - mergedSyncDataRaw.entries.forEach(rawEntry => - rawEntries.set(rawEntry.sys.id, rawEntry) - ) - - mergedSyncData.entries.forEach(entry => { - const contentTypeId = entry.sys.contentType.sys.id - const richTextFieldIds = richTextFieldMap.get(contentTypeId) - if (richTextFieldIds) { - richTextFieldIds.forEach(richTextFieldId => { - if (!entry.fields[richTextFieldId]) { - return - } - entry.fields[richTextFieldId] = rawEntries.get(entry.sys.id).fields[ - richTextFieldId - ] - }) - } - }) - - const { assets } = mergedSyncData - - const entryList = buildEntryList({ - mergedSyncData, - contentTypeItems, - }) + const { assets, entries } = mergedSyncData + // Array of all existing Contentful nodes const existingNodes = getNodes().filter( n => n.internal.owner === `gatsby-source-contentful` && @@ -297,48 +245,49 @@ export async function sourceNodes( // Create map of resolvable ids so we can check links against them while creating // links. - const resolvable = buildResolvableSet({ - existingNodes, - entryList, - assets, + const resolvable = buildResolvableSet({ entries, assets }) + + const contentTypeMap = buildContentTypeMap({ + contentTypeItems, + restrictedNodeFields, + conflictFieldPrefix, + useNameForId: pluginConfig.get(`useNameForId`), }) // Build foreign reference map before starting to insert any nodes const foreignReferenceMap = buildForeignReferenceMap({ - contentTypeItems, - entryList, + contentTypeMap, + entries, resolvable, defaultLocale, space, - useNameForId: pluginConfig.get(`useNameForId`), }) reporter.verbose(`Resolving Contentful references`) const newOrUpdatedEntries = new Set() - entryList.forEach(entries => { - entries.forEach(entry => { - newOrUpdatedEntries.add(`${entry.sys.id}___${entry.sys.type}`) - }) + currentSyncData.entries.forEach(entry => { + newOrUpdatedEntries.add(`${entry.sys.id}___${entry.sys.type}`) }) - // Update existing entry nodes that weren't updated but that need reverse - // links added. + // Update existing entry nodes that weren't updated but that need reverse links added. existingNodes - .filter(n => newOrUpdatedEntries.has(`${n.id}___${n.sys.type}`)) + .filter(n => !newOrUpdatedEntries.has(`${n.id}___${n.sys.type}`)) .forEach(n => { if (foreignReferenceMap[`${n.id}___${n.sys.type}`]) { foreignReferenceMap[`${n.id}___${n.sys.type}`].forEach( foreignReference => { - // Add reverse links - if (n[foreignReference.name]) { - n[foreignReference.name].push(foreignReference.id) - // It might already be there so we'll uniquify after pushing. - n[foreignReference.name] = _.uniq(n[foreignReference.name]) - } else { - // If is one foreign reference, there can always be many. - // Best to be safe and put it in an array to start with. - n[foreignReference.name] = [foreignReference.id] + const { name, id } = foreignReference + + // Create new reference field when none exists + if (!n[name]) { + n[name] = [id] + return + } + + // Add non existing references to reference field + if (n[name] && !n[name].includes(id)) { + n[name].push(id) } } ) @@ -395,28 +344,24 @@ export async function sourceNodes( ) creationActivity.start() - for (let i = 0; i < contentTypeItems.length; i++) { - const contentTypeItem = contentTypeItems[i] - - if (entryList[i].length) { - reporter.info( - `Creating ${entryList[i].length} Contentful ${ - pluginConfig.get(`useNameForId`) - ? contentTypeItem.name - : contentTypeItem.sys.id - } nodes` - ) - } + await Promise.all( + createContentTypeNodes({ + contentTypeMap, + createNode, + createNodeId, + }) + ) + + if (entries.length) { + reporter.info(`Creating ${entries.length} Contentful Entry nodes`) - // A contentType can hold lots of entries which create nodes - // We wait until all nodes are created and processed until we handle the next one // TODO add batching in gatsby-core await Promise.all( - createNodesForContentType({ - contentTypeItem, + createNodes({ + contentTypeMap, restrictedNodeFields, conflictFieldPrefix, - entries: entryList[i], + entries, createNode, createNodeId, getNode, @@ -425,7 +370,6 @@ export async function sourceNodes( defaultLocale, locales, space, - useNameForId: pluginConfig.get(`useNameForId`), pluginConfig, unstable_createNodeManifest, })