UNPKG

jsonpath-tmf-query

Version:

This library aims to provide a simple wrapper around jsonpath, to ease the implementation of TMF630 JSONPath specification as outlined by the TM Forum [here](https://projects.tmforum.org/wiki/pages/viewpage.action?spaceKey=PUB&title=TMF630+REST+API+Design

187 lines (180 loc) 6.11 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); const jsonpathPlus = require('jsonpath-plus'); const lodash = require('lodash'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; } const lodash__default = /*#__PURE__*/_interopDefaultCompat(lodash); const { get, set } = lodash__default; const sortOperations = (ops) => ops.sort((opA, opB) => { const operationOrder = ["sort", "filter", "fields"]; function getOpIndex(operation) { return operationOrder.indexOf(operation.op); } return getOpIndex(opA) - getOpIndex(opB); }); const removeEmpty = (obj) => { Object.keys(obj).forEach((key) => { const value = obj[key]; if (value == null) { delete obj[key]; } else if (typeof value === "object") { removeEmpty(value); if (!Object.keys(value).length) { delete obj[key]; } if (Array.isArray(value)) { for (let i = value.length - 1; i >= 0; i -= 1) { if (!value[i]) { value.splice(i, 1); } } } } }); }; const checkValidJsonPath = (jsonpathExpression) => { if (typeof jsonpathExpression !== "string") return false; try { const query = jsonpathPlus.JSONPath.toPathArray(jsonpathExpression); return !query.some((element) => { if (element === "") { throw new Error(); } }); } catch (_e) { try { const query = jsonpathPlus.JSONPath.toPathArray(`$${jsonpathExpression}`); return !query.some((element) => { if (element === "") { return true; } }); } catch (_e2) { return false; } } }; class JSONPathQuery { static query(document, operations) { const rootIsArray = Array.isArray(document); const mutatedDocument = JSON.parse(JSON.stringify(document)); const hasFields = operations.some((operation) => operation.op === "fields"); const hasFilter = operations.some((operation) => operation.op === "filter"); let hasSort = operations.some((operation) => operation.op === "sort"); let newDocument; let filteredDocument = rootIsArray ? [] : {}; if (hasFilter || rootIsArray) newDocument = []; else newDocument = {}; const pathPrefix = rootIsArray || hasFilter ? "$[*]." : "$."; if (hasFields) { operations.push({ op: "fields", path: `${pathPrefix}id` }); operations.push({ op: "fields", path: `${pathPrefix}href` }); } const sortedOperations = sortOperations(operations); sortedOperations.forEach((operation, index) => { hasSort = operations.slice(index).some((remainingOp) => remainingOp.op === "sort"); let paths; if (operation.path.startsWith("$") === false && operation.path !== "none") { if (rootIsArray) operation.path = `$${operation.path}`; else operation.path = `$.${operation.path}`; } if (hasSort) { paths = jsonpathPlus.JSONPath({ path: operation.path, json: mutatedDocument, resultType: "path" }); } else if (hasFilter) { paths = jsonpathPlus.JSONPath({ path: operation.path, json: filteredDocument, resultType: "path" }); } else { paths = jsonpathPlus.JSONPath({ path: operation.path, json: mutatedDocument, resultType: "path" }); } switch (operation.op) { case "filter": filteredDocument = jsonpathPlus.JSONPath({ path: operation.path, json: mutatedDocument }); if (operation.limit && operation.offset) { filteredDocument = filteredDocument.slice(operation.offset, operation.offset + operation.limit); } else if (operation.limit) { filteredDocument = filteredDocument.slice(0, operation.limit); } else if (operation.offset) { filteredDocument = filteredDocument.slice(operation.offset); } if (!hasFields) newDocument = filteredDocument; break; case "fields": if (hasFilter) { paths.forEach((_path) => { const path = jsonpathPlus.JSONPath.toPathArray(_path).filter((p) => p !== "$"); const element = get(filteredDocument, path); set(newDocument, path, element); }); } else { paths.forEach((_path) => { const path = jsonpathPlus.JSONPath.toPathArray(_path).filter((p) => p !== "$"); const element = get(mutatedDocument, path); set(newDocument, path, element); }); } break; case "sort": let jsonPathQuery = jsonpathPlus.JSONPath.toPathArray(operation.path); const sortParam = jsonPathQuery[jsonPathQuery.length - 1]; jsonPathQuery = jsonPathQuery.slice(0, -2); const stringPath = jsonpathPlus.JSONPath.toPathString(jsonPathQuery); const arrayOfNodes = jsonpathPlus.JSONPath({ path: stringPath, json: mutatedDocument }); arrayOfNodes[0].sort((valueA, valueB) => { if (operation.order === "asc") { if (valueA[`${sortParam}`] < valueB[`${sortParam}`]) { return -1; } if (valueA[`${sortParam}`] > valueB[`${sortParam}`]) { return 1; } } else { if (valueB[`${sortParam}`] < valueA[`${sortParam}`]) { return -1; } if (valueB[`${sortParam}`] > valueA[`${sortParam}`]) { return 1; } } return 0; }); break; } }); if (hasSort && hasFields === false && hasFilter === false) { return mutatedDocument; } removeEmpty(newDocument); return newDocument; } } exports.checkValidJsonPath = checkValidJsonPath; exports.default = JSONPathQuery;