UNPKG

apollo-link-contentful

Version:

Perform GraphQL queries against Contentful’s Rest API. No more, query size limits! No more, query complexities!!

254 lines (218 loc) • 9.59 kB
import _defineProperty from "@babel/runtime/helpers/defineProperty"; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } import { omit } from 'lomit'; import { DefinitionKind, SelectionKind, VariableKind } from './constants'; /** * Return root query object to work against based on if an operationName key * is set in operation. * * @param {Operation} operation * @return {Object} */ var getRootQuery = function getRootQuery(operation) { var query = operation.query, operationName = operation.operationName; return query && query.hasOwnProperty(operationName) ? query[operationName] : query; }; /** * Return the root key that will contain the data returned via the GraphQL query. * * @param {Operation} operation * @return {string} */ export var getRootKey = function getRootKey(operation) { var definition = getRootQuery(operation).definitions.find(function (definition) { return definition.operation === 'query'; }); if (definition) { var selection = definition.selectionSet.selections.find(function (selection) { return selection.name.kind === 'Name'; }); if (selection) { return selection.name.value; } } return null; }; /** * Contentful API - Search Parameters * @ref https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/search-parameters */ var contentfulReservedParameters = ['access_token', 'id', 'include', 'locale', 'content_type', 'select', 'query', 'links_to_entry', 'links_to_asset', 'order', 'limit', 'skip', 'mimetype_group', 'preview']; /** * Pass in a GraphQL query variable field and get the equivalent * Contentful REST API query argument. * * @ref https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/search-parameters/search-on-references * * @param {string} variableKey * @return {string} */ var getSearchKey = function getSearchKey(variableKey) { if (variableKey.endsWith('_in')) { return "fields." + variableKey.replace('_in', '') + "[in]"; } if (variableKey.endsWith('_not')) { return "fields." + variableKey.replace('_not', '') + "[ne]"; } if (variableKey.endsWith('_exists')) { return "fields." + variableKey.replace('_exists', '') + "[exists]"; } if (variableKey.endsWith('_not_in')) { return "fields." + variableKey.replace('_not_in', '') + "[nin]"; } // if (variableKey.endsWith('_contains')) { // return `fields.${variableKey.replace('_contains', '')}[?]` // } // if (variableKey.endsWith('_not_contains')) { // return `fields.${variableKey.replace('_not_contains', '')}[?]` // } // @todo Add support for `x[all]` - Ryan // @todo Add support for `x[match]` - Ryan // @todo Add support for `x[gt]` - Ryan // @todo Add support for `x[gte]` - Ryan // @todo Add support for `x[lt]` - Ryan // @todo Add support for `x[lte]` - Ryan // @todo Add support for `x[near]` - Ryan // @todo Add support for `x[within]` - Ryan return "fields." + variableKey; }; /** * Build a map that associates the variables passed into the query * with the actual fields/arguments that are being requested in the * API request. * * @param {Object} operation * @return {Object} */ var buildVariableMap = function buildVariableMap(operation) { var variableMap = {}; getRootQuery(operation).definitions.filter(function (definition) { return definition.kind === DefinitionKind.OperationDefinition; }).forEach(function (definition) { definition.selectionSet.selections.forEach(function (selection) { selection.arguments.forEach(function (argument) { if (argument.value && argument.value.fields) { argument.value.fields.forEach(function (field) { var validField = field.value && field.value.name && field.value.name.value && field.kind && field.name && field.name.value; if (validField) { variableMap[field.value.name.value] = { kind: field.kind, field: field.name.value }; } }); } else { var validArgument = argument.value && argument.value.name && argument.value.name.value && argument.kind && argument.name && argument.name.value; if (validArgument) { variableMap[argument.value.name.value] = { kind: argument.kind, field: argument.name.value }; } } }); }); }); return variableMap; }; /** * GraphQL queries specify the expected shape of the response from the API, * so this method attempts to extract that in a format that we can apply * to the parsed REST API response, in order to make sure the response * fulfills the contract of the request. * * @param {Array.<Object>} selectionSet * @param {Array.<Object>} definitions * @return {Object} */ var extractSelections = function extractSelections(selectionSet, definitions) { if (!(selectionSet && selectionSet.selections && selectionSet.selections.length)) return null; var selections = {}; selectionSet.selections.forEach(function (selection) { if (selection.kind === SelectionKind.Field) { if (selection.name && selection.name.value) { selections[selection.name.value] = extractSelections(selection.selectionSet, definitions); } } else if (selection.kind === SelectionKind.FragmentSpread) { var fragmentDefinition = definitions.find(function (definition) { return definition.kind === DefinitionKind.FramentDefinition && definition.name && definition.name.value && selection.name && selection.name.value && definition.name.value === selection.name.value; }); if (fragmentDefinition) { selections["..." + selection.name.value] = extractSelections(fragmentDefinition.selectionSet, definitions); } } }); return selections; }; /** * * * @param {Object} operation * @return {Object} */ export var buildDefinitionMap = function buildDefinitionMap(operation) { var _ref; var query = operation.query; var operationDefinition = query.definitions.find(function (definition) { return definition.kind === DefinitionKind.OperationDefinition; }); if (!(operationDefinition && operationDefinition.name && operationDefinition.name.value)) { return {}; } return _ref = {}, _ref[operationDefinition.name.value] = extractSelections(operationDefinition.selectionSet, query.definitions), _ref; }; /** * Convert the query variables to a format the Contentful API understands * * @param {Object} query * @param {Object} variables * @param {Object} variableMap * @param {string} operationName * @retrun {Object} */ export var parseQueryVariables = function parseQueryVariables(operation) { var variables = operation.variables; var variableMap = buildVariableMap(operation); var operationVariables = getRootQuery(operation).definitions.find(function (definition) { return definition.kind === DefinitionKind.OperationDefinition; }).variableDefinitions.map(function (variableDefinition) { return variableDefinition.variable.name.value; }); var operationQueries = Object.keys(variableMap).filter(function (variableKey) { return variables && variables.hasOwnProperty(variableKey) && variableMap && variableMap.hasOwnProperty(variableKey); }).map(function (variableKey) { var _ref3; try { switch (variableMap[variableKey].kind) { case VariableKind.Argument: // If the variable key is known search parameter for the Contentful API, // just pass it through un-parsed var fieldExists = variableMap[variableKey] && variableMap[variableKey].field; if (fieldExists && contentfulReservedParameters.includes(variableMap[variableKey].field)) { var _ref2; // Exclude preview variable is not set to true if (variableMap[variableKey].field === 'preview' && !variables[variableKey]) { return null; } return _ref2 = {}, _ref2[variableMap[variableKey].field] = variables[variableKey], _ref2; } break; case VariableKind.ObjectField: // Convert variable into query format supported in Contentful API var queryKey = getSearchKey(variableMap[variableKey].field); return _ref3 = {}, _ref3[queryKey] = variables[variableKey], _ref3; default: return null; } } catch (err) { console.error(err); return null; } }).filter(function (variable) { return !!variable; }).reduce(function (acc, cur) { return _objectSpread(_objectSpread({}, acc), cur); }, {}); return _objectSpread(_objectSpread({}, operationQueries), omit(variables, operationVariables)); };