Source: utils/rdf.js

// @ts-nocheck
import { Parser, Store, DataFactory } from 'n3';
import assert from 'assert';
import { fromJsonldjsNode } from './claimgraph';

/**
 * Converts a a JSON-LD RDF object to N3 data type
 * @param {object} node - JSON-LD RDF representation object
 * @returns {any}
 */
export function toJsonldjsNode(node) {
  if (node.DefaultGraph) {
    return DataFactory.defaultGraph();
  } else if (node.Iri) {
    return DataFactory.namedNode(node.Iri);
  } else if (node.Blank) {
    return DataFactory.blankNode(node.Blank);
  } else if (node.Literal) {
    return DataFactory.literal(node.Literal.value, {
      value: node.Literal.datatype,
      language: node.Literal.language,
    });
  }

  throw new Error(`Unable to determine node type: ${node}`);
}

/**
 * Converts a claimgraph JSON representation into an N3 RDF store
 * @param {object} claimgraph - JSON-LD RDF object
 * @returns {Store}
 */
export function claimgraphToStore(claimgraph) {
  const store = new Store();
  claimgraph.forEach((quad) => {
    const subject = quad[0];
    const predicate = quad[1];
    const object = quad[2];
    const graph = quad.length > 3 ? toJsonldjsNode(quad[3]) : undefined;
    store.addQuad(DataFactory.quad(
      toJsonldjsNode(subject),
      toJsonldjsNode(predicate),
      toJsonldjsNode(object),
      graph,
    ));
  });
  return store;
}

/**
 * Queries a claimgraph object and returns all the bindings to the variable named `?lookupNext`
 * @param {array<object>} claimgraph - A list of RDF quads
 * @param {string} query - SPARQL query string
 * @param {object} engine - RDF query engine
 * @returns {array<any>}
 */
export async function queryNextLookup(claimgraph, query, engine) {
  if (!engine) {
    throw new Error('queryNextLookup requires an RDF query engine');
  }

  // Create an N3 store from claimgraph JSON object
  const store = claimgraphToStore(claimgraph);

  // Query the engine, if querying multiple times user should
  // pass engine parameter for optimal performance
  const result = await engine.query(query, { sources: [store] });

  // Get bindings from query
  const bindings = await result.bindings();

  // Convert bindings to claimgraph format using lookupNext variable
  bindings.forEach((b) => assert(
    b.get('?lookupNext') !== undefined,
    "Query for next lookup must always bind '?lookupNext'",
  ));
  return bindings.map((binding) => fromJsonldjsNode(binding.get('?lookupNext')));
}

/**
 * Parses an RDF document string and formats it according to rify requirements
 * @param {string} document - RDF document in turtle or other form
 * @param {object} parserOptions - N3 Parser configuration object
 * @returns {*}
 */
export function parseRDFDocument(document, parserOptions = {}) {
  const parser = new Parser(parserOptions);

  // Parse document using N3, should return a list of quads
  const parsedResult = parser.parse(document);

  // Format document to format SDK expects which is a
  // list of triples as quads are assumed to have DefaultGraph
  return parsedResult.map((quad) => {
    const {
      subject,
      predicate,
      object,
      graph,
    } = quad;

    // Reject if graph isnt default as rify doesnt support it otherwise
    if (graph.termType !== 'DefaultGraph') {
      throw new Error(`Unexpected graph, expecting DefaultGraph: ${JSON.stringify(graph.toJSON())}`);
    }

    // Format subject, predicate and object terms into rify standard
    const formattedSubject = fromJsonldjsNode(subject);
    const formattedPredicate = fromJsonldjsNode(predicate);
    const formattedObject = fromJsonldjsNode(object);

    // Format result as RDF triple
    return [
      formattedSubject,
      formattedPredicate,
      formattedObject,
    ];
  });
}