Skip to content

Commit

Permalink
refactor: async functions and cache helper for getGqlPossibleTypes
Browse files Browse the repository at this point in the history
  • Loading branch information
emuvente committed Sep 25, 2024
1 parent 6cc890c commit 93eeb7d
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 80 deletions.
133 changes: 60 additions & 73 deletions server/util/getGqlPossibleTypes.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import fetch from './fetch.js';
import { log } from './log.js';
import { trace } from './mockTrace.js';
import { getFromCache, setToCache } from './memJsUtils.js';

const GQL_BUILT_IN_TYPES = [
'__Schema',
Expand All @@ -12,89 +11,77 @@ const GQL_BUILT_IN_TYPES = [
'__Directive'
];

function fetchGqlPossibleTypes(url, cache) {
return fetch(url, {
async function fetchSchema(url) {
const result = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
// eslint-disable-next-line max-len
query: '{ __schema { queryType { name } mutationType { name } subscriptionType { name } types { name fields { name } possibleTypes { name } } } }'
}),
})
.then(result => result.json())
// eslint-disable-next-line no-underscore-dangle
.then(result => result?.data?.__schema ?? {})
.then(({
types, queryType, mutationType, subscriptionType
}) => {
const possibleTypes = { Mergable: [] };
types?.forEach(type => {
// Skip adding possible types for built-in GraphQL types and root types
if (GQL_BUILT_IN_TYPES.includes(type.name)
|| type.name === queryType?.name
|| type.name === mutationType?.name
|| type.name === subscriptionType?.name) {
return;
}
// If this type has possible types, include them in the possibleTypes
// object as { Typename: ['PossibleTypenameA', 'PossibleTypenameB'] }
if (type.possibleTypes && type.possibleTypes.length) {
possibleTypes[type.name] = type.possibleTypes.map(({ name }) => name);
}
// If this type doesn't have an ID field, declare it as mergeable.
// See https://github.com/apollographql/apollo-client/pull/7070#issue-708438002
if (type.fields && type.fields.length && !type.fields.some(field => field.name === 'id')) {
possibleTypes.Mergable.push(type.name);
}
});
});
const data = await result.json();
// eslint-disable-next-line no-underscore-dangle
return data?.data?.__schema ?? {};
}

async function fetchGqlPossibleTypes(url, cache) {
// Get types from schema
const {
types, queryType, mutationType, subscriptionType
} = await fetchSchema(url);

// Construct possible types object
const possibleTypes = { Mergable: [] };
types?.forEach(type => {
// Skip adding possible types for built-in GraphQL types and root types
if (GQL_BUILT_IN_TYPES.includes(type.name)
|| type.name === queryType?.name
|| type.name === mutationType?.name
|| type.name === subscriptionType?.name) {
return;
}
// If this type has possible types, include them in the possibleTypes
// object as { Typename: ['PossibleTypenameA', 'PossibleTypenameB'] }
if (type.possibleTypes && type.possibleTypes.length) {
possibleTypes[type.name] = type.possibleTypes.map(({ name }) => name);
}
// If this type doesn't have an ID field, declare it as mergeable.
// See https://github.com/apollographql/apollo-client/pull/7070#issue-708438002
if (type.fields && type.fields.length && !type.fields.some(field => field.name === 'id')) {
possibleTypes.Mergable.push(type.name);
}
});

const typesJSON = JSON.stringify(possibleTypes);
// Convert the possible types object to JSON
const typesJSON = JSON.stringify(possibleTypes);

// Cache the possible types in the local process
process.env.FETCHED_GQL_TYPES = typesJSON;
// Cache the possible types in the local process
process.env.FETCHED_GQL_TYPES = typesJSON;

// Cache the possible types in memcached for other processes to use
cache.set('ui-gql-possible-types', typesJSON, { expires: 24 * 60 * 60 }, (error, success) => {
if (error) {
log(`MemJS Error Setting Cache for ui-gql-fragment-types, Error: ${error}`, 'error');
}
if (success) {
log(`MemJS Success Setting Cache for ui-gql-fragment-types, Success: ${success}`);
}
});
return possibleTypes;
});
// Cache the possible types in memcached for other processes to use
await setToCache('ui-gql-possible-types', typesJSON, 24 * 60 * 60, cache);

return possibleTypes;
}

function getGqlPossibleTypesFromCache(cache) {
return new Promise(resolve => {
// If the possible types have already been fetched in this process, return them
if (process.env.FETCHED_GQL_TYPES) {
resolve(JSON.parse(process.env.FETCHED_GQL_TYPES));
return;
}
async function getGqlPossibleTypesFromCache(cache) {
// If the possible types have already been fetched in this process, return them
if (process.env.FETCHED_GQL_TYPES) {
return JSON.parse(process.env.FETCHED_GQL_TYPES);
}

// Otherwise, check the cache
cache.get('ui-gql-possible-types', (error, data) => {
let parsedData = [];
if (error) {
log(`MemJS Error Getting ui-gql-fragment-types, Error: ${error}`, 'error');
}
if (data) parsedData = JSON.parse(data);
resolve(parsedData);
});
});
// Otherwise, check the cache
const data = await getFromCache('ui-gql-possible-types', cache);
if (data) {
return JSON.parse(data);
}
}

export default (function getGqlPossibleTypes(url, cache) {
return trace('getGqlFragmentTypes', () => {
return trace('getGqlFragmentsFromCache', () => {
return getGqlPossibleTypesFromCache(cache).then(data => {
if (Object.keys(data).length) {
return data;
}
return trace('fetchGqlFragments', () => fetchGqlPossibleTypes(url, cache));
});
});
});
export default (async function getGqlPossibleTypes(url, cache) {
const data = await getGqlPossibleTypesFromCache(cache);
if (data) {
return data;
}
return fetchGqlPossibleTypes(url, cache);
});
14 changes: 7 additions & 7 deletions server/util/memJsUtils.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { log } from './log.js';
import { log, error } from './log.js';
import { trace } from './mockTrace.js';

export const getFromCache = (key, cache) => {
return trace('getFromCache', { resource: key }, () => {
return new Promise(resolve => {
cache.get(key, (error, data) => {
if (error) {
log(`MemJS Error Getting ${key}, Error: ${error}`, 'error');
cache.get(key, (err, data) => {
if (err) {
error(`MemJS Error Getting ${key}`, { error: err });
}
if (data) {
log(`MemJS Success Getting ${key}`);
Expand All @@ -19,9 +19,9 @@ export const getFromCache = (key, cache) => {
export const setToCache = (key, value, expires, cache) => {
return trace('setToCache', { resource: key }, () => {
return new Promise((resolve, reject) => {
cache.set(key, value, { expires }, (error, success) => {
if (error) {
log(`MemJS Error Setting Cache for ${key}, Error: ${error}`, 'error');
cache.set(key, value, { expires }, (err, success) => {
if (err) {
error(`MemJS Error Setting Cache for ${key}`, { error: err });
reject();
} else {
log(`MemJS Success Setting Cache for ${key}, Success: ${success}`);
Expand Down

0 comments on commit 93eeb7d

Please sign in to comment.