From e2acebbf1a537b982649bf30033d0f63b8bb44fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Tue, 22 Mar 2022 00:46:44 +0100 Subject: [PATCH 1/6] Get plugin options from entity config --- src/gatsby-node.js | 16 ++++++++-------- src/helpers.js | 4 +++- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/gatsby-node.js b/src/gatsby-node.js index bf23610..3b8636a 100644 --- a/src/gatsby-node.js +++ b/src/gatsby-node.js @@ -24,22 +24,22 @@ exports.sourceNodes = async ( getNodes, getNode, }, - pluginOptions + strapiConfig ) => { // Cast singleTypes and collectionTypes to empty arrays if they're not defined - if (!Array.isArray(pluginOptions.singleTypes)) { - pluginOptions.singleTypes = []; + if (!Array.isArray(strapiConfig.singleTypes)) { + strapiConfig.singleTypes = []; } - if (!Array.isArray(pluginOptions.collectionTypes)) { - pluginOptions.collectionTypes = []; + if (!Array.isArray(strapiConfig.collectionTypes)) { + strapiConfig.collectionTypes = []; } - const { schemas } = await fetchStrapiContentTypes(pluginOptions); + const { schemas } = await fetchStrapiContentTypes(strapiConfig); const { deleteNode, touchNode } = actions; const ctx = { - strapiConfig: pluginOptions, + strapiConfig, actions, schemas, createContentDigest, @@ -58,7 +58,7 @@ exports.sourceNodes = async ( existingNodes.forEach((n) => touchNode(n)); - const endpoints = getEndpoints(pluginOptions, schemas); + const endpoints = getEndpoints(strapiConfig, schemas); const lastFetched = await cache.get(LAST_FETCHED_KEY); diff --git a/src/helpers.js b/src/helpers.js index 5941b67..5a0e995 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -99,7 +99,7 @@ const getEndpoints = ({ collectionTypes, singleTypes }, schemas) => { ) .map(({ schema: { kind, singularName, pluralName }, uid }) => { const options = types.find((config) => config.singularName === singularName); - const { queryParams, queryLimit } = options; + const { queryParams, queryLimit, pluginOptions } = options; if (kind === 'singleType') { return { @@ -110,6 +110,7 @@ const getEndpoints = ({ collectionTypes, singleTypes }, schemas) => { queryParams: queryParams || { populate: '*', }, + pluginOptions, }; } @@ -127,6 +128,7 @@ const getEndpoints = ({ collectionTypes, singleTypes }, schemas) => { }, populate: queryParams?.populate || '*', }, + pluginOptions, }; }); From abad4f8b93eebc905ab6be45b73d2f6096933142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Tue, 22 Mar 2022 10:55:11 +0100 Subject: [PATCH 2/6] Fetch all required localizations --- src/fetch.js | 69 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/src/fetch.js b/src/fetch.js index aa917a5..0200e8b 100644 --- a/src/fetch.js +++ b/src/fetch.js @@ -3,8 +3,8 @@ import createInstance from './axiosInstance'; import qs from 'qs'; import { cleanData } from './clean-data'; -const fetchStrapiContentTypes = async (pluginOptions) => { - const axiosInstance = createInstance(pluginOptions); +const fetchStrapiContentTypes = async (strapiConfig) => { + const axiosInstance = createInstance(strapiConfig); const [ { data: { data: contentTypes }, @@ -24,10 +24,13 @@ const fetchStrapiContentTypes = async (pluginOptions) => { }; }; -const fetchEntity = async ({ endpoint, queryParams, uid }, ctx) => { +const fetchEntity = async ({ endpoint, queryParams, uid, pluginOptions }, ctx) => { const { strapiConfig, reporter } = ctx; const axiosInstance = createInstance(strapiConfig); + // Ignore queryParams locale in favor of pluginOptions + delete queryParams.locale; + const opts = { method: 'GET', url: endpoint, @@ -38,9 +41,53 @@ const fetchEntity = async ({ endpoint, queryParams, uid }, ctx) => { try { reporter.info(`Starting to fetch data from Strapi - ${opts.url} with ${JSON.stringify(opts)}`); + // Handle internationalization + const locale = pluginOptions?.i18n?.locale; + const otherLocales = []; + if (locale) { + if (locale === 'all') { + // Get all available locales + const { data: response } = await axiosInstance({ + ...opts, + params: { + populate: { + localizations: { + fields: ['locale'], + }, + }, + }, + }); + // otherLocales = response.data.attributes.localizations.data.map(localization => localization.attributes.locale) + response.data.attributes.localizations.data.forEach((localization) => + otherLocales.push(localization.attributes.locale) + ); + } else { + // Only one locale + opts.params.locale = locale; + } + } + + // Fetch default entity based on request options const { data } = await axiosInstance(opts); - return castArray(data.data).map((entry) => cleanData(entry, { ...ctx, contentTypeUid: uid })); + // Fetch other localizations of this entry if there are any + const otherLocalizationsPromises = otherLocales.map(async (locale) => { + const { data: localizationResponse } = await axiosInstance({ + ...opts, + params: { + ...opts.params, + locale, + }, + }); + return localizationResponse.data; + }); + + // Run queries in parallel + const otherLocalizationsData = await Promise.all(otherLocalizationsPromises); + + return castArray([data.data, ...otherLocalizationsData]).map((entry) => + cleanData(entry, { ...ctx, contentTypeUid: uid }) + ); } catch (error) { // reporter.panic( // `Failed to fetch data from Strapi ${opts.url} with ${JSON.stringify(opts)}`, @@ -50,10 +97,13 @@ const fetchEntity = async ({ endpoint, queryParams, uid }, ctx) => { } }; -const fetchEntities = async ({ endpoint, queryParams, uid }, ctx) => { +const fetchEntities = async ({ endpoint, queryParams, uid, pluginOptions }, ctx) => { const { strapiConfig, reporter } = ctx; const axiosInstance = createInstance(strapiConfig); + // Ignore queryParams locale in favor of pluginOptions + delete queryParams.locale; + const opts = { method: 'GET', url: endpoint, @@ -61,6 +111,11 @@ const fetchEntities = async ({ endpoint, queryParams, uid }, ctx) => { paramsSerializer: (params) => qs.stringify(params, { encodeValuesOnly: true }), }; + // Use locale from pluginOptions if it's defined + if (pluginOptions?.i18n?.locale) { + opts.params.locale = pluginOptions.i18n.locale; + } + try { reporter.info( `Starting to fetch data from Strapi - ${opts.url} with ${JSON.stringify(opts.params)}` @@ -78,7 +133,7 @@ const fetchEntities = async ({ endpoint, queryParams, uid }, ctx) => { length: pageCount - page, }).map((_, i) => i + page + 1); - const arrayOfPromises = pagesToGet.map((page) => { + const fetchPagesPromises = pagesToGet.map((page) => { return (async () => { const options = { ...opts, @@ -104,7 +159,7 @@ const fetchEntities = async ({ endpoint, queryParams, uid }, ctx) => { })(); }); - const results = await Promise.all(arrayOfPromises); + const results = await Promise.all(fetchPagesPromises); const cleanedData = [...data, ...flattenDeep(results)].map((entry) => cleanData(entry, { ...ctx, contentTypeUid: uid }) From 9ee69d6e0129b16a7a3d09e21053d3fe83ac6f58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Tue, 22 Mar 2022 11:12:24 +0100 Subject: [PATCH 3/6] Remove code comment --- src/fetch.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/fetch.js b/src/fetch.js index 0200e8b..78a47ac 100644 --- a/src/fetch.js +++ b/src/fetch.js @@ -57,7 +57,6 @@ const fetchEntity = async ({ endpoint, queryParams, uid, pluginOptions }, ctx) = }, }, }); - // otherLocales = response.data.attributes.localizations.data.map(localization => localization.attributes.locale) response.data.attributes.localizations.data.forEach((localization) => otherLocales.push(localization.attributes.locale) ); From ccb6af0062988ff3c825039189ea37a790ff5bd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Tue, 22 Mar 2022 11:57:54 +0100 Subject: [PATCH 4/6] Add i18n docs --- README.md | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 83cca04..a165d45 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,10 @@ Source plugin for pulling documents into Gatsby from a Strapi API. Table of contents - [gatsby-source-strapi](#gatsby-source-strapi) - - [Install](#installing-the-plugin) + - [Installing the plugin](#installing-the-plugin) + - [Using yarn](#using-yarn) + - [Or using NPM](#or-using-npm) + - [Setting up the plugin](#setting-up-the-plugin) - [Basic usage](#basic-usage) - [Advanced usage](#advanced-usage) - [Deep queries populate](#deep-queries-populate) @@ -17,7 +20,14 @@ Source plugin for pulling documents into Gatsby from a Strapi API. - [Rich text field](#rich-text-field) - [Components](#components) - [Dynamic zones](#dynamic-zones) + - [Internationalization](#internationalization) - [Gatsby cloud and preview environment setup](#gatsby-cloud-and-preview-environment-setup) + - [Setup](#setup) + - [Enabling Content Sync](#enabling-content-sync) + - [Installing the @strapi/plugin-gatsby-preview](#installing-the-strapiplugin-gatsby-preview) + - [Using yarn](#using-yarn-1) + - [Using npm](#using-npm) + - [Configurations](#configurations) - [Restrictions and limitations](#restrictions-and-limitations) @@ -42,7 +52,7 @@ You can enable and configure this plugin in your `gatsby-config.js` file. ### Basic usage -First, you need to configure the `STRAPI_API_URL` and the `STRAPI_TOKEN` environment variables. We recommend using [`dotenv`][https://github.com/motdotla/dotenv] to expose these variables. +First, you need to configure the `STRAPI_API_URL` and the `STRAPI_TOKEN` environment variables. We recommend using [`dotenv`](https://github.com/motdotla/dotenv) to expose these variables. Make sure to create a full-access [API token](https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/configurations/optional/api-tokens.html) in Strapi. @@ -248,7 +258,7 @@ To query a specific component use the following query: #### Dynamic zones -To query dynamic zones, , write a query using [inline GraphQL fragments](https://graphql.org/learn/queries/#inline-fragments). +To query dynamic zones, write a query using [inline GraphQL fragments](https://graphql.org/learn/queries/#inline-fragments). You can use the following query: @@ -280,6 +290,62 @@ You can use the following query: } ``` +#### Internationalization + +Content types in Strapi can be localized with the [i18n plugin](https://docs.strapi.io/developer-docs/latest/plugins/i18n.html). But by default, gatsby-source-strapi will only fetch data in the default locale of your Strapi app. To specify which locale should be fetched, an `i18n` object can be provided in the content type's `pluginOptions`. Use the all value to get all available locales on a collection type. + +```javascript +const strapiConfig = { + // ... + collectionTypes: [ + { + singularName: 'article', + pluginOptions: { + i18n: { + locale: 'fr', // Only fetch a specific locale + }, + }, + }, + ], + singleTypes: [ + { + singularName: 'global', + pluginOptions: { + i18n: { + locale: 'all', // Fetch all localizations + }, + }, + }, + ], + // ... +}; +``` + +Then use the following query to fetch a localized content: + +```graphql +{ + # Get content in all available localizations + allStrapiGlobal { + nodes { + id + } + } + + # Get a single type in a specific locale + strapiGlobal(locale: {eq: "fr"}) { + id + } + + # Get a collection type in a specific locale + allStrapiArticle(filter: {locale: {eq: "fr"}}) { + nodes { + locale + } + } +} +``` + ## Gatsby cloud and preview environment setup ### Setup From 9115905fcfb304df27dec418124246abefed7849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Tue, 22 Mar 2022 12:05:43 +0100 Subject: [PATCH 5/6] Fix docs --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a165d45..9f58373 100644 --- a/README.md +++ b/README.md @@ -292,7 +292,7 @@ You can use the following query: #### Internationalization -Content types in Strapi can be localized with the [i18n plugin](https://docs.strapi.io/developer-docs/latest/plugins/i18n.html). But by default, gatsby-source-strapi will only fetch data in the default locale of your Strapi app. To specify which locale should be fetched, an `i18n` object can be provided in the content type's `pluginOptions`. Use the all value to get all available locales on a collection type. +Content types in Strapi can be localized with the [i18n plugin](https://docs.strapi.io/developer-docs/latest/plugins/i18n.html). But by default, gatsby-source-strapi will only fetch data in the default locale of your Strapi app. To specify which locale should be fetched, an `i18n` object can be provided in the content type's `pluginOptions`. You can also set the locale to `all` to get all available localizations of a content type: ```javascript const strapiConfig = { @@ -321,20 +321,20 @@ const strapiConfig = { }; ``` -Then use the following query to fetch a localized content: +Then use the one of the following queries to fetch a localized content type: ```graphql { # Get content in all available localizations allStrapiGlobal { nodes { - id + locale } } # Get a single type in a specific locale strapiGlobal(locale: {eq: "fr"}) { - id + locale } # Get a collection type in a specific locale From da12da54958aa56416116fd1e14acdae3555383b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Tue, 22 Mar 2022 17:43:17 +0100 Subject: [PATCH 6/6] Only ignore params.locale if pluginOptions is set --- src/fetch.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/fetch.js b/src/fetch.js index 78a47ac..1473428 100644 --- a/src/fetch.js +++ b/src/fetch.js @@ -28,9 +28,6 @@ const fetchEntity = async ({ endpoint, queryParams, uid, pluginOptions }, ctx) = const { strapiConfig, reporter } = ctx; const axiosInstance = createInstance(strapiConfig); - // Ignore queryParams locale in favor of pluginOptions - delete queryParams.locale; - const opts = { method: 'GET', url: endpoint, @@ -44,7 +41,11 @@ const fetchEntity = async ({ endpoint, queryParams, uid, pluginOptions }, ctx) = // Handle internationalization const locale = pluginOptions?.i18n?.locale; const otherLocales = []; + if (locale) { + // Ignore queryParams locale in favor of pluginOptions + delete queryParams.locale; + if (locale === 'all') { // Get all available locales const { data: response } = await axiosInstance({ @@ -62,7 +63,7 @@ const fetchEntity = async ({ endpoint, queryParams, uid, pluginOptions }, ctx) = ); } else { // Only one locale - opts.params.locale = locale; + queryParams.locale = locale; } } @@ -100,9 +101,6 @@ const fetchEntities = async ({ endpoint, queryParams, uid, pluginOptions }, ctx) const { strapiConfig, reporter } = ctx; const axiosInstance = createInstance(strapiConfig); - // Ignore queryParams locale in favor of pluginOptions - delete queryParams.locale; - const opts = { method: 'GET', url: endpoint, @@ -112,7 +110,8 @@ const fetchEntities = async ({ endpoint, queryParams, uid, pluginOptions }, ctx) // Use locale from pluginOptions if it's defined if (pluginOptions?.i18n?.locale) { - opts.params.locale = pluginOptions.i18n.locale; + delete queryParams.locale; + queryParams.locale = pluginOptions.i18n.locale; } try {