@controlium/utils 1.0.2-alpha.2 → 1.0.2-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/apiUtils/APIUtils.js +226 -0
- package/dist/esm/mock/mock.js +45 -31
- package/dist/esm/utils/utils.js +3 -2
- package/dist/types/apiUtils/APIUtils.d.ts +90 -0
- package/dist/types/mock/mock.d.ts +13 -11
- package/dist/types/utils/utils.d.ts +4 -4
- package/package.json +11 -52
- package/dist/cjs/detokeniser/detokeniser.js +0 -1015
- package/dist/cjs/index.js +0 -16
- package/dist/cjs/jsonUtils/jsonUtils.js +0 -460
- package/dist/cjs/logger/logger.js +0 -865
- package/dist/cjs/logger/types.js +0 -2
- package/dist/cjs/mock/mock.js +0 -509
- package/dist/cjs/stringUtils/stringUtils.js +0 -294
- package/dist/cjs/utils/utils.js +0 -1050
package/dist/cjs/index.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Mock = exports.ExistingFileWriteActions = exports.Utils = exports.StringUtils = exports.JsonUtils = exports.LogLevels = exports.Log = exports.Logger = void 0;
|
|
4
|
-
const logger_1 = require("./logger/logger");
|
|
5
|
-
Object.defineProperty(exports, "Logger", { enumerable: true, get: function () { return logger_1.Logger; } });
|
|
6
|
-
Object.defineProperty(exports, "Log", { enumerable: true, get: function () { return logger_1.Logger; } });
|
|
7
|
-
exports.LogLevels = logger_1.Logger.Levels;
|
|
8
|
-
var jsonUtils_1 = require("./jsonUtils/jsonUtils");
|
|
9
|
-
Object.defineProperty(exports, "JsonUtils", { enumerable: true, get: function () { return jsonUtils_1.JsonUtils; } });
|
|
10
|
-
var stringUtils_1 = require("./stringUtils/stringUtils");
|
|
11
|
-
Object.defineProperty(exports, "StringUtils", { enumerable: true, get: function () { return stringUtils_1.StringUtils; } });
|
|
12
|
-
var utils_1 = require("./utils/utils");
|
|
13
|
-
Object.defineProperty(exports, "Utils", { enumerable: true, get: function () { return utils_1.Utils; } });
|
|
14
|
-
Object.defineProperty(exports, "ExistingFileWriteActions", { enumerable: true, get: function () { return utils_1.ExistingFileWriteActions; } });
|
|
15
|
-
var mock_1 = require("./mock/mock");
|
|
16
|
-
Object.defineProperty(exports, "Mock", { enumerable: true, get: function () { return mock_1.Mock; } });
|
|
@@ -1,460 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.JsonUtils = void 0;
|
|
40
|
-
const json5_1 = __importDefault(require("json5"));
|
|
41
|
-
const JSONPath = __importStar(require("jsonpath-plus"));
|
|
42
|
-
const jsonpointer_1 = __importDefault(require("jsonpointer"));
|
|
43
|
-
const index_1 = require("../index");
|
|
44
|
-
/**
|
|
45
|
-
* JSON utility methods for querying, manipulating, parsing and validating JSON objects.
|
|
46
|
-
* All methods are static — no instantiation required.
|
|
47
|
-
*/
|
|
48
|
-
class JsonUtils {
|
|
49
|
-
/**
|
|
50
|
-
* Returns the JSONPath path to the parent of the node addressed by the given path.
|
|
51
|
-
*
|
|
52
|
-
* @param pathToChild - A valid JSONPath path to a JSON node.
|
|
53
|
-
* @returns The JSONPath path to the parent of the given node.
|
|
54
|
-
* @throws {Error} If the node has no parent (i.e. it is a top-level node).
|
|
55
|
-
*
|
|
56
|
-
* @example
|
|
57
|
-
* JsonUtils.getParentPath("$.a.b.c"); // returns "$.a.b"
|
|
58
|
-
*/
|
|
59
|
-
static getParentPath(pathToChild) {
|
|
60
|
-
const pathArray = JSONPath.JSONPath.toPathArray((pathToChild.startsWith(".") ? "" : ".") + pathToChild);
|
|
61
|
-
if (pathArray.length < 2) {
|
|
62
|
-
const errText = `Unable to get Parent as child [${pathToChild}] has no parent (it is top level)!`;
|
|
63
|
-
index_1.Log.writeLine(index_1.LogLevels.Error, errText);
|
|
64
|
-
throw new Error(errText);
|
|
65
|
-
}
|
|
66
|
-
pathArray.pop();
|
|
67
|
-
return JSONPath.JSONPath.toPathString(pathArray);
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Returns a deep copy of the given object with the property identified by the
|
|
71
|
-
* given JSONPath renamed. The original object is not mutated.
|
|
72
|
-
*
|
|
73
|
-
* The JSONPath must match exactly one property — zero or multiple matches will throw.
|
|
74
|
-
*
|
|
75
|
-
* @param jsonObject - The source object containing the property to rename.
|
|
76
|
-
* @param pathToPropertyToRename - A valid JSONPath identifying the property to rename.
|
|
77
|
-
* @param newName - The new name for the property.
|
|
78
|
-
* @returns A new deep-copied object with the property renamed.
|
|
79
|
-
* @throws {Error} If the JSONPath matches zero or more than one property.
|
|
80
|
-
*
|
|
81
|
-
* @example
|
|
82
|
-
* const result = JsonUtils.withRenamedProperty({ a: { b: 1 } }, "$.a.b", "c");
|
|
83
|
-
* // result => { a: { c: 1 } }
|
|
84
|
-
*/
|
|
85
|
-
static withRenamedProperty(jsonObject, pathToPropertyToRename, newName) {
|
|
86
|
-
const normalizedNewName = newName.trim();
|
|
87
|
-
const parentPath = this.getParentPath(pathToPropertyToRename);
|
|
88
|
-
const objectBeingRenamed = this.getPropertiesMatchingPath(jsonObject, pathToPropertyToRename);
|
|
89
|
-
if (objectBeingRenamed.length === 0) {
|
|
90
|
-
const errText = `renameJsonObjectProperty: cannot rename - JSON Path [${pathToPropertyToRename}] matches nothing`;
|
|
91
|
-
index_1.Log.writeLine(index_1.LogLevels.Error, errText);
|
|
92
|
-
throw new Error(errText);
|
|
93
|
-
}
|
|
94
|
-
else if (objectBeingRenamed.length > 1) {
|
|
95
|
-
const errText = `renameJsonObjectProperty: cannot rename - JSON Path [${pathToPropertyToRename}] matches ${objectBeingRenamed.length} fields/objects!! Expected exactly 1 match`;
|
|
96
|
-
index_1.Log.writeLine(index_1.LogLevels.Error, errText);
|
|
97
|
-
throw new Error(errText);
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
const objectBeingRenamedParent = this.getPropertiesMatchingPath(jsonObject, parentPath);
|
|
101
|
-
let pathToNewObject = index_1.StringUtils.replaceAll(objectBeingRenamedParent[0].pointer, "/", ".");
|
|
102
|
-
pathToNewObject = index_1.StringUtils.isBlank(pathToNewObject) ? normalizedNewName : ((normalizedNewName.startsWith("['") && normalizedNewName.endsWith("']")) ? pathToNewObject.substring(1) + normalizedNewName : pathToNewObject.substring(1) + "." + normalizedNewName);
|
|
103
|
-
let updatedObject = JSON.parse(JSON.stringify(jsonObject));
|
|
104
|
-
// We rename the property by adding the property using the new name...
|
|
105
|
-
updatedObject = this.updateJSONObject(updatedObject, pathToNewObject, objectBeingRenamed[0].value);
|
|
106
|
-
// ... then deleting the property with the old name
|
|
107
|
-
updatedObject = this.updateJSONObject(updatedObject, pathToPropertyToRename, "_undefined");
|
|
108
|
-
return updatedObject;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Shallow-merges the given object into the property identified by the given JSONPath
|
|
113
|
-
* within the source object. Properties in `objectToMerge` overwrite same-named
|
|
114
|
-
* properties in the target. The JSONPath must match exactly one property.
|
|
115
|
-
*
|
|
116
|
-
* Use `"$"` as the path to merge directly into the root object.
|
|
117
|
-
*
|
|
118
|
-
* @param jsonObject - The source object containing the property to merge into.
|
|
119
|
-
* @param pathToPropertyToMergeInto - A valid JSONPath identifying the target property.
|
|
120
|
-
* @param objectToMerge - The object whose properties will be merged into the target.
|
|
121
|
-
* @returns The updated source object with the merge applied.
|
|
122
|
-
* @throws {Error} If the JSONPath matches zero or more than one property.
|
|
123
|
-
*
|
|
124
|
-
* @example
|
|
125
|
-
* const result = JsonUtils.mergeObjectIntoProperty({ a: { x: 1 } }, "$.a", { y: 2 });
|
|
126
|
-
* // result => { a: { x: 1, y: 2 } }
|
|
127
|
-
*/
|
|
128
|
-
static mergeObjectIntoProperty(jsonObject, pathToPropertyToMergeInto, objectToMerge) {
|
|
129
|
-
const objectToMergeWith = this.getPropertiesMatchingPath(jsonObject, pathToPropertyToMergeInto);
|
|
130
|
-
if (objectToMergeWith.length === 0) {
|
|
131
|
-
const errText = `mergeJsonObjects: cannot merge - JSON Path [${pathToPropertyToMergeInto}] matches nothing`;
|
|
132
|
-
index_1.Log.writeLine(index_1.LogLevels.Error, errText);
|
|
133
|
-
throw new Error(errText);
|
|
134
|
-
}
|
|
135
|
-
else if (objectToMergeWith.length > 1) {
|
|
136
|
-
const errText = `mergeJsonObjects: cannot merge - JSON Path [${pathToPropertyToMergeInto}] matches ${objectToMergeWith.length} fields/objects!! Expected exactly 1 match`;
|
|
137
|
-
index_1.Log.writeLine(index_1.LogLevels.Error, errText);
|
|
138
|
-
throw new Error(errText);
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
const targetObject = objectToMergeWith[0].value;
|
|
142
|
-
let mergedOriginalObject = jsonObject;
|
|
143
|
-
if (pathToPropertyToMergeInto === "$") {
|
|
144
|
-
mergedOriginalObject = { ...targetObject, ...objectToMerge };
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
mergedOriginalObject = this.updateJSONObject(mergedOriginalObject, pathToPropertyToMergeInto, { ...targetObject, ...objectToMerge });
|
|
148
|
-
}
|
|
149
|
-
return mergedOriginalObject;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Reads a file from disk and parses its contents as JSON, returning the result as an object.
|
|
154
|
-
* Returns an empty object `{}` if the file contents are not valid JSON.
|
|
155
|
-
*
|
|
156
|
-
* @param pathAndFilename - Path to the JSON file to read.
|
|
157
|
-
* @param options - Optional settings:
|
|
158
|
-
* - `encoding` — File encoding to use when reading (default: `"utf-8"`).
|
|
159
|
-
* - `detokeniseFileContents` — When `true`, passes file contents through the
|
|
160
|
-
* detokeniser before parsing (default: `false`).
|
|
161
|
-
* @returns The parsed JSON as an object, or `{}` if the file contains invalid JSON.
|
|
162
|
-
* @throws {Error} If the file cannot be read.
|
|
163
|
-
*/
|
|
164
|
-
static getObjectFromFile(pathAndFilename, options) {
|
|
165
|
-
const processedJson = index_1.Utils.getFileContents(pathAndFilename, { encoding: options?.encoding, detokeniseFileContents: options?.detokeniseFileContents });
|
|
166
|
-
if (this.isJson(processedJson)) {
|
|
167
|
-
const jsonObject = JSON.parse(processedJson);
|
|
168
|
-
return jsonObject;
|
|
169
|
-
}
|
|
170
|
-
else {
|
|
171
|
-
index_1.Log.writeLine(index_1.LogLevels.Error, `File [${pathAndFilename}] does not contain valid JSON. Returning empty object`);
|
|
172
|
-
return {};
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Parses a JSON (or optionally JSON5) string into an object.
|
|
177
|
-
* If `item` is `null`, an empty object `{}` is returned.
|
|
178
|
-
*
|
|
179
|
-
* @param item - The JSON string to parse, or `null` (returns `{}`).
|
|
180
|
-
* @param useJson5 - When `true`, parses using JSON5 rules, allowing comments,
|
|
181
|
-
* trailing commas, unquoted keys, etc. Defaults to `false` (strict JSON).
|
|
182
|
-
* @returns The parsed object.
|
|
183
|
-
* @throws {Error} If `item` is not a string or null, or if parsing fails.
|
|
184
|
-
*
|
|
185
|
-
* @example
|
|
186
|
-
* JsonUtils.parse('{"a":1}'); // => { a: 1 }
|
|
187
|
-
* JsonUtils.parse(null); // => {}
|
|
188
|
-
* JsonUtils.parse("{a:1}", true); // => { a: 1 } (JSON5)
|
|
189
|
-
*/
|
|
190
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
191
|
-
static parse(item, useJson5 = false) {
|
|
192
|
-
if (index_1.Utils.isNull(item)) {
|
|
193
|
-
item = "{}";
|
|
194
|
-
}
|
|
195
|
-
if (typeof item !== "string") {
|
|
196
|
-
const errMsg = `Cannot parse item [${typeof item}] as not null or string!`;
|
|
197
|
-
index_1.Log.writeLine(index_1.LogLevels.Error, errMsg);
|
|
198
|
-
throw new Error(errMsg);
|
|
199
|
-
}
|
|
200
|
-
try {
|
|
201
|
-
return useJson5 ? json5_1.default.parse(item) : JSON.parse(item);
|
|
202
|
-
}
|
|
203
|
-
catch (err) {
|
|
204
|
-
const errTxt = `Cannot parse item [${item.length < 50 ? item : `Length ${item.length}`}]: ${err.message}`;
|
|
205
|
-
index_1.Log.writeLine(index_1.LogLevels.Error, errTxt);
|
|
206
|
-
throw new Error(errTxt);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* Checks whether the given item is valid JSON.
|
|
211
|
-
*
|
|
212
|
-
* Returns `true` if:
|
|
213
|
-
* - `item` is a string that parses as a JSON object (not a primitive), or
|
|
214
|
-
* - `item` is an object that can be `JSON.stringify`-ed and parsed back as an object.
|
|
215
|
-
*
|
|
216
|
-
* Returns `false` for `undefined`, unparseable strings, or values that parse
|
|
217
|
-
* to a primitive (e.g. `"true"`, `"42"`).
|
|
218
|
-
*
|
|
219
|
-
* @param item - The value to check.
|
|
220
|
-
* @param allowJson5 - When `true`, accepts JSON5 syntax (comments, trailing commas,
|
|
221
|
-
* unquoted keys, etc.). Defaults to `false` (strict JSON only).
|
|
222
|
-
* @returns `true` if the item represents a valid JSON object, `false` otherwise.
|
|
223
|
-
*
|
|
224
|
-
* @example
|
|
225
|
-
* JsonUtils.isJson('{"a":1}'); // true
|
|
226
|
-
* JsonUtils.isJson('42'); // false (primitive)
|
|
227
|
-
* JsonUtils.isJson(undefined); // false
|
|
228
|
-
* JsonUtils.isJson("{a:1}", true); // true (JSON5)
|
|
229
|
-
*/
|
|
230
|
-
static isJson(item, allowJson5 = false) {
|
|
231
|
-
if (index_1.Utils.isUndefined(item)) {
|
|
232
|
-
return false;
|
|
233
|
-
}
|
|
234
|
-
try {
|
|
235
|
-
item = typeof item === "object" ? JSON.stringify(item) : item;
|
|
236
|
-
item = allowJson5 ? json5_1.default.parse(item) : JSON.parse(item);
|
|
237
|
-
if (typeof item === "object" && item !== null) {
|
|
238
|
-
return true;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
catch (e) {
|
|
242
|
-
return false;
|
|
243
|
-
}
|
|
244
|
-
return false;
|
|
245
|
-
}
|
|
246
|
-
/**
|
|
247
|
-
* Returns all properties within a JSON object that match the given JSONPath expression.
|
|
248
|
-
* Each result includes the matched value, its JSON Pointer path, and its parent object.
|
|
249
|
-
*
|
|
250
|
-
* @param jsonObject - The object (or JSON string) to query.
|
|
251
|
-
* @param pathToJSONProperties - A valid JSONPath expression identifying the properties to retrieve.
|
|
252
|
-
* @returns An array of matches, each with `value`, `pointer` (JSON Pointer string), and `parent`.
|
|
253
|
-
* Returns an empty array if no properties match.
|
|
254
|
-
* @throws {Error} If `jsonObject` is not a valid JSON object, or if the JSONPath query fails.
|
|
255
|
-
*
|
|
256
|
-
* @see https://jsonpath-plus.github.io/JSONPath/docs/ts/
|
|
257
|
-
*
|
|
258
|
-
* @example
|
|
259
|
-
* JsonUtils.getPropertiesMatchingPath({ a: { b: 1 } }, "$.a.b");
|
|
260
|
-
* // => [{ value: 1, pointer: "/a/b", parent: { b: 1 } }]
|
|
261
|
-
*/
|
|
262
|
-
static getPropertiesMatchingPath(jsonObject, pathToJSONProperties) {
|
|
263
|
-
try {
|
|
264
|
-
if (!this.isJson(jsonObject)) {
|
|
265
|
-
throw new Error("Passed object is not a valid Json Object");
|
|
266
|
-
}
|
|
267
|
-
if (typeof jsonObject === "string") {
|
|
268
|
-
jsonObject = JSON.parse(jsonObject);
|
|
269
|
-
}
|
|
270
|
-
const jsonPropertys = JSONPath.JSONPath({
|
|
271
|
-
path: pathToJSONProperties,
|
|
272
|
-
json: jsonObject,
|
|
273
|
-
resultType: "all",
|
|
274
|
-
});
|
|
275
|
-
index_1.Log.writeLine(index_1.LogLevels.FrameworkDebug, `JSON:\n${JSON.stringify(jsonObject, null, 2)}`, { maxLines: 512 });
|
|
276
|
-
index_1.Log.writeLine(index_1.LogLevels.FrameworkInformation, `For path [${pathToJSONProperties}], got ${jsonPropertys.length} matches`);
|
|
277
|
-
return jsonPropertys;
|
|
278
|
-
}
|
|
279
|
-
catch (err) {
|
|
280
|
-
index_1.Log.writeLine(index_1.LogLevels.Error, `Error getting Json properties with path [${pathToJSONProperties}] from object:\n${err}`);
|
|
281
|
-
throw new Error(`Error getting Json properties with path [${pathToJSONProperties}] from object:\n${err}`);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
/**
|
|
285
|
-
* Sets, adds, or deletes a property within a JSON object identified by a JSONPath expression.
|
|
286
|
-
* Operates on a deep copy of `currentObject` — the original is not mutated.
|
|
287
|
-
*
|
|
288
|
-
* - If the property **exists**, its value is updated.
|
|
289
|
-
* - If the property **does not exist**, it is added with the given value.
|
|
290
|
-
* - To **delete** a property, pass the string `"_undefined"` as the value.
|
|
291
|
-
* Note: deleting a property that does not exist will throw.
|
|
292
|
-
*
|
|
293
|
-
* The JSONPath must match zero or one property — multiple matches will throw.
|
|
294
|
-
*
|
|
295
|
-
* When `Log.loggingLevel` is below `LogLevels.TestInformation`, a detailed error
|
|
296
|
-
* including the full object and value is written to the log before throwing.
|
|
297
|
-
*
|
|
298
|
-
* @param currentObject - The source object to update (not mutated).
|
|
299
|
-
* @param pathString - A valid JSONPath expression identifying the property to set/add/delete.
|
|
300
|
-
* @param value - The value to set. Pass the string `"_undefined"` to delete the property.
|
|
301
|
-
* @returns A new deep-copied object with the change applied.
|
|
302
|
-
* @throws {Error} If `currentObject` is not a valid JSON object, if the JSONPath matches
|
|
303
|
-
* more than one property, if deletion is attempted on a non-existent property, or if
|
|
304
|
-
* the value could not be verified after being set.
|
|
305
|
-
*
|
|
306
|
-
* @example
|
|
307
|
-
* JsonUtils.updateJSONObject({ a: 1 }, "$.a", 2); // => { a: 2 }
|
|
308
|
-
* JsonUtils.updateJSONObject({ a: 1 }, "$.b", "hello"); // => { a: 1, b: "hello" }
|
|
309
|
-
* JsonUtils.updateJSONObject({ a: 1 }, "$.a", "_undefined"); // => {}
|
|
310
|
-
*/
|
|
311
|
-
static updateJSONObject(currentObject, pathString, value) {
|
|
312
|
-
let jsonPointer;
|
|
313
|
-
index_1.Log.writeLine(index_1.LogLevels.FrameworkInformation, `Setting [${pathString}] of object to [${typeof value !== "object" ? value : "an object value"}]`);
|
|
314
|
-
try {
|
|
315
|
-
let modifiedObject;
|
|
316
|
-
if (JsonUtils.isJson(currentObject)) {
|
|
317
|
-
modifiedObject = JsonUtils.parse(JSON.stringify(currentObject));
|
|
318
|
-
}
|
|
319
|
-
else {
|
|
320
|
-
const errMsg = "Object is not a JSON object. Cannot update!";
|
|
321
|
-
index_1.Log.writeLine(index_1.LogLevels.Error, errMsg);
|
|
322
|
-
throw new Error(errMsg);
|
|
323
|
-
}
|
|
324
|
-
const matchingProperties = this.getPropertiesMatchingPath(modifiedObject, pathString);
|
|
325
|
-
if (matchingProperties.length === 0) {
|
|
326
|
-
index_1.Log.writeLine(index_1.LogLevels.FrameworkDebug, "no matching properties found - will ADD property");
|
|
327
|
-
// No matches. So work out what the Json pointer is and add it. We prepend a period to denote path from the top unless
|
|
328
|
-
// it is a Top level with special chars
|
|
329
|
-
const jsonPathArray = JSONPath.JSONPath.toPathArray(pathString.startsWith('[') ? pathString : '.' + pathString);
|
|
330
|
-
jsonPointer = JSONPath.JSONPath.toPointer(jsonPathArray);
|
|
331
|
-
}
|
|
332
|
-
else if (matchingProperties.length > 1) {
|
|
333
|
-
index_1.Log.writeLine(index_1.LogLevels.FrameworkDebug, `[${matchingProperties.length}] matching properties found!! Will throw error`);
|
|
334
|
-
throw new Error(`JSON Path [${pathString}] matched [${matchingProperties.length}]! Expected zero or one match.`);
|
|
335
|
-
}
|
|
336
|
-
else {
|
|
337
|
-
// Yippee exactly one property
|
|
338
|
-
index_1.Log.writeLine(index_1.LogLevels.FrameworkDebug, "One matching property found - will update/delete property");
|
|
339
|
-
jsonPointer = matchingProperties[0].pointer;
|
|
340
|
-
}
|
|
341
|
-
if (value === "_undefined") {
|
|
342
|
-
const deadProperty = jsonPointer.split("/").pop();
|
|
343
|
-
index_1.Log.writeLine(index_1.LogLevels.FrameworkDebug, `value is _undefined so will delete property [${jsonPointer}]`);
|
|
344
|
-
if (Array.isArray(matchingProperties[0].parent)) {
|
|
345
|
-
index_1.Log.writeLine(index_1.LogLevels.FrameworkDebug, `Deleting [${matchingProperties[0].parent}] is an array. So remove by index....`);
|
|
346
|
-
matchingProperties[0].parent.splice(Number(deadProperty), 1);
|
|
347
|
-
}
|
|
348
|
-
else {
|
|
349
|
-
index_1.Log.writeLine(index_1.LogLevels.FrameworkDebug, `Deleting [${matchingProperties[0].parent}] is an NOT array. So remove by deletion...`);
|
|
350
|
-
// Caller wants to delete property...
|
|
351
|
-
// http://perfectionkills.com/understanding-delete/
|
|
352
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
353
|
-
delete matchingProperties[0].parent[deadProperty]; // How can I do this WITHOUT going via any?
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
else {
|
|
357
|
-
index_1.Log.writeLine(index_1.LogLevels.FrameworkDebug, `Updating or adding property [${jsonPointer}] with value: ${typeof value === "object" ? "\n" + JSON.stringify(value, null, 2) : value}`);
|
|
358
|
-
jsonpointer_1.default.set(modifiedObject, jsonPointer, value);
|
|
359
|
-
const setValue = jsonpointer_1.default.get(modifiedObject, jsonPointer);
|
|
360
|
-
if (setValue !== value) {
|
|
361
|
-
const errMessage = `After setting [${pathString}] to value: ${typeof value === "object" ? "\n" + JSON.stringify(value, null, 2) : `[${value}]`}\nCheck showed it was now: ${typeof setValue === "object" ? "\n" + JSON.stringify(setValue, null, 2) : `[${setValue}]`}`;
|
|
362
|
-
index_1.Log.writeLine(index_1.LogLevels.Error, errMessage);
|
|
363
|
-
throw new Error(errMessage);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
return modifiedObject;
|
|
367
|
-
}
|
|
368
|
-
catch (err) {
|
|
369
|
-
const errText = `Error adding/updating/deleting property [${pathString}]: ${err}`;
|
|
370
|
-
// Only write error out if we are Test Debug or lower. No need for Test Information)
|
|
371
|
-
if (index_1.Log.loggingLevel < index_1.LogLevels.TestInformation) {
|
|
372
|
-
index_1.Log.writeLine(index_1.LogLevels.Error, `${errText}\nValue:${typeof value === "object" ? JSON.stringify(value, null, 2) : value}\nObject:\n${JSON.stringify(currentObject, null, 2)}`, { maxLines: 1024 });
|
|
373
|
-
}
|
|
374
|
-
throw new Error(errText);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
/**
|
|
378
|
-
* Returns the number of properties within a JSON object that match the given JSONPath expression.
|
|
379
|
-
*
|
|
380
|
-
* @param jsonObject - The object to query.
|
|
381
|
-
* @param pathString - A valid JSONPath expression identifying the properties to count.
|
|
382
|
-
* @returns The number of matching properties, or `0` if none match.
|
|
383
|
-
* @throws {Error} If `jsonObject` is not a valid JSON object.
|
|
384
|
-
*
|
|
385
|
-
* @example
|
|
386
|
-
* JsonUtils.getMatchingJSONPropertyCount({ a: 1, b: 2 }, "$.a"); // => 1
|
|
387
|
-
* JsonUtils.getMatchingJSONPropertyCount({ a: 1, b: 2 }, "$.*"); // => 2
|
|
388
|
-
*/
|
|
389
|
-
static getMatchingJSONPropertyCount(jsonObject, pathString) {
|
|
390
|
-
if (!this.isJson(jsonObject)) {
|
|
391
|
-
index_1.Log.writeLine(index_1.LogLevels.Error, "Passed object is not a valid Json Object. Aborting");
|
|
392
|
-
throw new Error("Passed object is not a valid Json Object");
|
|
393
|
-
}
|
|
394
|
-
const jsonProperties = this.getPropertiesMatchingPath(jsonObject, pathString);
|
|
395
|
-
index_1.Log.writeLine(index_1.LogLevels.FrameworkInformation, `[${pathString}] has [${jsonProperties.length}] matches`);
|
|
396
|
-
return jsonProperties?.length ?? 0;
|
|
397
|
-
}
|
|
398
|
-
/**
|
|
399
|
-
* Removes all properties matching the given key names from a JSON object, including
|
|
400
|
-
* those nested within child objects or arrays. Operates directly on the passed object
|
|
401
|
-
* (mutates in place).
|
|
402
|
-
*
|
|
403
|
-
* @param jsonObject - The object (or array of objects) to remove properties from.
|
|
404
|
-
* @param removeKeys - One or more property names to remove at any depth.
|
|
405
|
-
* @param throwError - When `true`, any error encountered during removal is re-thrown.
|
|
406
|
-
* When `false` (default), errors are logged and silently swallowed.
|
|
407
|
-
*
|
|
408
|
-
* @example
|
|
409
|
-
* const obj = { a: 1, b: { a: 2, c: 3 } };
|
|
410
|
-
* JsonUtils.removeJsonPropertyByKey(obj, ["a"]);
|
|
411
|
-
* // obj => { b: { c: 3 } }
|
|
412
|
-
*/
|
|
413
|
-
static removeJsonPropertyByKey(jsonObject, removeKeys, throwError = false) {
|
|
414
|
-
try {
|
|
415
|
-
if (Array.isArray(jsonObject)) {
|
|
416
|
-
index_1.Log.writeLine(index_1.LogLevels.FrameworkDebug, "JSON Object is an array, itterating through array and removing required key/s from each item");
|
|
417
|
-
for (const arrayItem of jsonObject) {
|
|
418
|
-
this.removeJsonPropertyByKey(arrayItem, removeKeys);
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
else if (typeof jsonObject === "object") {
|
|
422
|
-
index_1.Log.writeLine(index_1.LogLevels.FrameworkDebug, `JSON Object is an object, removing key/s [${removeKeys.join(",")}]`);
|
|
423
|
-
for (const removeKey of removeKeys) {
|
|
424
|
-
for (const key of Object.keys(jsonObject)) {
|
|
425
|
-
if (key === removeKey) {
|
|
426
|
-
delete jsonObject[removeKey];
|
|
427
|
-
}
|
|
428
|
-
else {
|
|
429
|
-
this.removeJsonPropertyByKey(jsonObject[key], removeKeys);
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
else {
|
|
435
|
-
index_1.Log.writeLine(index_1.LogLevels.Error, `Unable to remove Keys [${removeKeys.join(",")}] as json object is ${typeof jsonObject}. Expected object or Array<object>`);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
catch (err) {
|
|
439
|
-
const errText = `Error in removing the property from JSON ${err}`;
|
|
440
|
-
index_1.Log.writeLine(index_1.LogLevels.Error, errText);
|
|
441
|
-
if (throwError)
|
|
442
|
-
throw new Error(errText);
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
/**
|
|
446
|
-
* Escapes all RegExp special characters in a string so it can be safely used
|
|
447
|
-
* as a literal pattern in a `RegExp` constructor.
|
|
448
|
-
*
|
|
449
|
-
* @param toBeEscaped - The string to escape.
|
|
450
|
-
* @returns The input string with all RegExp special characters escaped.
|
|
451
|
-
*
|
|
452
|
-
* @example
|
|
453
|
-
* JsonUtils.escapeRegExp("a.b+c"); // => "a\\.b\\+c"
|
|
454
|
-
* new RegExp(JsonUtils.escapeRegExp("1+1=2")); // matches literal "1+1=2"
|
|
455
|
-
*/
|
|
456
|
-
static escapeRegExp(toBeEscaped) {
|
|
457
|
-
return toBeEscaped.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
exports.JsonUtils = JsonUtils;
|