1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-10-13 22:41:57 +00:00

4.0.0-rc13.4

* Fixed: Game directory pages not loading.
* Changed: Rewrite GraphQL query merging logic to hopefully perform in a more robust manner less likely to break in the future.
This commit is contained in:
SirStendec 2018-10-16 15:12:47 -04:00
parent 7fc27ff025
commit 551a08bfd0
12 changed files with 158 additions and 186 deletions

View file

@ -6,10 +6,11 @@
// ============================================================================
import Module from 'utilities/module';
import {has, get} from 'utilities/object';
import {get, deep_copy} from 'utilities/object';
import merge from 'utilities/graphql';
const BAD_ERRORS = [
/*const BAD_ERRORS = [
'timeout',
'unable to load',
'error internal',
@ -31,7 +32,7 @@ function skip_error(err) {
for(const m of BAD_ERRORS)
if ( err.message.includes(m) )
return true;
}
}*/
export class GQLError extends Error {
@ -212,6 +213,9 @@ export default class Apollo extends Module {
query = query_map && query_map.get(id),
modifiers = this.modifiers[operation];
const pre_modification = deep_copy(request.query);
if ( modifiers ) {
for(const mod of modifiers) {
if ( typeof mod === 'function' )
@ -245,6 +249,8 @@ export default class Apollo extends Module {
this.log.info('Unable to find GQL Print. Clearing store for query:', operation);
this.client.queryManager.queryStore.store[id] = null;
}
this.log.info('Query', operation, pre_modification, request.query, request.variables);
}
apolloPostFlight(response) {
@ -406,71 +412,3 @@ export default class Apollo extends Module {
}
}
// ============================================================================
// Query Merging
// ============================================================================
function canMerge(a, b) {
return a.kind === b.kind &&
a.kind !== 'FragmentDefinition' &&
(a.selectionSet == null) === (b.selectionSet == null);
}
function merge(a, b) {
if ( ! canMerge(a, b) )
return a;
if ( a.definitions ) {
const a_def = a.definitions,
b_def = b.definitions;
for(let i=0; i < a_def.length && i < b_def.length; i++)
a_def[i] = merge(a_def[i], b_def[i]);
}
if ( a.selectionSet ) {
const s = a.selectionSet.selections,
selects = {};
for(const sel of b.selectionSet.selections) {
const name = sel.kind === 'InlineFragment' ?
(sel.typeCondition.name ?
sel.typeCondition.name.value : null) :
(sel.name ? sel.name.value : null),
alias = sel.alias ? sel.alias.value : null,
key = `${name}:${alias}`;
if ( name )
selects[key] = sel;
}
for(let i=0, l = s.length; i < l; i++) {
const sel = s[i],
name = sel.kind === 'InlineFragment' ?
(sel.typeCondition.name ?
sel.typeCondition.name.value : null) :
(sel.name ? sel.name.value : null),
alias = sel.alias ? sel.alias.value : null,
key = `${name}:${alias}`,
other = selects[key];
if ( other ) {
s[i] = merge(sel, other);
selects[key] = null;
}
}
for(const key in selects)
if ( has(selects, key) ) {
const val = selects[key];
if ( val )
s.push(val);
}
}
// TODO: Variables?
return a;
}

113
src/utilities/graphql.js Normal file
View file

@ -0,0 +1,113 @@
import { deep_copy } from "./object";
'use strict';
// ============================================================================
// GraphQL Document Manipulation
// ============================================================================
export const MERGE_METHODS = {
Document: (a, b) => {
if ( a.definitions && b.definitions )
a.definitions = mergeList(a.definitions, b.definitions);
else if ( b.definitions )
a.definitions = b.definitions;
return a;
},
Field: (a, b) => {
if ( a.name && (! b.name || b.name.value !== a.name.value) )
return a;
// TODO: arguments
// TODO: directives
if ( a.selectionSet && b.selectionSet )
a.selectionSet = merge(a.selectionSet, b.selectionSet);
else if ( b.selectionSet )
a.selectionSet = b.selectionSet;
return a;
},
OperationDefinition: (a, b) => {
if ( a.operation !== b.operation )
return a;
// TODO: variableDefinitions
// TODO: directives
if ( a.selectionSet && b.selectionSet )
a.selectionSet = merge(a.selectionSet, b.selectionSet);
else if ( b.selectionSet )
a.selectionSet = b.selectionSet;
return a;
},
FragmentDefinition: (a, b) => {
if ( a.typeCondition && b.typeCondition ) {
if ( a.typeCondition.kind !== b.typeCondition.kind )
return a;
if ( a.typeCondition.name.value != b.typeCondition.name.value )
return a;
}
// TODO: directives
if ( a.selectionSet && b.selectionSet )
a.selectionSet = merge(a.selectionSet, b.selectionSet);
else if ( b.selectionSet )
a.selectionSet = b.selectionSet;
return a;
},
SelectionSet: (a, b) => {
if ( a.selections && b.selections )
a.selections = mergeList(a.selections, b.selections);
else if ( b.selections )
a.selections = b.selections;
return a;
}
}
export function mergeList(a, b) {
const a_names = {};
for(const item of a) {
if ( ! item || ! item.name || item.name.kind !== 'Name' )
continue;
a_names[item.name.value] = item;
}
for(const item of b) {
if ( ! item || ! item.name || item.name.kind !== 'Name' )
continue;
const name = item.name.value,
idx = a_names[name] ? a.indexOf(a_names[name]) : -1;
if ( idx !== -1 )
a[idx] = merge(a[idx], item);
else
a.push(item);
}
return a;
}
export default function merge(a, b) {
if ( a.kind !== b.kind )
return a;
if ( MERGE_METHODS[a.kind] )
return MERGE_METHODS[a.kind](a, b);
return a;
}