mirror of
https://github.com/LukeHagar/developer.sailpoint.com.git
synced 2025-12-09 20:37:47 +00:00
Add json path evaluator
This commit is contained in:
@@ -25,6 +25,14 @@ module.exports = {
|
||||
{label: 'NERM', to: '/docs/api/nerm/v1'},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'dropdown',
|
||||
label: 'Tools',
|
||||
position: 'left',
|
||||
items: [
|
||||
{label: 'Json Path Evaluator', to: '/util/json-path-evaluator'},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'dropdown',
|
||||
label: 'Community',
|
||||
|
||||
99
package-lock.json
generated
99
package-lock.json
generated
@@ -18,6 +18,7 @@
|
||||
"@gracefullight/docusaurus-plugin-microsoft-clarity": "^1.0.0",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"@typeform/embed-react": "^1.21.0",
|
||||
"ace-builds": "^1.36.4",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"classnames": "^2.3.2",
|
||||
"clsx": "^2.0.0",
|
||||
@@ -25,9 +26,12 @@
|
||||
"docusaurus-theme-openapi-docs": "^4.0.1",
|
||||
"docusaurus2-dotenv": "^1.4.0",
|
||||
"esbuild-loader": "^2.20.0",
|
||||
"jsonpath-plus": "^10.1.0",
|
||||
"jsonpathly": "^2.0.1",
|
||||
"ldrs": "^1.0.1",
|
||||
"prism-react-renderer": "^2.3.0",
|
||||
"react": "^18.2.0",
|
||||
"react-ace": "^13.0.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-live": "^4.0.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
@@ -3879,6 +3883,28 @@
|
||||
"resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
|
||||
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg=="
|
||||
},
|
||||
"node_modules/@jsep-plugin/assignment": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz",
|
||||
"integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==",
|
||||
"engines": {
|
||||
"node": ">= 10.16.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"jsep": "^0.4.0||^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jsep-plugin/regex": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz",
|
||||
"integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==",
|
||||
"engines": {
|
||||
"node": ">= 10.16.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"jsep": "^0.4.0||^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@leichtgewicht/ip-codec": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
|
||||
@@ -5032,6 +5058,11 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/ace-builds": {
|
||||
"version": "1.36.4",
|
||||
"resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.36.4.tgz",
|
||||
"integrity": "sha512-eE+iAsLRfNsq30yd34cezKSob6/N9mQatWs44Bp5LUDgKZ3rJtQds/YtcbnwbEWMTe7yCIxG/Cfezd4BsKIiFg=="
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.14.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
||||
@@ -5278,6 +5309,14 @@
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/antlr4": {
|
||||
"version": "4.13.2",
|
||||
"resolved": "https://registry.npmjs.org/antlr4/-/antlr4-4.13.2.tgz",
|
||||
"integrity": "sha512-QiVbZhyy4xAZ17UPEuG3YTOt8ZaoeOR1CvEAqrEsDBsOqINslaB147i9xqljZqoyf5S+EUlGStaj+t22LT9MOg==",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/any-promise": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
|
||||
@@ -7725,6 +7764,11 @@
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/diff-match-patch": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz",
|
||||
"integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw=="
|
||||
},
|
||||
"node_modules/diffie-hellman": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
|
||||
@@ -10941,6 +10985,14 @@
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/jsep": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz",
|
||||
"integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==",
|
||||
"engines": {
|
||||
"node": ">= 10.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jsesc": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
|
||||
@@ -11018,6 +11070,32 @@
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonpath-plus": {
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.1.0.tgz",
|
||||
"integrity": "sha512-gHfV1IYqH8uJHYVTs8BJX1XKy2/rR93+f8QQi0xhx95aCiXn1ettYAd5T+7FU6wfqyDoX/wy0pm/fL3jOKJ9Lg==",
|
||||
"dependencies": {
|
||||
"@jsep-plugin/assignment": "^1.2.1",
|
||||
"@jsep-plugin/regex": "^1.0.3",
|
||||
"jsep": "^1.3.9"
|
||||
},
|
||||
"bin": {
|
||||
"jsonpath": "bin/jsonpath-cli.js",
|
||||
"jsonpath-plus": "bin/jsonpath-cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonpathly": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/jsonpathly/-/jsonpathly-2.0.1.tgz",
|
||||
"integrity": "sha512-vme/uKIxIwwttk8w1HOM9pLzMSz8Kwm8QaoKNEvZtUI8rqwdfccHaS8eggGr0DsK++W5PBAF58X31IUCZgtRLA==",
|
||||
"dependencies": {
|
||||
"antlr4": "^4.13.1",
|
||||
"fast-deep-equal": "^3.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/katex": {
|
||||
"version": "0.16.11",
|
||||
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.11.tgz",
|
||||
@@ -11191,6 +11269,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
|
||||
},
|
||||
"node_modules/lodash.get": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
|
||||
"integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="
|
||||
},
|
||||
"node_modules/lodash.isequal": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||
@@ -21557,6 +21640,22 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-ace": {
|
||||
"version": "13.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-ace/-/react-ace-13.0.0.tgz",
|
||||
"integrity": "sha512-PPk2O/ArHzDtbnK82QImfHYXwuiitRgHJf5AxwMQh9zciojbWsPmKJm1tMgWOYLCtGEz8/Dh3MxRxrXe7QcstQ==",
|
||||
"dependencies": {
|
||||
"ace-builds": "^1.36.3",
|
||||
"diff-match-patch": "^1.0.5",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dev-utils": {
|
||||
"version": "12.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"@gracefullight/docusaurus-plugin-microsoft-clarity": "^1.0.0",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"@typeform/embed-react": "^1.21.0",
|
||||
"ace-builds": "^1.36.4",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"classnames": "^2.3.2",
|
||||
"clsx": "^2.0.0",
|
||||
@@ -38,9 +39,12 @@
|
||||
"docusaurus-theme-openapi-docs": "^4.0.1",
|
||||
"docusaurus2-dotenv": "^1.4.0",
|
||||
"esbuild-loader": "^2.20.0",
|
||||
"jsonpath-plus": "^10.1.0",
|
||||
"jsonpathly": "^2.0.1",
|
||||
"ldrs": "^1.0.1",
|
||||
"prism-react-renderer": "^2.3.0",
|
||||
"react": "^18.2.0",
|
||||
"react-ace": "^13.0.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-live": "^4.0.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
|
||||
71
src/pages/util/Expressions.js
Normal file
71
src/pages/util/Expressions.js
Normal file
@@ -0,0 +1,71 @@
|
||||
import React from 'react';
|
||||
|
||||
function JsonExpressions() {
|
||||
return (
|
||||
<div className="collapse" id="jsonPathExpressions">
|
||||
<div className="card card-body">
|
||||
<table className="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>JSONPath</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>$</td>
|
||||
<td>the root object/element</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>@</td>
|
||||
<td>the current object/element</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>. or []</td>
|
||||
<td>child operator</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>..</td>
|
||||
<td>recursive descent. JSONPath borrows this syntax from E4X.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>*</td>
|
||||
<td>wildcard. All objects/elements regardless their names.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[]</td>
|
||||
<td>
|
||||
subscript operator. XPath uses it to iterate over element collections and for predicates. In
|
||||
Javascript and JSON it is the native array operator.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[,]</td>
|
||||
<td>
|
||||
Union operator in XPath results in a combination of node sets. JSONPath allows alternate
|
||||
names or array indices as a set.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[start:end:step]</td>
|
||||
<td>array slice operator borrowed from ES4.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>?()</td>
|
||||
<td>applies a filter (script) expression.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>()</td>
|
||||
<td>script expression, using the underlying script engine.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<a href="https://goessner.net/articles/JsonPath/index.html#e2">
|
||||
See: JSONPath expressions - https://goessner.net/
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default JsonExpressions;
|
||||
181
src/pages/util/json-path-evaluator.js
Normal file
181
src/pages/util/json-path-evaluator.js
Normal file
@@ -0,0 +1,181 @@
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import Link from '@docusaurus/Link';
|
||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||
import Layout from '@theme/Layout';
|
||||
import BlogBanner from '../components/blog/BlogBanner';
|
||||
|
||||
import styles from './json-path.module.css';
|
||||
import useBaseUrl from '@docusaurus/useBaseUrl';
|
||||
|
||||
import './json-path.module.css';
|
||||
|
||||
import * as jp from "jsonpathly";
|
||||
|
||||
import JsonExpressions from './Expressions';
|
||||
import Sample from './sample.json';
|
||||
|
||||
import AceEditor from 'react-ace';
|
||||
import 'ace-builds/src-noconflict/mode-json';
|
||||
import 'ace-builds/src-noconflict/theme-solarized_dark';
|
||||
import 'ace-builds/src-noconflict/ext-language_tools.js';
|
||||
|
||||
// Ensure `ace` is defined before accessing its configuration
|
||||
if (typeof ace !== 'undefined' && ace.config) {
|
||||
ace.config.setModuleUrl(
|
||||
'ace/mode/json_worker',
|
||||
new URL('ace-builds/src-noconflict/worker-json.js', 'https://ajaxorg.github.io/').toString()
|
||||
);
|
||||
}
|
||||
|
||||
export default function JsonPathEvaluator() {
|
||||
|
||||
const [inputJson, setInputJson] = React.useState(JSON.stringify(Sample, null, 4));
|
||||
const [result, setResult] = React.useState(JSON.stringify([], null, 4));
|
||||
const [resultType, setResultType] = React.useState<'value' | 'path'>('value');
|
||||
const [query, setQuery] = React.useState('$.requestedItemsStatus[?(@.name in ["Engineering Access"])]');
|
||||
const [isQueryValid, setQueryValid] = React.useState(true);
|
||||
const [queryParseError, setQueryParseError] = React.useState('');
|
||||
|
||||
const queryInput = React.useRef<HTMLInputElement>(null);
|
||||
|
||||
function onChangeJson(input) {
|
||||
setInputJson(input);
|
||||
}
|
||||
|
||||
function onInputQuery(event) {
|
||||
const inputQuery = event.target.value;
|
||||
setQuery(inputQuery);
|
||||
}
|
||||
|
||||
function onChangeResultType(event) {
|
||||
const type = event.target.checked ? 'path' : 'value';
|
||||
setResultType(type);
|
||||
}
|
||||
|
||||
function applyJsonPath(jsonStr, jsonPath) {
|
||||
let json = '';
|
||||
let result = '';
|
||||
|
||||
try {
|
||||
json = JSON.parse(jsonStr.replace(/(\r\n|\n|\r)/gm, ''));
|
||||
} catch (error) {
|
||||
setResult('JSON Parse Error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
result = jp.query(json, jsonPath);
|
||||
setQueryValid(true);
|
||||
setQueryParseError('');
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
setQueryValid(false);
|
||||
if (error instanceof Error) {
|
||||
setQueryParseError(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(result)
|
||||
|
||||
if (0 < result?.length) {
|
||||
setResult(JSON.stringify(result, undefined, 2));
|
||||
} else {
|
||||
setResult('No match');
|
||||
}
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
applyJsonPath(inputJson, query);
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
queryInput.current?.focus();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="container-fluid" style={{margin: 50 + 'px'}}>
|
||||
<div className="form-floating">
|
||||
<input
|
||||
style={{width: 500 + 'px', height: 25 + 'px'}}
|
||||
id="jsonpath-query"
|
||||
type="text"
|
||||
className={"form-control " + (isQueryValid ? "" : "is-invalid")}
|
||||
placeholder="Put JSONPath syntax"
|
||||
value={query}
|
||||
onInput={onInputQuery}
|
||||
ref={queryInput}
|
||||
/>
|
||||
<label htmlFor="jsonpath-query">
|
||||
JSONPath
|
||||
</label>
|
||||
<div className="invalid-feedback">
|
||||
{queryParseError}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row py-2">
|
||||
<div className="col-auto">
|
||||
<div className="form-check form-switch mt-1">
|
||||
<input
|
||||
id="path-switch"
|
||||
type="checkbox"
|
||||
className="form-check-input"
|
||||
onChange={onChangeResultType}
|
||||
/>
|
||||
<label
|
||||
htmlFor="path-switch"
|
||||
className="form-check-label"
|
||||
>
|
||||
Output paths
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-auto">
|
||||
<button
|
||||
className="btn btn-link btn-sm"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#jsonPathExpressions"
|
||||
aria-expanded="false"
|
||||
aria-controls="collapseExample"
|
||||
>
|
||||
Expand JSONPath expressions
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* <JsonExpressions /> */}
|
||||
|
||||
<div className="row row-cols-1 row-cols-md-2">
|
||||
<div className="col">
|
||||
<h2>Inputs</h2>
|
||||
<AceEditor
|
||||
className="editor"
|
||||
mode="json"
|
||||
theme="solarized_dark"
|
||||
onChange={onChangeJson}
|
||||
name="editor"
|
||||
width="auto"
|
||||
editorProps={{ $blockScrolling: true }}
|
||||
value={inputJson}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col">
|
||||
<h2>Evaluation Results</h2>
|
||||
<AceEditor
|
||||
mode="json"
|
||||
theme="solarized_dark"
|
||||
name="editor"
|
||||
width="auto"
|
||||
editorProps={{ $blockScrolling: true }}
|
||||
value={result}
|
||||
readOnly={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
8
src/pages/util/json-path.module.css
Normal file
8
src/pages/util/json-path.module.css
Normal file
@@ -0,0 +1,8 @@
|
||||
.editor {
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
#jsonpath-query {
|
||||
width: 500px;
|
||||
height: 2em;
|
||||
}
|
||||
38
src/pages/util/sample.json
Normal file
38
src/pages/util/sample.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"accessRequestId": "2c91808b6ef1d43e016efba0ce470904",
|
||||
"requestedFor": {
|
||||
"type": "IDENTITY",
|
||||
"id": "2c91808568c529c60168cca6f90c1313",
|
||||
"name": "William Wilson"
|
||||
},
|
||||
"requestedItemsStatus": [
|
||||
{
|
||||
"id": "2c91808b6ef1d43e016efba0ce470904",
|
||||
"name": "Engineering Access",
|
||||
"description": "Access to engineering database",
|
||||
"type": "ACCESS_PROFILE",
|
||||
"operation": "Add",
|
||||
"comment": "William needs this access to do his job.",
|
||||
"clientMetadata": {
|
||||
"applicationName": "My application"
|
||||
},
|
||||
"approvalInfo": [
|
||||
{
|
||||
"approvalComment": "This access looks good. Approved.",
|
||||
"approvalDecision": "APPROVED",
|
||||
"approverName": "Stephen.Austin",
|
||||
"approver": {
|
||||
"type": "IDENTITY",
|
||||
"id": "2c91808568c529c60168cca6f90c1313",
|
||||
"name": "William Wilson"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"requestedBy": {
|
||||
"type": "IDENTITY",
|
||||
"id": "2c91808568c529c60168cca6f90c1313",
|
||||
"name": "William Wilson"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user