abra-flexi 0.5.6 → 0.6.0
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/index.cjs +316 -97
- package/dist/index.d.cts +41 -9
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +41 -9
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +316 -97
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -96,7 +96,7 @@ function parsePropertyValue(propertyType, annot, obj) {
|
|
|
96
96
|
return new Date(parseInt(s[0], parseInt(s[1]) - 1));
|
|
97
97
|
case PropertyType.Numeric: return new big_js.Big(obj).round(annot?.decimals ?? 2);
|
|
98
98
|
case PropertyType.Logic: return obj.toLowerCase() === "true";
|
|
99
|
-
case PropertyType.Blob: return
|
|
99
|
+
case PropertyType.Blob: return base64ToBytes(obj);
|
|
100
100
|
case PropertyType.Array:
|
|
101
101
|
if (!annot?.itemType) throw new AFError(AFErrorCode.ITEM_TYPE_MISSING, "itemType is required for Array");
|
|
102
102
|
const myAnnot = { ...annot };
|
|
@@ -121,7 +121,7 @@ function serializePropertyValue(propertyType, annot, val) {
|
|
|
121
121
|
if (val instanceof big_js.Big) return val.toString();
|
|
122
122
|
return val;
|
|
123
123
|
case PropertyType.Logic: return !!val ? "true" : "false";
|
|
124
|
-
case PropertyType.Blob: return val
|
|
124
|
+
case PropertyType.Blob: return bytesToBase64(val);
|
|
125
125
|
case PropertyType.Array:
|
|
126
126
|
if (!annot?.itemType) throw new AFError(AFErrorCode.ITEM_TYPE_MISSING, "itemType is required for Array");
|
|
127
127
|
const myAnnot = { ...annot };
|
|
@@ -146,6 +146,22 @@ function dateToLocalIso(date) {
|
|
|
146
146
|
const offset = -date.getTimezoneOffset();
|
|
147
147
|
return `${yyyy}-${mm}-${dd}T${hh}:${mi}:${ss}.${ms}${offset >= 0 ? "+" : "-"}${pad(Math.floor(Math.abs(offset) / 60))}:${pad(Math.abs(offset) % 60)}`;
|
|
148
148
|
}
|
|
149
|
+
function base64ToBytes(base64) {
|
|
150
|
+
if (typeof Buffer !== "undefined") {
|
|
151
|
+
const buf = Buffer.from(base64, "base64");
|
|
152
|
+
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
153
|
+
}
|
|
154
|
+
const binary = atob(base64);
|
|
155
|
+
const bytes = new Uint8Array(binary.length);
|
|
156
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
157
|
+
return bytes;
|
|
158
|
+
}
|
|
159
|
+
function bytesToBase64(bytes) {
|
|
160
|
+
if (typeof Buffer !== "undefined") return Buffer.from(bytes).toString("base64");
|
|
161
|
+
let binary = "";
|
|
162
|
+
for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
|
|
163
|
+
return btoa(binary);
|
|
164
|
+
}
|
|
149
165
|
function arraysEqual(a, b) {
|
|
150
166
|
if (a === b) return true;
|
|
151
167
|
if (a.length !== b.length) return false;
|
|
@@ -170,6 +186,7 @@ var AFEntity = class AFEntity {
|
|
|
170
186
|
}
|
|
171
187
|
constructor(stitkyCache) {
|
|
172
188
|
this._orig = {};
|
|
189
|
+
this._isNew = true;
|
|
173
190
|
this._stitkyCache = stitkyCache;
|
|
174
191
|
}
|
|
175
192
|
getPropertyTypeAnnotation(key) {
|
|
@@ -185,7 +202,7 @@ var AFEntity = class AFEntity {
|
|
|
185
202
|
return !this.hasChanged();
|
|
186
203
|
}
|
|
187
204
|
get isNew() {
|
|
188
|
-
return
|
|
205
|
+
return this._isNew;
|
|
189
206
|
}
|
|
190
207
|
getCotr() {
|
|
191
208
|
return this.constructor;
|
|
@@ -66562,6 +66579,29 @@ function extractEvidence(inUrl) {
|
|
|
66562
66579
|
return split[3];
|
|
66563
66580
|
}
|
|
66564
66581
|
|
|
66582
|
+
//#endregion
|
|
66583
|
+
//#region src/abra/AFNestedEntityResolver.ts
|
|
66584
|
+
function resolveNestedEntityPathPrefix(entityPath, options) {
|
|
66585
|
+
switch (entityPath) {
|
|
66586
|
+
case "individualni-cenik": {
|
|
66587
|
+
const sel = serializeParentSelector(options.adresarId, "adresarId", entityPath);
|
|
66588
|
+
if (sel === void 0) return "";
|
|
66589
|
+
return `adresar/${sel}/`;
|
|
66590
|
+
}
|
|
66591
|
+
default: return "";
|
|
66592
|
+
}
|
|
66593
|
+
}
|
|
66594
|
+
function serializeParentSelector(value, optionName, entityPath) {
|
|
66595
|
+
if (value === void 0 || value === null) return void 0;
|
|
66596
|
+
if (value instanceof AFFilter) {
|
|
66597
|
+
const piece = value.toUrlComponent();
|
|
66598
|
+
if (!piece.length) throw new AFError(AFErrorCode.MISSING_ID, `'${optionName}' for ${entityPath} resolved to empty URL component.`);
|
|
66599
|
+
return piece;
|
|
66600
|
+
}
|
|
66601
|
+
if (typeof value === "string" && !value.length) throw new AFError(AFErrorCode.MISSING_ID, `'${optionName}' for ${entityPath} must be a non-empty id.`);
|
|
66602
|
+
return String(value);
|
|
66603
|
+
}
|
|
66604
|
+
|
|
66565
66605
|
//#endregion
|
|
66566
66606
|
//#region src/abra/AFStitkyCache.ts
|
|
66567
66607
|
const DEBOUNCE_MS = 5 * 1e3;
|
|
@@ -66580,51 +66620,61 @@ var AFStitkyCache = class {
|
|
|
66580
66620
|
if (this._strategy === StitkyCacheStrategy.None) return;
|
|
66581
66621
|
const now = /* @__PURE__ */ new Date();
|
|
66582
66622
|
if (this._lastUpdate && now.getTime() - this._lastUpdate.getTime() < DEBOUNCE_MS) return;
|
|
66583
|
-
|
|
66584
|
-
|
|
66585
|
-
|
|
66586
|
-
|
|
66587
|
-
|
|
66588
|
-
|
|
66589
|
-
|
|
66590
|
-
|
|
66591
|
-
|
|
66592
|
-
|
|
66593
|
-
this.
|
|
66594
|
-
|
|
66595
|
-
|
|
66596
|
-
|
|
66597
|
-
|
|
66598
|
-
|
|
66599
|
-
|
|
66600
|
-
|
|
66601
|
-
|
|
66602
|
-
|
|
66603
|
-
|
|
66604
|
-
|
|
66605
|
-
|
|
66606
|
-
|
|
66607
|
-
|
|
66608
|
-
|
|
66609
|
-
|
|
66610
|
-
|
|
66611
|
-
|
|
66612
|
-
|
|
66613
|
-
|
|
66614
|
-
|
|
66615
|
-
|
|
66616
|
-
|
|
66617
|
-
|
|
66618
|
-
|
|
66619
|
-
|
|
66620
|
-
|
|
66621
|
-
|
|
66622
|
-
|
|
66623
|
-
|
|
66624
|
-
|
|
66623
|
+
if (this._inflight) return this._inflight;
|
|
66624
|
+
this._inflight = (async () => {
|
|
66625
|
+
try {
|
|
66626
|
+
const sinceTs = this._lastUpdate ? formatAbraTimestamp(this._lastUpdate) : void 0;
|
|
66627
|
+
const skupOpts = {
|
|
66628
|
+
limit: NO_LIMIT,
|
|
66629
|
+
detail: AFQueryDetail.FULL,
|
|
66630
|
+
noUpdateStitkyCache: true
|
|
66631
|
+
};
|
|
66632
|
+
if (sinceTs) skupOpts.filter = Filter(`lastUpdate > :date`, { date: sinceTs });
|
|
66633
|
+
const skUpdate = await this._client.query(AFSkupinaStitku, skupOpts);
|
|
66634
|
+
for (const sk of skUpdate) {
|
|
66635
|
+
let found = this._stitekSkupiny.find((ss) => ss.id === sk.id);
|
|
66636
|
+
if (!found) {
|
|
66637
|
+
this._stitekSkupiny.push(sk);
|
|
66638
|
+
continue;
|
|
66639
|
+
}
|
|
66640
|
+
Object.assign(found, sk);
|
|
66641
|
+
}
|
|
66642
|
+
const stitOpts = {
|
|
66643
|
+
limit: NO_LIMIT,
|
|
66644
|
+
detail: [
|
|
66645
|
+
"id",
|
|
66646
|
+
"kod",
|
|
66647
|
+
"lastUpdate",
|
|
66648
|
+
"nazev",
|
|
66649
|
+
"nazevA",
|
|
66650
|
+
"nazevB",
|
|
66651
|
+
"nazevC",
|
|
66652
|
+
"nazevD",
|
|
66653
|
+
"poznam",
|
|
66654
|
+
"popis",
|
|
66655
|
+
"platiOd",
|
|
66656
|
+
"platiDo",
|
|
66657
|
+
"skupVybKlic"
|
|
66658
|
+
],
|
|
66659
|
+
noUpdateStitkyCache: true
|
|
66660
|
+
};
|
|
66661
|
+
if (sinceTs) stitOpts.filter = Filter(`lastUpdate > :date`, { date: sinceTs });
|
|
66662
|
+
const stUpdate = await this._client.query(AFStitek, stitOpts);
|
|
66663
|
+
for (const st of stUpdate) {
|
|
66664
|
+
st.skupVybKlic = this._stitekSkupiny.find((ss) => ss.kod === st.skupVybKlic?.kod);
|
|
66665
|
+
const found = this._stitky.find((s) => s.id === st.id);
|
|
66666
|
+
if (!found) {
|
|
66667
|
+
this._stitky.push(st);
|
|
66668
|
+
continue;
|
|
66669
|
+
}
|
|
66670
|
+
Object.assign(found, st);
|
|
66671
|
+
}
|
|
66672
|
+
this._lastUpdate = now;
|
|
66673
|
+
} finally {
|
|
66674
|
+
this._inflight = void 0;
|
|
66625
66675
|
}
|
|
66626
|
-
|
|
66627
|
-
|
|
66676
|
+
})();
|
|
66677
|
+
return this._inflight;
|
|
66628
66678
|
}
|
|
66629
66679
|
stitkyWithString(keys, groupFilter) {
|
|
66630
66680
|
if (!keys) return void 0;
|
|
@@ -66644,15 +66694,27 @@ var AFStitkyCache = class {
|
|
|
66644
66694
|
return list;
|
|
66645
66695
|
}
|
|
66646
66696
|
};
|
|
66697
|
+
function formatAbraTimestamp(d) {
|
|
66698
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
66699
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
66700
|
+
}
|
|
66647
66701
|
|
|
66648
66702
|
//#endregion
|
|
66649
66703
|
//#region src/abra/AFApiClient.ts
|
|
66650
66704
|
const ABRA_API_FORMAT = "json";
|
|
66705
|
+
const LOG_LEVEL_RANK = {
|
|
66706
|
+
none: 0,
|
|
66707
|
+
error: 1,
|
|
66708
|
+
warn: 2,
|
|
66709
|
+
info: 3,
|
|
66710
|
+
debug: 4
|
|
66711
|
+
};
|
|
66651
66712
|
var AFApiClient = class {
|
|
66652
66713
|
constructor(config) {
|
|
66653
66714
|
this._url = config.url;
|
|
66654
66715
|
this._company = config.company;
|
|
66655
66716
|
this._fetch = config.fetch || fetch;
|
|
66717
|
+
this._logger = makeFilteringLogger(config.logger ?? console, config.logLevel ?? (config.logger ? "debug" : "none"));
|
|
66656
66718
|
this._stitkyCache = new AFStitkyCache(this, config.stitkyCacheStrategy);
|
|
66657
66719
|
}
|
|
66658
66720
|
get url() {
|
|
@@ -66664,14 +66726,14 @@ var AFApiClient = class {
|
|
|
66664
66726
|
get stitkyCacheStrategy() {
|
|
66665
66727
|
return this._stitkyCache.strategy;
|
|
66666
66728
|
}
|
|
66667
|
-
|
|
66668
|
-
if (!this.company || !this.company.length) throw new AFError(AFErrorCode.MISSING_ABRA_COMPANY, `Can't query AFApiClient without providing company path component first.`);
|
|
66729
|
+
_buildQueryUrl(entityPath, format, options) {
|
|
66669
66730
|
const detail = options.detail || AFQueryDetail.SUMMARY;
|
|
66670
66731
|
let furl = options.filter?.toUrlComponent();
|
|
66671
66732
|
if (furl && !furl.length) furl = void 0;
|
|
66672
|
-
|
|
66733
|
+
const pathPrefix = resolveNestedEntityPathPrefix(entityPath, options);
|
|
66734
|
+
let url = this._url + "/c/" + this.company + "/" + pathPrefix + entityPath;
|
|
66673
66735
|
url += furl ? "/" + furl : "";
|
|
66674
|
-
url += "." +
|
|
66736
|
+
url += "." + format;
|
|
66675
66737
|
url = addParamToUrl(url, "detail", composeDetail(detail));
|
|
66676
66738
|
url = addParamToUrl(url, "includes", composeIncludes(detail, entityPath));
|
|
66677
66739
|
url = addParamToUrl(url, "relations", composeRelations(detail));
|
|
@@ -66690,20 +66752,61 @@ var AFApiClient = class {
|
|
|
66690
66752
|
url = addParamToUrl(url, "pocetMesicu", options.pocetMesicu);
|
|
66691
66753
|
url = addParamToUrl(url, "date", options.date);
|
|
66692
66754
|
url = addParamToUrl(url, "currency", options.currency);
|
|
66693
|
-
|
|
66755
|
+
return url;
|
|
66756
|
+
}
|
|
66757
|
+
async queryRaw(entityPath, options = {}) {
|
|
66758
|
+
if (!this.company || !this.company.length) throw new AFError(AFErrorCode.MISSING_ABRA_COMPANY, `Can't query AFApiClient without providing company path component first.`);
|
|
66759
|
+
const url = this._buildQueryUrl(entityPath, ABRA_API_FORMAT, options);
|
|
66760
|
+
this._logger.debug(url);
|
|
66694
66761
|
try {
|
|
66695
66762
|
const raw$1 = await this._fetch(url, { signal: options.abortController?.signal });
|
|
66696
|
-
|
|
66697
|
-
|
|
66763
|
+
const json = await raw$1.json().catch(() => null);
|
|
66764
|
+
if (raw$1.status >= 400 && raw$1.status < 600) {
|
|
66765
|
+
const details = this._extractAbraErrors(json);
|
|
66766
|
+
throw new AFError(AFErrorCode.ABRA_FLEXI_ERROR, `${raw$1.status} ${raw$1.statusText}${details ? ` — ${details}` : ""}`);
|
|
66767
|
+
}
|
|
66768
|
+
return json?.winstrom?.[entityPath];
|
|
66769
|
+
} catch (e) {
|
|
66770
|
+
if (!(e instanceof AFError)) {
|
|
66771
|
+
this._logger.error(e);
|
|
66772
|
+
e = new AFError(AFErrorCode.UNKNOWN, e.toString());
|
|
66773
|
+
}
|
|
66774
|
+
throw e;
|
|
66775
|
+
}
|
|
66776
|
+
}
|
|
66777
|
+
async queryFileRaw(entityPath, format, options = {}) {
|
|
66778
|
+
if (!this.company || !this.company.length) throw new AFError(AFErrorCode.MISSING_ABRA_COMPANY, `Can't query AFApiClient without providing company path component first.`);
|
|
66779
|
+
let url = this._buildQueryUrl(entityPath, format, options);
|
|
66780
|
+
url = addParamToUrl(url, "report-name", options.reportName);
|
|
66781
|
+
url = addParamToUrl(url, "report-lang", options.reportLang);
|
|
66782
|
+
this._logger.debug(url);
|
|
66783
|
+
try {
|
|
66784
|
+
const raw$1 = await this._fetch(url, { signal: options.abortController?.signal });
|
|
66785
|
+
if (raw$1.status >= 400 && raw$1.status < 600) {
|
|
66786
|
+
let details = "";
|
|
66787
|
+
if ((raw$1.headers.get("content-type") || "").includes("application/json")) {
|
|
66788
|
+
const json = await raw$1.json().catch(() => null);
|
|
66789
|
+
details = this._extractAbraErrors(json);
|
|
66790
|
+
}
|
|
66791
|
+
throw new AFError(AFErrorCode.ABRA_FLEXI_ERROR, `${raw$1.status} ${raw$1.statusText}${details ? ` — ${details}` : ""}`);
|
|
66792
|
+
}
|
|
66793
|
+
return {
|
|
66794
|
+
blob: await raw$1.blob(),
|
|
66795
|
+
contentType: raw$1.headers.get("content-type") || "application/octet-stream",
|
|
66796
|
+
filename: parseContentDispositionFilename(raw$1.headers.get("content-disposition"))
|
|
66797
|
+
};
|
|
66698
66798
|
} catch (e) {
|
|
66699
66799
|
if (!(e instanceof AFError)) {
|
|
66700
|
-
|
|
66800
|
+
this._logger.error(e);
|
|
66701
66801
|
e = new AFError(AFErrorCode.UNKNOWN, e.toString());
|
|
66702
66802
|
}
|
|
66703
66803
|
throw e;
|
|
66704
66804
|
}
|
|
66705
66805
|
}
|
|
66706
|
-
async
|
|
66806
|
+
async queryFile(entity, format, options = {}) {
|
|
66807
|
+
return this.queryFileRaw(entity.EntityPath, format, options);
|
|
66808
|
+
}
|
|
66809
|
+
async query(entity, options = {}) {
|
|
66707
66810
|
const res = this.queryRaw(entity.EntityPath, options);
|
|
66708
66811
|
try {
|
|
66709
66812
|
const rawData = await res;
|
|
@@ -66712,7 +66815,7 @@ var AFApiClient = class {
|
|
|
66712
66815
|
return data;
|
|
66713
66816
|
} catch (e) {
|
|
66714
66817
|
if (!(e instanceof AFError)) {
|
|
66715
|
-
|
|
66818
|
+
this._logger.error(e);
|
|
66716
66819
|
e = new AFError(AFErrorCode.UNKNOWN, e.toString());
|
|
66717
66820
|
}
|
|
66718
66821
|
throw e;
|
|
@@ -66759,7 +66862,7 @@ var AFApiClient = class {
|
|
|
66759
66862
|
out.push(res);
|
|
66760
66863
|
} catch (e) {
|
|
66761
66864
|
if (!(e instanceof AFError)) {
|
|
66762
|
-
|
|
66865
|
+
this._logger.error(e);
|
|
66763
66866
|
e = new AFError(AFErrorCode.UNKNOWN, e.toString());
|
|
66764
66867
|
}
|
|
66765
66868
|
throw e;
|
|
@@ -66770,7 +66873,7 @@ var AFApiClient = class {
|
|
|
66770
66873
|
if (!options.noUpdateStitkyCache) await this._stitkyCache.fetchTick();
|
|
66771
66874
|
return out;
|
|
66772
66875
|
}
|
|
66773
|
-
async populate(entities, options) {
|
|
66876
|
+
async populate(entities, options = {}) {
|
|
66774
66877
|
const fetchBy = [];
|
|
66775
66878
|
for (const en of entities) {
|
|
66776
66879
|
if (typeof en.id !== "undefined" && en.id !== null) {
|
|
@@ -66809,18 +66912,19 @@ var AFApiClient = class {
|
|
|
66809
66912
|
if (!en) continue;
|
|
66810
66913
|
const oKeys = Object.keys(enQ);
|
|
66811
66914
|
for (const okey of oKeys) this._decodeProperty(en, okey, enQ);
|
|
66915
|
+
en._isNew = false;
|
|
66812
66916
|
}
|
|
66813
66917
|
if (!options.noUpdateStitkyCache) await this._stitkyCache.fetchTick();
|
|
66814
66918
|
} catch (e) {
|
|
66815
66919
|
if (!(e instanceof AFError)) {
|
|
66816
|
-
|
|
66920
|
+
this._logger.error(e);
|
|
66817
66921
|
e = new AFError(AFErrorCode.UNKNOWN, e.toString());
|
|
66818
66922
|
}
|
|
66819
66923
|
throw e;
|
|
66820
66924
|
}
|
|
66821
66925
|
return entities;
|
|
66822
66926
|
}
|
|
66823
|
-
async populateOne(entity, options) {
|
|
66927
|
+
async populateOne(entity, options = {}) {
|
|
66824
66928
|
const res = await this.populate([entity], options);
|
|
66825
66929
|
if (!res || !res.length) throw new AFError(AFErrorCode.OBJECT_NOT_FOUND, `${entity} object not found. Kod / ID: ${entity.kod} / ${entity.id}`);
|
|
66826
66930
|
return res[0];
|
|
@@ -66833,14 +66937,15 @@ var AFApiClient = class {
|
|
|
66833
66937
|
const ent = new entity(this._stitkyCache);
|
|
66834
66938
|
if (id.id) ent.id = id.id;
|
|
66835
66939
|
if (id.kod) ent.kod = id.kod;
|
|
66940
|
+
ent._isNew = false;
|
|
66836
66941
|
return ent;
|
|
66837
66942
|
}
|
|
66838
66943
|
async saveRaw(entityPath, data, options) {
|
|
66839
66944
|
if (!this.company || !this.company.length) throw new AFError(AFErrorCode.MISSING_ABRA_COMPANY, `Can't query AFApiClient without providing company path component first.`);
|
|
66840
66945
|
if (!options) options = {};
|
|
66841
66946
|
let url = this._url + "/c/" + this.company + "/" + entityPath + ".json";
|
|
66842
|
-
|
|
66843
|
-
|
|
66947
|
+
this._logger.debug(url);
|
|
66948
|
+
this._logger.debug(data);
|
|
66844
66949
|
if (options.removeStitky) data["stitky@removeAll"] = "true";
|
|
66845
66950
|
try {
|
|
66846
66951
|
const raw$1 = await this._fetch(url, {
|
|
@@ -66849,98 +66954,189 @@ var AFApiClient = class {
|
|
|
66849
66954
|
headers: { "Content-Type": "application/json" },
|
|
66850
66955
|
body: JSON.stringify({ winstrom: [{ [entityPath]: data }] })
|
|
66851
66956
|
});
|
|
66957
|
+
const json = await raw$1.json().catch(() => null);
|
|
66958
|
+
this._logger.debug(json);
|
|
66852
66959
|
if (raw$1.status >= 400 && raw$1.status < 600) {
|
|
66853
|
-
|
|
66854
|
-
throw new AFError(AFErrorCode.ABRA_FLEXI_ERROR, `${raw$1.status} ${raw$1.statusText}`);
|
|
66960
|
+
const details = this._extractAbraErrors(json);
|
|
66961
|
+
throw new AFError(AFErrorCode.ABRA_FLEXI_ERROR, `${raw$1.status} ${raw$1.statusText}${details ? ` — ${details}` : ""}`);
|
|
66855
66962
|
}
|
|
66856
|
-
const
|
|
66857
|
-
|
|
66858
|
-
|
|
66963
|
+
const jres = json?.winstrom;
|
|
66964
|
+
if (jres && jres["success"] === "false") {
|
|
66965
|
+
const details = this._extractAbraErrors(json);
|
|
66966
|
+
throw new AFError(AFErrorCode.ABRA_FLEXI_ERROR, `Save of ${entityPath} failed${details ? ` — ${details}` : ""}`);
|
|
66967
|
+
}
|
|
66968
|
+
return Array.isArray(jres?.results) ? jres.results : [];
|
|
66859
66969
|
} catch (e) {
|
|
66860
66970
|
if (!(e instanceof AFError)) {
|
|
66861
|
-
|
|
66971
|
+
this._logger.error(e);
|
|
66862
66972
|
e = new AFError(AFErrorCode.UNKNOWN, e.toString());
|
|
66863
66973
|
}
|
|
66864
66974
|
throw e;
|
|
66865
66975
|
}
|
|
66866
66976
|
}
|
|
66867
66977
|
async save(entity, options) {
|
|
66978
|
+
if (entity.isNew) {
|
|
66979
|
+
const uvs = entity["uzivatelske-vazby"];
|
|
66980
|
+
if (Array.isArray(uvs) && uvs.length > 0) throw new AFError(AFErrorCode.FORBIDDEN_OPERATION, `Creating ${entity.constructor.EntityName} with 'uzivatelske-vazby' is not supported by the API. Workaround: save the entity first without user relations, then add them in a second update (save) request.`);
|
|
66981
|
+
}
|
|
66868
66982
|
const obj = this._encodeEntity(entity);
|
|
66869
66983
|
const res = this.saveRaw(entity.constructor.EntityPath, obj, options);
|
|
66870
66984
|
try {
|
|
66871
|
-
await res;
|
|
66985
|
+
const results = await res;
|
|
66986
|
+
this._applySaveResultToEntity(entity, results);
|
|
66987
|
+
entity._isNew = false;
|
|
66872
66988
|
return entity;
|
|
66873
66989
|
} catch (e) {
|
|
66874
66990
|
if (!(e instanceof AFError)) {
|
|
66875
|
-
|
|
66991
|
+
this._logger.error(e);
|
|
66876
66992
|
e = new AFError(AFErrorCode.UNKNOWN, e.toString());
|
|
66877
66993
|
}
|
|
66878
66994
|
throw e;
|
|
66879
66995
|
}
|
|
66880
66996
|
}
|
|
66881
|
-
async deleteRaw(entityPath, id, options) {
|
|
66997
|
+
async deleteRaw(entityPath, id, options = {}) {
|
|
66882
66998
|
if (!this.company || !this.company.length) throw new AFError(AFErrorCode.MISSING_ABRA_COMPANY, `Can't query AFApiClient without providing company path component first.`);
|
|
66883
66999
|
if (!id && typeof id !== "number" || id === "") throw new AFError(AFErrorCode.MISSING_ID, `Can't delete entity without knowing it's id.`);
|
|
66884
|
-
let url
|
|
66885
|
-
|
|
66886
|
-
|
|
66887
|
-
|
|
67000
|
+
let url;
|
|
67001
|
+
let fetchOptions;
|
|
67002
|
+
if (options.asUserRelation) {
|
|
67003
|
+
url = this._url + "/c/" + this.company + "/" + entityPath + ".json";
|
|
67004
|
+
fetchOptions = {
|
|
67005
|
+
signal: options.abortController?.signal,
|
|
67006
|
+
method: "PUT",
|
|
67007
|
+
body: JSON.stringify({ winstrom: [{
|
|
67008
|
+
[entityPath]: { id },
|
|
67009
|
+
[entityPath + "@action"]: "delete"
|
|
67010
|
+
}] })
|
|
67011
|
+
};
|
|
67012
|
+
} else {
|
|
67013
|
+
url = this._url + "/c/" + this.company + "/" + entityPath + "/" + id + ".json";
|
|
67014
|
+
fetchOptions = {
|
|
66888
67015
|
signal: options.abortController?.signal,
|
|
66889
67016
|
method: "DELETE"
|
|
66890
|
-
}
|
|
66891
|
-
|
|
66892
|
-
|
|
66893
|
-
|
|
66894
|
-
|
|
67017
|
+
};
|
|
67018
|
+
}
|
|
67019
|
+
this._logger.debug(url);
|
|
67020
|
+
try {
|
|
67021
|
+
const raw$1 = await this._fetch(url, fetchOptions);
|
|
67022
|
+
const json = await raw$1.json().catch(() => null);
|
|
67023
|
+
this._logger.debug(json);
|
|
67024
|
+
if (raw$1.status >= 400 && raw$1.status < 600) {
|
|
67025
|
+
const details = this._extractAbraErrors(json);
|
|
67026
|
+
throw new AFError(AFErrorCode.ABRA_FLEXI_ERROR, `${raw$1.status} ${raw$1.statusText}${details ? ` — ${details}` : ""}`);
|
|
67027
|
+
}
|
|
67028
|
+
const jres = json?.winstrom;
|
|
67029
|
+
if (jres && jres["success"] === "false") {
|
|
67030
|
+
const details = this._extractAbraErrors(json);
|
|
67031
|
+
throw new AFError(AFErrorCode.ABRA_FLEXI_ERROR, `Delete of ${entityPath} failed${details ? ` — ${details}` : ""}`);
|
|
67032
|
+
}
|
|
66895
67033
|
} catch (e) {
|
|
66896
67034
|
if (!(e instanceof AFError)) {
|
|
66897
|
-
|
|
67035
|
+
this._logger.error(e);
|
|
66898
67036
|
e = new AFError(AFErrorCode.UNKNOWN, e.toString());
|
|
66899
67037
|
}
|
|
66900
67038
|
throw e;
|
|
66901
67039
|
}
|
|
66902
67040
|
}
|
|
66903
|
-
async delete(entity, options) {
|
|
67041
|
+
async delete(entity, options = {}) {
|
|
66904
67042
|
if (entity.isNew) return true;
|
|
66905
|
-
const
|
|
67043
|
+
const entityPath = entity.constructor.EntityPath;
|
|
67044
|
+
const res = this.deleteRaw(entityPath, entity.id, {
|
|
67045
|
+
...options,
|
|
67046
|
+
asUserRelation: entity instanceof AFUzivatelskaVazba
|
|
67047
|
+
});
|
|
66906
67048
|
try {
|
|
66907
67049
|
await res;
|
|
66908
67050
|
return true;
|
|
66909
67051
|
} catch (e) {
|
|
66910
67052
|
if (!(e instanceof AFError)) {
|
|
66911
|
-
|
|
67053
|
+
this._logger.error(e);
|
|
66912
67054
|
e = new AFError(AFErrorCode.UNKNOWN, e.toString());
|
|
66913
67055
|
}
|
|
66914
67056
|
throw e;
|
|
66915
67057
|
}
|
|
66916
67058
|
}
|
|
66917
|
-
async
|
|
67059
|
+
async callEntityActionRaw(entityPath, id, actionName, options = {}) {
|
|
66918
67060
|
if (!this.company || !this.company.length) throw new AFError(AFErrorCode.MISSING_ABRA_COMPANY, `Can't query AFApiClient without providing company path component first.`);
|
|
66919
|
-
|
|
66920
|
-
if (!
|
|
66921
|
-
const
|
|
66922
|
-
|
|
67061
|
+
if (id === void 0 || id === null || id === "") throw new AFError(AFErrorCode.MISSING_ID, `Can't call action '${actionName}' on ${entityPath} without id.`);
|
|
67062
|
+
if (!actionName || !actionName.length) throw new AFError(AFErrorCode.UNKNOWN, `Action name must be a non-empty string.`);
|
|
67063
|
+
const url = this._url + "/c/" + this.company + "/" + entityPath + ".json";
|
|
67064
|
+
this._logger.debug(url);
|
|
66923
67065
|
try {
|
|
66924
67066
|
const raw$1 = await this._fetch(url, {
|
|
66925
67067
|
signal: options.abortController?.signal,
|
|
66926
67068
|
method: "PUT",
|
|
67069
|
+
headers: { "Content-Type": "application/json" },
|
|
66927
67070
|
body: JSON.stringify({ winstrom: [{
|
|
66928
67071
|
[entityPath]: { id },
|
|
66929
|
-
[entityPath + "@action"]:
|
|
67072
|
+
[entityPath + "@action"]: actionName
|
|
66930
67073
|
}] })
|
|
66931
67074
|
});
|
|
66932
|
-
|
|
66933
|
-
|
|
66934
|
-
|
|
66935
|
-
|
|
67075
|
+
const json = await raw$1.json().catch(() => null);
|
|
67076
|
+
this._logger.debug(json);
|
|
67077
|
+
if (raw$1.status >= 400 && raw$1.status < 600) {
|
|
67078
|
+
const details = this._extractAbraErrors(json);
|
|
67079
|
+
throw new AFError(AFErrorCode.ABRA_FLEXI_ERROR, `${raw$1.status} ${raw$1.statusText}${details ? ` — ${details}` : ""}`);
|
|
67080
|
+
}
|
|
67081
|
+
const jres = json?.winstrom;
|
|
67082
|
+
if (jres && jres["success"] === "false") {
|
|
67083
|
+
const details = this._extractAbraErrors(json);
|
|
67084
|
+
throw new AFError(AFErrorCode.ABRA_FLEXI_ERROR, `Action '${actionName}' on ${entityPath} failed${details ? ` — ${details}` : ""}`);
|
|
67085
|
+
}
|
|
67086
|
+
return true;
|
|
66936
67087
|
} catch (e) {
|
|
66937
67088
|
if (!(e instanceof AFError)) {
|
|
66938
|
-
|
|
67089
|
+
this._logger.error(e);
|
|
66939
67090
|
e = new AFError(AFErrorCode.UNKNOWN, e.toString());
|
|
66940
67091
|
}
|
|
66941
67092
|
throw e;
|
|
66942
67093
|
}
|
|
66943
67094
|
}
|
|
67095
|
+
async callEntityAction(entity, actionName, options = {}) {
|
|
67096
|
+
if (entity.isNew) throw new AFError(AFErrorCode.RELATED_INSTANCE_NOT_SAVED, `Can't call action '${actionName}' on an unsaved ${entity.constructor.EntityName}. Save it first.`);
|
|
67097
|
+
if (entity.id === void 0 || entity.id === null) throw new AFError(AFErrorCode.MISSING_ID, `Can't call action '${actionName}' on ${entity.constructor.EntityName} without id.`);
|
|
67098
|
+
const entityPath = entity.constructor.EntityPath;
|
|
67099
|
+
return this.callEntityActionRaw(entityPath, entity.id, actionName, options);
|
|
67100
|
+
}
|
|
67101
|
+
_applySaveResultToEntity(entity, results) {
|
|
67102
|
+
if (!Array.isArray(results) || !results.length) return;
|
|
67103
|
+
const entityPath = entity.constructor.EntityPath;
|
|
67104
|
+
let r = results.find((res) => res && typeof res.ref === "string" && res.ref.includes(`/${entityPath}/`));
|
|
67105
|
+
if (!r) {
|
|
67106
|
+
const refless = results.filter((res) => res && typeof res.ref !== "string");
|
|
67107
|
+
if (refless.length === 1 && results.length === 1) r = refless[0];
|
|
67108
|
+
}
|
|
67109
|
+
if (!r || r.id === void 0 || r.id === null) return;
|
|
67110
|
+
const newId = typeof r.id === "number" ? r.id : Number(r.id);
|
|
67111
|
+
if (Number.isFinite(newId)) {
|
|
67112
|
+
entity.id = newId;
|
|
67113
|
+
entity._orig.id = newId;
|
|
67114
|
+
}
|
|
67115
|
+
}
|
|
67116
|
+
_extractAbraErrors(json) {
|
|
67117
|
+
if (!json) return "";
|
|
67118
|
+
const winstrom = json.winstrom;
|
|
67119
|
+
if (!winstrom) return "";
|
|
67120
|
+
const messages = [];
|
|
67121
|
+
if (typeof winstrom.message === "string" && winstrom.message.length) messages.push(winstrom.message);
|
|
67122
|
+
const results = winstrom.results;
|
|
67123
|
+
if (Array.isArray(results)) for (const r of results) {
|
|
67124
|
+
if (!r || !Array.isArray(r.errors)) continue;
|
|
67125
|
+
for (const err of r.errors) {
|
|
67126
|
+
if (!err) continue;
|
|
67127
|
+
if (typeof err === "string") {
|
|
67128
|
+
messages.push(err);
|
|
67129
|
+
continue;
|
|
67130
|
+
}
|
|
67131
|
+
const parts = [];
|
|
67132
|
+
if (err.code) parts.push(`[${err.code}]`);
|
|
67133
|
+
if (err.for) parts.push(`(${err.for})`);
|
|
67134
|
+
if (err.message) parts.push(err.message);
|
|
67135
|
+
if (parts.length) messages.push(parts.join(" "));
|
|
67136
|
+
}
|
|
67137
|
+
}
|
|
67138
|
+
return messages.join("; ");
|
|
67139
|
+
}
|
|
66944
67140
|
_decodeEntityObj(entity, obj) {
|
|
66945
67141
|
if (!obj) return [];
|
|
66946
67142
|
if (typeof obj === "string") {
|
|
@@ -66952,6 +67148,7 @@ var AFApiClient = class {
|
|
|
66952
67148
|
const ent = new entity(this._stitkyCache);
|
|
66953
67149
|
const oKeys = Object.keys(o);
|
|
66954
67150
|
for (const okey of oKeys) this._decodeProperty(ent, okey, o);
|
|
67151
|
+
ent._isNew = false;
|
|
66955
67152
|
res.push(ent);
|
|
66956
67153
|
}
|
|
66957
67154
|
return res;
|
|
@@ -67008,7 +67205,7 @@ var AFApiClient = class {
|
|
|
67008
67205
|
return;
|
|
67009
67206
|
}
|
|
67010
67207
|
if (!(val instanceof AFEntity)) throw new AFError(AFErrorCode.UNKNOWN, `Key '${key}' on ${entity.constructor.EntityName}(id: ${entity.id}) referencing not AFEntity instance`);
|
|
67011
|
-
if (val.isNew) throw new AFError(AFErrorCode.RELATED_INSTANCE_NOT_SAVED, `Key '${key}' on ${entity.constructor.EntityName}(id: ${entity.id})
|
|
67208
|
+
if (val.isNew) throw new AFError(AFErrorCode.RELATED_INSTANCE_NOT_SAVED, `Key '${key}' on ${entity.constructor.EntityName}(id: ${entity.id}) references an unsaved instance. Save it first, or use createIdStub() to reference an existing entity by id/kod.`);
|
|
67012
67209
|
if (typeof val.id === "undefined") obj[key] = `code:${val.kod}`;
|
|
67013
67210
|
else obj[key] = val.id;
|
|
67014
67211
|
return;
|
|
@@ -67018,6 +67215,28 @@ var AFApiClient = class {
|
|
|
67018
67215
|
if (entity instanceof AFPriloha && key === "content") obj["content@encoding"] = "base64";
|
|
67019
67216
|
}
|
|
67020
67217
|
};
|
|
67218
|
+
function makeFilteringLogger(target, level) {
|
|
67219
|
+
const threshold = LOG_LEVEL_RANK[level];
|
|
67220
|
+
const make = (lvl) => {
|
|
67221
|
+
if (LOG_LEVEL_RANK[lvl] > threshold) return () => {};
|
|
67222
|
+
return (...args) => target[lvl](...args);
|
|
67223
|
+
};
|
|
67224
|
+
return {
|
|
67225
|
+
debug: make("debug"),
|
|
67226
|
+
info: make("info"),
|
|
67227
|
+
warn: make("warn"),
|
|
67228
|
+
error: make("error")
|
|
67229
|
+
};
|
|
67230
|
+
}
|
|
67231
|
+
function parseContentDispositionFilename(header) {
|
|
67232
|
+
if (!header) return void 0;
|
|
67233
|
+
const ext = /filename\*=(?:UTF-8'')?([^;]+)/i.exec(header);
|
|
67234
|
+
if (ext) try {
|
|
67235
|
+
return decodeURIComponent(ext[1].trim().replace(/^"|"$/g, ""));
|
|
67236
|
+
} catch {}
|
|
67237
|
+
const plain = /filename=("([^"]+)"|([^;]+))/i.exec(header);
|
|
67238
|
+
if (plain) return (plain[2] ?? plain[3] ?? "").trim();
|
|
67239
|
+
}
|
|
67021
67240
|
|
|
67022
67241
|
//#endregion
|
|
67023
67242
|
//#region src/abra/AFApiSession.ts
|