all files / semantic-graphql/src/graphql/ getGraphqlObjectResolver.js

29.51% Statements 18/61
22.86% Branches 8/35
100% Functions 1/1
36.73% Lines 18/49
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105   11×   11×     11×                                                               11× 11× 11×     11×                                             11×                                 11×                   11×      
const { owlInverseOf, _owlInverseOf, rdfsDomain, _rdfsSubClassOf } = require('../constants');
const isNil = require('../utils/isNil');
const castArrayShape = require('../utils/castArrayShape');
const { walkmap } = require('../graph/traversal');
const isGraphqlList = require('./isGraphqlList');
const requireGraphqlRelay = require('../requireGraphqlRelay');
 
function getGraphqlObjectResolver(g, iri, ranges) {
  const isList = isGraphqlList(g, iri);
 
  let inverseOfMap;
 
  // If inverseProperties exists, we can use them to retrieve missing remote data
  Iif (!g[iri].shouldNeverUseInverseOf && (g[iri][owlInverseOf] || g[iri][_owlInverseOf])) {
    const extendedRanges = new Set();
    const inverseProperties = new Set();
 
    if (g[iri][owlInverseOf]) g[iri][owlInverseOf].forEach(inverseProperties.add, inverseProperties);
    if (g[iri][_owlInverseOf]) g[iri][_owlInverseOf].forEach(inverseProperties.add, inverseProperties);
 
    // We want to look for the full extent of the currentProperty's ranges, i.e. include their subClasses
    ranges.forEach(rangeIri => walkmap(g, rangeIri, _rdfsSubClassOf, extendedRanges));
 
    // For each inverseProperty we map the corresponding classes
    // That are both of the currentProperty's extended range and the inverseProperty's extended domain
    inverseOfMap = new Map();
 
    inverseProperties.forEach(propertyIri => {
      if (!g[propertyIri][rdfsDomain]) return;
 
      const admitingRanges = [];
      const extendedDomains = new Set();
 
      // Find the extended domain of the inverseProperty
      g[propertyIri][rdfsDomain].forEach(domainIri => walkmap(g, domainIri, _rdfsSubClassOf, extendedDomains));
 
      // And among those domain, keep only those accepted as a range of the currentProperty
      extendedDomains.forEach(domainIri => {
        if (extendedRanges.has(domainIri)) admitingRanges.push(domainIri);
      });
 
      inverseOfMap.set(propertyIri, admitingRanges);
    });
  }
 
  const hasNoInverseOf = !(inverseOfMap && inverseOfMap.size);
  const resolveNothing = () => isList ? [] : null;
  const resolveResource = isList ? g.resolvers.resolveResources : g.resolvers.resolveResource;
 
  // A resolver for inverseOf properties
  const inverseOfResolver = (source, args, context, info) => {
    if (hasNoInverseOf) return resolveNothing();
 
    return Promise.resolve(g.resolvers.resolveSourceId(source, context, info))
    .then(sourceId => {
 
      const promises = [];
 
      inverseOfMap.forEach((admitingRanges, propertyIri) => {
        promises.push(g.resolvers.resolveResourcesByPredicate(admitingRanges, propertyIri, sourceId, context, info));
      });
 
      return Promise.all(promises)
      .then(results => {
        const finalResult = results.reduce((a, b) => a.concat(b), []);
 
        return isList ? finalResult : finalResult[0];
      });
    });
  };
 
  // XXX: put outside of scope to avoid re-allocation ?
  // The actual resolve function
  const resolver = (source, args, context, info) => {
    if (g[iri].shouldAlwaysUseInverseOf) return inverseOfResolver(source, args, context, info);
 
    return Promise.resolve(g.resolvers.resolveSourcePropertyValue(source, iri, context, info))
    .then(ref => {
      // A reference to data was resolved, we resolve the underlying resources
      // NOTE: this does not aggregate both direct and inverse data
      if (!isNil(ref)) return resolveResource(castArrayShape(ref, isList), context, info);
 
      // No reference to data was resolved, maybe the data is on an inverse Property
      if (!g[iri].shouldNeverUseInverseOf) return inverseOfResolver(source, args, context, info);
 
      // Give up
      return resolveNothing();
    });
  };
 
  Iif (g.config.relay && g[iri].isRelayConnection) {
    const { connectionFromArray, connectionFromPromisedArray } = requireGraphqlRelay();
 
    return (source, args, context, info) => {
      const results = resolver(source, args, context, info);
 
      return (Array.isArray(results) ? connectionFromArray : connectionFromPromisedArray)(results, args);
    };
  }
 
  return resolver;
}
 
module.exports = getGraphqlObjectResolver;