esi-cap
Version:
Enterprise System Integration Based on SAP CAP
484 lines (325 loc) • 15.1 kB
Markdown
# esi-cap Utilities API Reference
> **Module:** `src/lib/utils/index.js`
> **Purpose:** Core utility belt for the esi-cap framework — date validation, XML parsing, JSON manipulation, array operations, and UUID generation/parsing.
## Table of Contents
- [Dependencies](#dependencies)
- [Exports](#exports)
- [Namespace: `date`](#namespace-date)
- [date.isValid(sDate)](#dateisvalidsdate)
- [Namespace: `xml`](#namespace-xml)
- [xml.isValid(oXML)](#xmlisvalidoxml)
- [Namespace: `json`](#namespace-json)
- [json.isValid(oJson)](#jsonisvalidojson)
- [json.copy(oJson)](#jsoncopyojson)
- [json.replace(oJson, sOldValue, sNewValue, oEvaluate?)](#jsonreplaceojson-soldvalue-snewvalue-oevaluate)
- [json.getValue(oJson, sPropertyPath)](#jsongetvalueojson-spropertypath)
- [json.unique(oNestedArray)](#jsonuniqueonnestedarray)
- [json.order(oJson, oKeyOrder)](#jsonorderojson-okeyorder)
- [json.projection(oJson, oColumns, bHasAssociattion?)](#jsonprojectionojson-ocolumns-bhasassociattion)
- [json.map(oJson, oMap)](#jsonmapojson-omap)
- [json.merge(oJson1, oJson2)](#jsonmergeojson1-ojson2)
- [json.flat(oJson, sFlattenedProperty)](#jsonflatojson-sflattenedproperty)
- [json.stripUndefined(oSourceJson)](#jsonstripundefinedosourcejson)
- [Namespace: `array`](#namespace-array)
- [array.add(oArray, oItem)](#arrayaddoarray-oitem)
- [array.topN(oSortedArray, iTop)](#arraytopnosortedarray-itop)
- [array.flat(oArray, sFlattenedProperty)](#arrayflatoarray-sflattenedproperty)
- [array.unique(oArray)](#arrayuniqueoarray)
- [array.order(oArray, oOrder)](#arrayorderoarray-oorder)
- [array.projection(oArray, oColumns, bHasAssociattion?)](#arrayprojectionoarray-ocolumns-bhasassociattion)
- [array.map(oArray, oMap)](#arraymapoarray-omap)
- [array.sort(oArray, oOrderBy)](#arraysortoarray-oorderby)
- [array.filter(oArray, oFilterCondition)](#arrayfilteroarray-ofiltercondition)
- [array.hasElement(oArray, oElement)](#arrayhaselementoarray-oelement)
- [array.toArray(oArray)](#arraytoarrayoarray)
- [array.toDisArray(oArray)](#arraytodisarrayoarray)
- [array.toInterleavedArray(oArray, oElement)](#arraytointerleavedarray-oarray-oelement)
- [array.toArrayProjection(oArray, sProjectionFieldName)](#arraytoarrayprojectionoarray-sprojectionfieldname)
- [array.findProperty(oArray, sProjectionFieldName)](#arrayfindpropertyoarray-sprojectionfieldname)
- [array.toGroupByPropertyName(oArray, sPropertyName)](#arraytogroupbypropertynameoarray-spropertyname)
- [array.toGroupByPropertyList(oArray, oPropertyList)](#arraytogroupbypropertylistoarray-opropertylist)
- [Class: `UUID`](#class-uuid)
- [constructor()](#constructor)
- [converse(oJsonData)](#converseoJsondata)
- [inverse(sUUID)](#inversesuuid)
## Dependencies
| Package | Purpose |
|----------|-------------------------------------------------|
| `xml2js` | XML string parsing and validation |
| `uuid` | UUID format validation |
| `lodash` | Deep merge, orderBy, uniqWith, and isEqual |
| `../_interface` | Internal logger and `_LOG` constant |
## Exports
```js
module.exports = { date, xml, json, array, UUID, _LOG };
```
## Namespace: `date`
Date-related validation helpers.
### `date.isValid(sDate)`
| Parameter | Type | Description |
|-----------|----------|--------------------------------------|
| `sDate` | `string` | Date string to validate |
| **Returns** | `Promise<boolean>` | `true` if valid date |
**Behavior:** Parses the string via `new Date()` and verifies the result is a real Date (not `NaN`). Logs errors via the framework logger.
```js
await date.isValid("2026-04-20"); // true
await date.isValid("not-a-date"); // false
```
## Namespace: `xml`
XML parsing and validation helpers.
### `xml.isValid(oXML)`
| Parameter | Type | Description |
|-----------|------------------|-----------------------------|
| `oXML` | `string\|object` | XML content to validate |
| **Returns** | `Promise<boolean>` | `true` if well-formed XML |
**Behavior:** Uses `xml2js.Parser.parseStringPromise` to attempt parsing. Logs the parsed result on success and errors on failure.
```js
await xml.isValid("<root><item>val</item></root>"); // true
await xml.isValid("not xml"); // false
```
## Namespace: `json`
Comprehensive JSON/object manipulation utilities.
### `json.isValid(oJson)`
Checks if the input can be parsed as JSON via `JSON.parse`.
| Parameter | Type | Returns |
|-----------|------------------|------------|
| `oJson` | `string\|object` | `boolean` |
### `json.copy(oJson)`
Creates a deep clone using `structuredClone`. Falls back to returning the original reference if the input isn't valid JSON.
| Parameter | Type | Returns |
|-----------|----------|----------|
| `oJson` | `object` | `object` |
### `json.replace(oJson, sOldValue, sNewValue, oEvaluate?)`
Recursively walks objects and arrays, replacing all string occurrences of `sOldValue` with `sNewValue`. **Mutates the input in place.**
| Parameter | Type | Default | Description |
|-------------|----------|-------------|----------------------------------------------------------|
| `oJson` | `object\|Array` | — | Target structure |
| `sOldValue` | `string` | — | Substring to find |
| `sNewValue` | `string` | — | Replacement substring |
| `oEvaluate` | `object` | `undefined` | Optional context; when set, resolved string is evaluated as a property path via `json.getValue` |
```js
const data = { msg: "Hello {{name}}", nested: { msg: "Hi {{name}}" } };
json.replace(data, "{{name}}", "World");
// data → { msg: "Hello World", nested: { msg: "Hi World" } }
```
### `json.getValue(oJson, sPropertyPath)`
Resolves a dot-separated property path to its value.
| Parameter | Type | Returns |
|-----------------|----------|---------|
| `oJson` | `object` | `*` |
| `sPropertyPath` | `string` | Value or `undefined` |
```js
json.getValue({ a: { b: { c: 42 } } }, "a.b.c"); // 42
json.getValue({ a: 1 }, "x.y"); // undefined
```
### `json.unique(oNestedArray)`
Flattens a nested array and removes duplicates using JSON serialization for deep equality.
| Parameter | Type | Returns |
|----------------|---------|---------|
| `oNestedArray` | `any[]` | `any[]` |
### `json.order(oJson, oKeyOrder)`
Returns a new object with keys reordered: specified keys first, then the rest.
| Parameter | Type | Returns |
|------------|------------|----------|
| `oJson` | `object` | `object` |
| `oKeyOrder` | `string[]` | — |
```js
json.order({ c: 3, a: 1, b: 2 }, ["a", "b"]); // { a: 1, b: 2, c: 3 }
```
### `json.projection(oJson, oColumns, bHasAssociattion?)`
Filters an object to include only the specified properties.
| Parameter | Type | Default | Description |
|-------------------|------------|---------|----------------------------------------------|
| `oJson` | `object` | — | Source object |
| `oColumns` | `string[]` | — | Property names to keep |
| `bHasAssociattion` | `boolean` | `false` | Also include keys prefixed with column names |
### `json.map(oJson, oMap)`
Creates a new object by mapping source properties via a definition object.
| Parameter | Type | Description |
|-----------|----------|-------------------------------------------------------|
| `oJson` | `object` | Source data |
| `oMap` | `object` | `{ targetKey: "source.path" }` mapping definition |
```js
json.map({ user: { name: "John" } }, { fullName: "user.name" }); // { fullName: "John" }
```
### `json.merge(oJson1, oJson2)`
Deep-merges two objects using Lodash `_.merge`. Returns `{}` if either argument is not an object.
### `json.flat(oJson, sFlattenedProperty)`
Promotes a nested property's contents to the top level of the object.
```js
json.flat({ id: 1, details: { name: "A", desc: "B" } }, "details");
// { id: 1, name: "A", desc: "B" }
```
### `json.stripUndefined(oSourceJson)`
Removes properties where the value is `undefined`, `null`, or an empty string.
## Namespace: `array`
Array operations with automatic fallback to `json` namespace for single-object inputs.
### `array.add(oArray, oItem)`
Appends item(s) to an array. Accepts single values or arrays.
### `array.topN(oSortedArray, iTop)`
Returns the first `iTop` elements via `Array.slice`.
### `array.flat(oArray, sFlattenedProperty)`
Flattens a nested property for each element. Delegates to `json.flat` for single objects.
### `array.unique(oArray)`
Deduplicates using Lodash `_.uniqWith` with `_.isEqual` for deep comparison.
### `array.order(oArray, oOrder)`
Reorders keys for every object in the array per the specified key order.
### `array.projection(oArray, oColumns, bHasAssociattion?)`
Projects each element to only contain the listed columns.
### `array.map(oArray, oMap)`
Applies a property mapping to every element in the array.
### `array.sort(oArray, oOrderBy)`
Multi-column sort with type coercion support.
| `oOrderBy` entry | Property | Type | Description |
|---|---|---|---|
| `ref` | `string[]` | Required | Column path |
| `sort` | `'asc'\|'desc'` | Optional (default `'asc'`) | Sort direction |
| `function` | `'Date'\|'parseFloat'` | Optional | Value coercion |
```js
array.sort(items, [
{ ref: ["date"], sort: "desc", function: "Date" },
{ ref: ["price"], sort: "asc", function: "parseFloat" }
]);
```
### `array.filter(oArray, oFilterCondition)`
Filters array elements based on a list of filter conditions (AND logic).
**FilterCondition:**
| Property | Type | Description |
|---|---|---|
| `name` | `string` | Property name to filter by |
| `op` | `'='\|'!='\|'>'\|'<'\|'>='\|'<='` | Comparison operator |
| `value` | `string\|number\|boolean` | Value to compare against |
```js
array.filter(employees, [
{ name: "age", op: ">=", value: 30 },
{ name: "dept", op: "=", value: "Engineering" }
]);
```
### `array.hasElement(oArray, oElement)`
Returns `true` if the element is found (strict `===` via `Array.includes`).
### `array.toArray(value)`
Normalizes any value into an array: `undefined` → `[]`, non-array → `[value]`, array → as-is.
### `array.toDisArray(oArray)`
Unwraps a single-element array to its value. Multi-element arrays pass through.
### `array.toInterleavedArray(oArray, oElement)`
Inserts a separator element between consecutive items.
```js
array.toInterleavedArray(["a", "b", "c"], "|"); // ["a", ["|"], "b", ["|"], "c"]
```
### `array.toArrayProjection(oArray, sProjectionFieldName)`
Extracts a single field from each element into a flat array.
```js
array.toArrayProjection([{ id: 1 }, { id: 2 }], "id"); // [1, 2]
```
### `array.findProperty(oArray, sProjectionFieldName)`
Recursively searches nested structures for all values of a named property. Returns a deduplicated array.
### `array.toGroupByPropertyName(oArray, sPropertyName)`
Groups elements into an object keyed by a single property's value.
```js
array.toGroupByPropertyName(
[{ type: "A", v: 1 }, { type: "B", v: 2 }, { type: "A", v: 3 }],
"type"
);
// { A: [{...}, {...}], B: [{...}] }
```
### `array.toGroupByPropertyList(oArray, oPropertyList)`
Groups elements by a composite key derived from multiple properties (joined with `##`). Supports dot-notation paths and recursive deep property lookup.
```js
array.toGroupByPropertyList(data, ["region", "details.category"]);
// { "US##Electronics": [...], "EU##Clothing": [...] }
```
## Class: `UUID`
Singleton class for deterministic UUID generation and parsing based on a fixed field configuration. Encodes/decodes structured business data (CompanyCode, PersonWorkAgreementExternalID, TimeSheetRecord) into RFC-4122-compliant UUIDs.
### Constructor
```js
const uuidHelper = new UUID();
```
Creates a singleton instance. Subsequent `new UUID()` calls return the same instance.
**Internal field configuration:**
| Field | Order | Format (Regex) | Description |
|---|---|---|---|
| `CompanyCode` | 1 | `/^[A-Za-z]{2}\d{2}$/` | 2 letters + 2 digits |
| `PersonWorkAgreementExternalID` | 2 | `/^[Ii]\d{7}$\|^\d{1,9}$/` | I-number or numeric ID |
| `TimeSheetRecord` | 3 | `/^\d{12}$/` | 12-digit record number |
### `converse(oJsonData)`
Encodes structured JSON data into a deterministic UUID.
| Parameter | Type | Description |
|------------|----------|-------------------------------------------|
| `oJsonData` | `object` | Object with `CompanyCode`, `PersonWorkAgreementExternalID`, `TimeSheetRecord` |
| **Returns** | `string` | A valid UUID string |
| **Throws** | `Error` | If any field has an invalid format or the generated UUID fails validation |
```js
const uid = new UUID();
uid.converse({
CompanyCode: "AB12",
PersonWorkAgreementExternalID: "I1234567",
TimeSheetRecord: "000000012345"
});
// Returns a valid UUID string e.g., "c0656612-2e12-4345-8670-000000012345"
```
### `inverse(sUUID)`
Decodes a previously generated UUID back into its original structured data.
| Parameter | Type | Description |
|-----------|----------|------------------------------------|
| `sUUID` | `string` | A UUID previously created by `converse` |
| **Returns** | `object` | `{ CompanyCode, PersonWorkAgreementExternalID, TimeSheetRecord }` or `{}` if invalid |
```js
const uid = new UUID();
uid.inverse("c0656612-2e12-4345-8670-000000012345");
// { CompanyCode: "AB12", PersonWorkAgreementExternalID: "I1234567", TimeSheetRecord: "000000012345" }
```
## Quick Usage Examples
```js
const { date, xml, json, array, UUID } = require('./src/lib/utils');
// Validate & transform
const valid = await date.isValid("2026-04-20");
const items = [{ id: 1, name: "A" }, { id: 2, name: "B" }];
// Project, sort, filter
const names = array.toArrayProjection(items, "name"); // ["A", "B"]
const sorted = array.sort(items, [{ ref: ["id"], sort: "desc" }]);
const filtered = array.filter(items, [{ name: "id", op: ">", value: 1 }]);
// Group & merge
const grouped = array.toGroupByPropertyName(items, "name");
const merged = json.merge({ a: 1 }, { b: 2 }); // { a: 1, b: 2 }
// UUID round-trip
const uid = new UUID();
const uuidStr = uid.converse({ CompanyCode: "AB12", PersonWorkAgreementExternalID: "I1234567", TimeSheetRecord: "000000012345" });
const original = uid.inverse(uuidStr);
```