Skip to content

Instantly share code, notes, and snippets.

@DaveBitter
Last active May 19, 2020 08:58
Show Gist options
  • Save DaveBitter/144319f88c1f6775d348d45f899f6466 to your computer and use it in GitHub Desktop.
Save DaveBitter/144319f88c1f6775d348d45f899f6466 to your computer and use it in GitHub Desktop.
Export Simplified Contentful Data

Export Simplified Contentful Data

Contentful exports can be bloated. This utility exports Contentful in a simplified result.

Usage

  • Add the below file to your project and import the utility.
  • Install required dependencies: yarn install dotenv contentful-export

Configuration

You must pass a configuration containing at least the mandatory fields:

spaceId mandatory - String

Space Id of the Contentful space you want to export.

managementToken mandatory - String

Management Token of the Contentful space you want to export. You can generate this token here

topLevelEntryKey mandatory - String

Key of the top level entry you want to export you want to export. For example, pass 'page' to export all the pages and their linked content.

outputDir optional, defaults to './content' - String

Directory to save the generated export to.

outputFile optional, defaults to './content' - String

Filename to save the generated export as.

Example

const exportSimplifiedContentfulData = require('./exportSimplifiedContentfulData');

/*** Contenful Configuration ***/
const configuration = {
    spaceId: '<YOUR_CONTENTFUL_SPACE_ID>',
    managementToken: '<YOUR_CONTENTFUL_MANAGEMENT_TOKEN>',
    topLevelEntryKey: 'page',
    outputDir: './src/content',
    outputFile: 'content.json'
};

exportSimplifiedContentfulData(configuration);

// Saves following JSON to './src/content/content.json':
//
// {
//   "en-US": {
//     "home": {
//       "foo": "Bar"
//     },
//     "about": {
//       "foo": "Bar"
//     }
//   }
// }
const fs = require('fs');
const contentfulExport = require('contentful-export')
const defaultConfiguration = {
spaceId: null,
managementToken: null,
topLevelEntryKey: null,
outputDir: './src/content',
outputFile: 'content.json'
}
module.exports = (config) => {
/*** Helpers ***/
/**
* Recursive function to make key value pairs of nested objects
* @param {Object} - Object with keys: 'key', 'value'
* @returns {Object}
*/
const objectifyNestedKeyValues = ({ key, value }) => ({
[key]: Array.isArray(value) ? value.reduce((acc, cur) => ({ ...acc, [cur.key]: cur.value }), {}) : value
})
/**
* Recursive function to make key value pairs of nested objects
* @param {String} locale
* @param {Object} - Contentful entry
* @param {Object} entries - All Contentful entries
* @returns {Object}
*/
const sanitiseContenfulItem = (locale, { fields }, entries) => {
const key = fields.key[locale];
const values = fields.value[locale];
return {
key,
// eslint-disable-next-line no-use-before-define
value: Array.isArray(values) ? values.map(value => (value.sys.type === 'Link' ? getLinkedItem(locale, value, entries) : value)) : values
}
};
/**
* Recursive function to make key value pairs of nested objects
* @param {String} locale
* @param {Object} value - Contentful entry
* @param {Object} entries - All Contentful entries
* @returns {Object}
*/
const getLinkedItem = (locale, value, entries) => {
const { id } = value.sys;
const linkedEntry = entries.find(entry => entry.sys.id === id);
return linkedEntry ? sanitiseContenfulItem(locale, linkedEntry, entries) : {};
}
/**
* Recursive function to make key value pairs of nested objects
* @param {Object} entries - All Contentful entries
* @param {String} topLevelEntryKey - Contentful parent entry key to return
* @returns {Array}
*/
const getPages = (entries, topLevelEntryKey) => entries.filter(entry => entry.sys.contentType.sys.id === topLevelEntryKey)
/**
* Function to return list of locale codes
* @param {Array} locales - All active Contentful locales
* @returns {Array}
*/
const getlocaleCodes = (locales) => locales.map(({ code }) => code);
/**
* Function to return list of locale codes
* @param {String} locale
* @param {Object} pages - All Contentful entries
* @param {Object} entries - All Contentful entries
* @returns {Array}
*/
const generateLocaleObject = (locale, pages, entries) => pages.reduce((acc, cur) => ({ ...acc, ...objectifyNestedKeyValues(sanitiseContenfulItem(locale, cur, entries)) }), {})
/**
* Function to return list of locale codes
* @param {Array} locales - All active Contentful locales
* @param {Array} pages - All Contentful pages
* @param {Object} entries - All Contentful entries
* @returns {Object}
*/
const generateContentObject = (locales, pages, entries) => locales
.reduce((acc, cur) => ({
...acc,
[cur]: generateLocaleObject(cur, pages, entries)
}), {});
/**
* Recursive function to make key value pairs of nested objects
* @param {Object} data - Contentful response
* @param {String} topLevelEntryKey - Contentful parent entry key to return
* @returns {Object} - key-value paired object
*/
const sanitiseContentfulResponse = (data, topLevelEntryKey) => {
const { entries, locales } = data;
const localeCodes = getlocaleCodes(locales);
const pages = getPages(entries, topLevelEntryKey);
return generateContentObject(localeCodes, pages, entries)
}
/*** Configuration ***/
const configuration = { ...defaultConfiguration, ...config }
if (!configuration.spaceId) { throw new Error('No spaceId provided') };
if (!configuration.managementToken) { throw new Error('No managementToken provided') };
if (!configuration.topLevelEntryKey) { throw new Error('No topLevelEntryKey provided') };
const options = {
saveFile: false,
skipEditorInterfaces: true,
skipRoles: true,
skipWebhooks: true,
spaceId: configuration.spaceId,
managementToken: configuration.managementToken
};
return new Promise((resolve, reject) => {
contentfulExport(options)
.then((result) => {
const content = sanitiseContentfulResponse(result, configuration.topLevelEntryKey);
if (!fs.existsSync(configuration.outputDir)) {
fs.mkdirSync(configuration.outputDir, { recursive: true });
}
// eslint-disable-next-line no-magic-numbers
fs.writeFileSync(`${configuration.outputDir}/${configuration.outputFile}`, JSON.stringify(content, null, 2));
resolve()
})
.catch(reject)
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment