node-autotask 0.37.1 → 0.38.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/CHANGELOG.md +17 -0
- package/MCP_TOOL_CATALOG.json +127 -44
- package/MCP_TOOL_MANIFEST.md +8 -8
- package/N8N_MCP_MANIFEST.json +4 -4
- package/N8N_MCP_MANIFEST.md +5 -5
- package/README.md +3 -0
- package/capabilities.json +2454 -1698
- package/dist/capabilities.cjs +2 -2
- package/dist/capabilities.js +1 -1
- package/dist/{chunk-7IADHNBZ.js → chunk-4QHGCYEI.js} +2255 -1946
- package/dist/chunk-5VHSLJMR.js +71 -0
- package/dist/{chunk-HBYKTXN3.cjs → chunk-73JMTV4O.cjs} +2453 -1697
- package/dist/{chunk-PFFW4DIK.cjs → chunk-7FNKFNIJ.cjs} +285 -285
- package/dist/{chunk-25VGFT5M.cjs → chunk-DCTUBUIZ.cjs} +143 -143
- package/dist/{chunk-7WI7FBPB.js → chunk-IY3WCPCK.js} +2 -2
- package/dist/{chunk-TBFXRNNE.cjs → chunk-IY5QZZVF.cjs} +2389 -2080
- package/dist/{chunk-PFJAMFUL.js → chunk-KPYXO7F5.js} +2 -2
- package/dist/chunk-MJTKK3QG.cjs +71 -0
- package/dist/{chunk-KQ6HMBB4.js → chunk-U3K6IDKB.js} +2453 -1697
- package/dist/{index-CqepIQfp.d.cts → index-BFbUB5YD.d.cts} +28 -8
- package/dist/{index-m-7fOwFl.d.cts → index-BQVStxwi.d.cts} +15 -1
- package/dist/{index-m-7fOwFl.d.ts → index-BQVStxwi.d.ts} +15 -1
- package/dist/{index-BDqw377y.d.ts → index-Q-4HQEEb.d.ts} +28 -8
- package/dist/index.cjs +27 -22
- package/dist/index.d.cts +26 -5
- package/dist/index.d.ts +26 -5
- package/dist/index.js +9 -4
- package/dist/operations/index.cjs +247 -227
- package/dist/operations/index.d.cts +16 -4
- package/dist/operations/index.d.ts +16 -4
- package/dist/operations/index.js +58 -38
- package/dist/pool.cjs +10 -9
- package/dist/pool.d.cts +2 -2
- package/dist/pool.d.ts +2 -2
- package/dist/pool.js +5 -4
- package/dist/resources/index.cjs +5 -4
- package/dist/resources/index.d.cts +2 -2
- package/dist/resources/index.d.ts +2 -2
- package/dist/resources/index.js +4 -3
- package/dist/types/index.d.cts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/untrusted.cjs +24 -70
- package/dist/untrusted.js +12 -58
- package/docs/API.md +101 -0
- package/examples/mcp/manifest.ts +5 -0
- package/examples/mcp/rank.ts +62 -0
- package/examples/mcp/tools-meta.ts +6 -1
- package/package.json +1 -1
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CAPABILITIES
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-U3K6IDKB.js";
|
|
4
|
+
import {
|
|
5
|
+
wrapUntrusted
|
|
6
|
+
} from "./chunk-5VHSLJMR.js";
|
|
4
7
|
|
|
5
8
|
// src/auth.ts
|
|
6
9
|
var AUTH_HEADERS = {
|
|
@@ -2067,7 +2070,7 @@ function validateCondition(cond, path) {
|
|
|
2067
2070
|
}
|
|
2068
2071
|
if (op === "in" || op === "notIn") {
|
|
2069
2072
|
if (!Array.isArray(cond.value)) {
|
|
2070
|
-
throw new AutotaskConfigError(`Operator "${op}" requires an array "value" at ${path}`);
|
|
2073
|
+
throw new AutotaskConfigError(`Operator "${op}" requires an array or comma-separated string "value" at ${path}`);
|
|
2071
2074
|
}
|
|
2072
2075
|
}
|
|
2073
2076
|
if (cond.value !== void 0) {
|
|
@@ -2125,7 +2128,7 @@ function validateFilter(filter) {
|
|
|
2125
2128
|
}
|
|
2126
2129
|
}
|
|
2127
2130
|
function buildFilter(conditions) {
|
|
2128
|
-
const list = Array.isArray(conditions) ? conditions : [conditions];
|
|
2131
|
+
const list = normalizeInValues(Array.isArray(conditions) ? conditions : [conditions]);
|
|
2129
2132
|
if (list.length === 0) {
|
|
2130
2133
|
throw new AutotaskConfigError("buildFilter requires at least one condition");
|
|
2131
2134
|
}
|
|
@@ -2136,12 +2139,32 @@ function buildFilter(conditions) {
|
|
|
2136
2139
|
}
|
|
2137
2140
|
function normalizedConditions(filter) {
|
|
2138
2141
|
if (filter === void 0 || filter === null) return void 0;
|
|
2139
|
-
if (Array.isArray(filter)) return filter;
|
|
2140
|
-
if (isRecord2(filter) && Array.isArray(filter.filter)) return filter.filter;
|
|
2142
|
+
if (Array.isArray(filter)) return normalizeInValues(filter);
|
|
2143
|
+
if (isRecord2(filter) && Array.isArray(filter.filter)) return normalizeInValues(filter.filter);
|
|
2141
2144
|
throw new AutotaskConfigError(
|
|
2142
2145
|
`filter must be a conditions array or { filter: [...] } (e.g. { filter: [{ field: 'status', op: 'eq', value: 'Open' }] }) \u2014 got ${refusedFilterShape(filter)}`
|
|
2143
2146
|
);
|
|
2144
2147
|
}
|
|
2148
|
+
function normalizeInValues(conditions, path = "filter") {
|
|
2149
|
+
const out = conditions.map((cond, i) => normalizeInCondition(cond, `${path}[${i}]`));
|
|
2150
|
+
return out.every((cond, i) => cond === conditions[i]) ? conditions : out;
|
|
2151
|
+
}
|
|
2152
|
+
function normalizeInCondition(cond, path) {
|
|
2153
|
+
if (!isRecord2(cond)) return cond;
|
|
2154
|
+
if (Array.isArray(cond.items)) {
|
|
2155
|
+
const items = normalizeInValues(cond.items, `${path}.items`);
|
|
2156
|
+
return items === cond.items ? cond : { ...cond, items };
|
|
2157
|
+
}
|
|
2158
|
+
if (cond.op !== "in" && cond.op !== "notIn" || typeof cond.value !== "string") return cond;
|
|
2159
|
+
const tokens = cond.value.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
2160
|
+
if (tokens.length === 0) {
|
|
2161
|
+
throw new AutotaskConfigError(
|
|
2162
|
+
`Operator "${cond.op}" at ${path} has an empty value list \u2014 pass an array or a comma-separated string with at least one value (e.g. "1,2,3")`
|
|
2163
|
+
);
|
|
2164
|
+
}
|
|
2165
|
+
const value = cond.udf === true ? tokens : tokens.map((t) => String(Number(t)) === t ? Number(t) : t);
|
|
2166
|
+
return { ...cond, value };
|
|
2167
|
+
}
|
|
2145
2168
|
function refusedFilterShape(filter) {
|
|
2146
2169
|
if (isRecord2(filter) && !("filter" in filter)) return "an object missing the filter field";
|
|
2147
2170
|
if (isRecord2(filter)) return "an object whose filter is not a conditions array";
|
|
@@ -2149,6 +2172,19 @@ function refusedFilterShape(filter) {
|
|
|
2149
2172
|
if (t === "object" || t === "function") return "an object";
|
|
2150
2173
|
return t === "string" ? "a string" : `a ${t}`;
|
|
2151
2174
|
}
|
|
2175
|
+
function assertNoRelationLeaf(conditions, operation) {
|
|
2176
|
+
if (!Array.isArray(conditions)) return;
|
|
2177
|
+
for (const c of conditions) {
|
|
2178
|
+
if (!isRecord2(c)) continue;
|
|
2179
|
+
if (Array.isArray(c.items)) assertNoRelationLeaf(c.items, operation);
|
|
2180
|
+
else if (c.udf !== true && typeof c.field === "string" && c.field.includes(".")) {
|
|
2181
|
+
throw new AutotaskConfigError(
|
|
2182
|
+
`${operation}: filter field "${c.field}" is a relation filter that was not resolved on this path \u2014 the request was not issued`,
|
|
2183
|
+
{ operation, suggestedAction: "Use a query surface that resolves relation filters (query, queryAll, queryBounded, count, operations.aggregate), or filter by the <relation>ID field directly." }
|
|
2184
|
+
);
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2152
2188
|
|
|
2153
2189
|
// src/http/request-builders.ts
|
|
2154
2190
|
function assertSafePath(path) {
|
|
@@ -17452,6 +17488,7 @@ var HttpClient = class {
|
|
|
17452
17488
|
const url = buildUrl(this.baseUrl, opts.path, opts.query);
|
|
17453
17489
|
const headers = withAuthHeaders(opts.headers, this.auth);
|
|
17454
17490
|
assertCredentialShape(headers, this.auth);
|
|
17491
|
+
if (opts.path.includes("/query")) assertNoQueryRelationLeaf(opts);
|
|
17455
17492
|
if (opts.body !== void 0) headers["Content-Type"] = "application/json";
|
|
17456
17493
|
const bodyText = opts.body !== void 0 ? JSON.stringify(opts.body) : void 0;
|
|
17457
17494
|
const correlationId = newCorrelationId();
|
|
@@ -17539,6 +17576,20 @@ var HttpClient = class {
|
|
|
17539
17576
|
}
|
|
17540
17577
|
}
|
|
17541
17578
|
};
|
|
17579
|
+
function assertNoQueryRelationLeaf(opts) {
|
|
17580
|
+
const operation = opts.operation ?? opts.path;
|
|
17581
|
+
const body = opts.body;
|
|
17582
|
+
if (body !== null && typeof body === "object") assertNoRelationLeaf(body.filter, operation);
|
|
17583
|
+
const search = opts.query?.["search"];
|
|
17584
|
+
if (typeof search !== "string") return;
|
|
17585
|
+
let parsed;
|
|
17586
|
+
try {
|
|
17587
|
+
parsed = JSON.parse(search);
|
|
17588
|
+
} catch {
|
|
17589
|
+
return;
|
|
17590
|
+
}
|
|
17591
|
+
if (parsed !== null && typeof parsed === "object") assertNoRelationLeaf(parsed.filter, operation);
|
|
17592
|
+
}
|
|
17542
17593
|
|
|
17543
17594
|
// src/operations/requiredFields.ts
|
|
17544
17595
|
function effectiveRequiredFields(record, zoneFacts) {
|
|
@@ -17663,395 +17714,1227 @@ function resolveFieldsPreset(name, resourceKey, explicitIncludeFields) {
|
|
|
17663
17714
|
return [...preset.fields];
|
|
17664
17715
|
}
|
|
17665
17716
|
|
|
17666
|
-
// src/
|
|
17667
|
-
var
|
|
17668
|
-
|
|
17669
|
-
|
|
17670
|
-
|
|
17671
|
-
|
|
17672
|
-
|
|
17673
|
-
function describeResponseShape(body) {
|
|
17674
|
-
if (body === null) return "null";
|
|
17675
|
-
if (Array.isArray(body)) return `array(len=${body.length})`;
|
|
17676
|
-
if (typeof body !== "object") return typeof body;
|
|
17677
|
-
const rec = body;
|
|
17678
|
-
const keys = Object.keys(rec).sort();
|
|
17679
|
-
const typeOf = (v) => v === null ? "null" : Array.isArray(v) ? "array" : typeof v;
|
|
17680
|
-
const parts = keys.slice(0, 16).map((k) => `${k}:${typeOf(rec[k])}`);
|
|
17681
|
-
const more = keys.length > 16 ? `, +${keys.length - 16} more` : "";
|
|
17682
|
-
return `{ ${parts.join(", ")}${more} }`;
|
|
17683
|
-
}
|
|
17684
|
-
function defaultMarkerToken(data) {
|
|
17685
|
-
for (const field of ["name", "title"]) {
|
|
17686
|
-
const v = data[field];
|
|
17687
|
-
if (typeof v === "string" && v.trim() !== "") return v.trim();
|
|
17717
|
+
// src/operations/expandResourceNames.ts
|
|
17718
|
+
var EXPAND_RESOURCE_NAMES_MAX_IDS = 500;
|
|
17719
|
+
function composeName(record) {
|
|
17720
|
+
const parts = [];
|
|
17721
|
+
for (const field of ["firstName", "middleName", "lastName"]) {
|
|
17722
|
+
const value = record[field];
|
|
17723
|
+
if (typeof value === "string" && value.trim() !== "") parts.push(value.trim());
|
|
17688
17724
|
}
|
|
17689
|
-
return null;
|
|
17690
|
-
}
|
|
17691
|
-
var CONTEXT_ABSENT_MARKER = "#context-absent";
|
|
17692
|
-
function contextAbsentKey(path) {
|
|
17693
|
-
return path + "\0context-absent";
|
|
17694
|
-
}
|
|
17695
|
-
function noteCharsTruncationMarker(charsRemoved) {
|
|
17696
|
-
return `\u2026 [+${charsRemoved} chars truncated]`;
|
|
17697
|
-
}
|
|
17698
|
-
function truncateNoteChars(items, cap, fields) {
|
|
17699
|
-
let truncated = 0;
|
|
17700
|
-
const out = items.map((item) => {
|
|
17701
|
-
if (!isRecord2(item)) return item;
|
|
17702
|
-
let changed = false;
|
|
17703
|
-
const copy = { ...item };
|
|
17704
|
-
for (const field of fields) {
|
|
17705
|
-
const v = copy[field];
|
|
17706
|
-
if (typeof v === "string" && v.length > cap) {
|
|
17707
|
-
const end = cap > 0 && (v.charCodeAt(cap - 1) & 64512) === 55296 ? cap - 1 : cap;
|
|
17708
|
-
copy[field] = v.slice(0, end) + noteCharsTruncationMarker(v.length - end);
|
|
17709
|
-
truncated += 1;
|
|
17710
|
-
changed = true;
|
|
17711
|
-
}
|
|
17712
|
-
}
|
|
17713
|
-
return changed ? copy : item;
|
|
17714
|
-
});
|
|
17715
|
-
return [out, truncated];
|
|
17716
|
-
}
|
|
17717
|
-
function blindEmptyReason(stamp) {
|
|
17718
|
-
return {
|
|
17719
|
-
surface: stamp.surface,
|
|
17720
|
-
kind: "blind-surface",
|
|
17721
|
-
note: stamp.note,
|
|
17722
|
-
authority: stamp.authority,
|
|
17723
|
-
remedy: "the note is the remedy \u2014 the authority op answers for a record"
|
|
17724
|
-
};
|
|
17725
|
-
}
|
|
17726
|
-
function attachUpdateTargetMissReason(err, key) {
|
|
17727
|
-
const pinned = EMPTY_REASON_STAMPS[key]?.update;
|
|
17728
|
-
if (pinned === void 0) return;
|
|
17729
|
-
err.details = { ...err.details ?? {}, emptyReason: pinned };
|
|
17725
|
+
return parts.length > 0 ? parts.join(" ") : null;
|
|
17730
17726
|
}
|
|
17731
|
-
function
|
|
17732
|
-
if (
|
|
17733
|
-
throw new AutotaskConfigError(
|
|
17727
|
+
async function expandResourceNames(client, ids2, opts = {}) {
|
|
17728
|
+
if (opts.maxIds !== void 0 && (!Number.isInteger(opts.maxIds) || opts.maxIds < 1)) {
|
|
17729
|
+
throw new AutotaskConfigError(`expandResourceNames: maxIds must be an integer >= 1 (got ${String(opts.maxIds)})`);
|
|
17734
17730
|
}
|
|
17735
|
-
const
|
|
17736
|
-
|
|
17737
|
-
|
|
17738
|
-
|
|
17739
|
-
|
|
17740
|
-
|
|
17741
|
-
return { ...entry, to };
|
|
17742
|
-
})
|
|
17743
|
-
} : {};
|
|
17744
|
-
const dataPart = spec.data !== void 0 ? { data: redactResponseBody(spec.data, policy) } : {};
|
|
17745
|
-
return {
|
|
17746
|
-
operation: spec.operation,
|
|
17747
|
-
wouldApply,
|
|
17748
|
-
target: { resource, ids: spec.ids },
|
|
17749
|
-
request: { method: spec.method, path: spec.path },
|
|
17750
|
-
...diffPart,
|
|
17751
|
-
checks: spec.checks,
|
|
17752
|
-
impact: spec.impact,
|
|
17753
|
-
simulated: true,
|
|
17754
|
-
warnings: spec.warnings ?? [],
|
|
17755
|
-
...dataPart,
|
|
17756
|
-
...spec.emptyReason !== void 0 ? { emptyReason: spec.emptyReason } : {}
|
|
17757
|
-
};
|
|
17758
|
-
}
|
|
17759
|
-
function computeFieldDiff(current, data) {
|
|
17760
|
-
const out = [];
|
|
17761
|
-
for (const [field, to] of Object.entries(data)) {
|
|
17762
|
-
if (field === "id" || to === void 0) continue;
|
|
17763
|
-
const from = current[field];
|
|
17764
|
-
if (from !== void 0 && JSON.stringify(from) === JSON.stringify(to)) continue;
|
|
17765
|
-
out.push({ field, from: from === void 0 ? null : from, to });
|
|
17731
|
+
const cap = Math.min(EXPAND_RESOURCE_NAMES_MAX_IDS, opts.maxIds ?? EXPAND_RESOURCE_NAMES_MAX_IDS);
|
|
17732
|
+
if (!Array.isArray(ids2) || ids2.length === 0) {
|
|
17733
|
+
throw new AutotaskConfigError(
|
|
17734
|
+
"expandResourceNames: ids must be a non-empty array of vendor numeric resource ids (a zero-id call is a shape error, not a no-op)",
|
|
17735
|
+
{ suggestedAction: "Pass at least one vendor numeric resource id \u2014 safe integer, or the canonical integer string for ids above 2^53." }
|
|
17736
|
+
);
|
|
17766
17737
|
}
|
|
17767
|
-
|
|
17768
|
-
|
|
17769
|
-
|
|
17770
|
-
|
|
17771
|
-
|
|
17772
|
-
if (value === void 0) continue;
|
|
17773
|
-
merged[key] = value;
|
|
17738
|
+
if (ids2.length > cap) {
|
|
17739
|
+
throw new AutotaskConfigError(
|
|
17740
|
+
`expandResourceNames: ${ids2.length} ids over the ${cap}-id cap (EXPAND_RESOURCE_NAMES_MAX_IDS = ${EXPAND_RESOURCE_NAMES_MAX_IDS}; maxIds may lower, never raise) \u2014 zero ids were fetched (pre-request)`,
|
|
17741
|
+
{ suggestedAction: `Split the call into groups of <= ${cap} ids.` }
|
|
17742
|
+
);
|
|
17774
17743
|
}
|
|
17775
|
-
|
|
17776
|
-
|
|
17777
|
-
|
|
17778
|
-
|
|
17779
|
-
|
|
17780
|
-
|
|
17781
|
-
|
|
17744
|
+
const problems = [];
|
|
17745
|
+
const canonical = [];
|
|
17746
|
+
for (let i = 0; i < ids2.length; i += 1) {
|
|
17747
|
+
const at = `ids[${i}]`;
|
|
17748
|
+
const raw = ids2[i];
|
|
17749
|
+
try {
|
|
17750
|
+
canonical[i] = parseIdentifier(raw, at);
|
|
17751
|
+
} catch {
|
|
17752
|
+
if (typeof raw === "string") {
|
|
17753
|
+
problems.push(`${at}: "${raw}" is not a vendor numeric resource id \u2014 non-numeric string identifiers are operations.resolveAny territory (the resource's findBy* helpers)`);
|
|
17754
|
+
} else if (typeof raw === "number") {
|
|
17755
|
+
problems.push(`${at}: ${raw} is not a non-negative safe integer (a JS number this large is already corrupted \u2014 pass the canonical integer string form)`);
|
|
17756
|
+
} else {
|
|
17757
|
+
problems.push(`${at}: required \u2014 the vendor numeric resource id (safe integer, or the canonical integer string for ids above 2^53)`);
|
|
17782
17758
|
}
|
|
17783
|
-
return { name: entry.name, value: entry.value };
|
|
17784
|
-
});
|
|
17785
|
-
}
|
|
17786
|
-
if (isRecord2(value)) {
|
|
17787
|
-
return Object.entries(value).map(([name, entryValue]) => ({ name, value: entryValue }));
|
|
17788
|
-
}
|
|
17789
|
-
throw new AutotaskConfigError("userDefinedFields must be an array of { name, value } entries or a name-to-value map");
|
|
17790
|
-
}
|
|
17791
|
-
function blindStampFor(stamp, filter) {
|
|
17792
|
-
if (stamp === void 0) return void 0;
|
|
17793
|
-
if (stamp.kind !== "unfiltered-blind") return stamp;
|
|
17794
|
-
const conditions = normalizedConditions(filter);
|
|
17795
|
-
return conditions === void 0 || conditions.length === 0 ? stamp : void 0;
|
|
17796
|
-
}
|
|
17797
|
-
function decodeFileData(body) {
|
|
17798
|
-
if (isRecord2(body)) {
|
|
17799
|
-
const data = body.data;
|
|
17800
|
-
if (typeof data === "string") return Buffer.from(data, "base64");
|
|
17801
|
-
if (Array.isArray(data) && data.every((b) => typeof b === "number")) {
|
|
17802
|
-
return Buffer.from(data);
|
|
17803
17759
|
}
|
|
17804
17760
|
}
|
|
17805
|
-
if (
|
|
17806
|
-
|
|
17807
|
-
|
|
17808
|
-
|
|
17809
|
-
|
|
17810
|
-
}
|
|
17811
|
-
var DEPLURAL_EXCEPTIONS = {
|
|
17812
|
-
tag_aliase: "tag_alias"
|
|
17813
|
-
};
|
|
17814
|
-
function entityNameFromPath(resourcePath) {
|
|
17815
|
-
const segment = resourcePath.slice(resourcePath.lastIndexOf("/") + 1);
|
|
17816
|
-
const snake = segment.replace(/([A-Z])/g, (_m, c, i) => (i > 0 ? `_` : "") + c.toLowerCase()).replace(/_{2,}/g, "_");
|
|
17817
|
-
if (snake.endsWith("ies")) return `${snake.slice(0, -3)}y`;
|
|
17818
|
-
if (snake.endsWith("s")) {
|
|
17819
|
-
const stripped = snake.slice(0, -1);
|
|
17820
|
-
return DEPLURAL_EXCEPTIONS[stripped] ?? stripped;
|
|
17821
|
-
}
|
|
17822
|
-
return snake;
|
|
17823
|
-
}
|
|
17824
|
-
|
|
17825
|
-
// src/resources/base/query.ts
|
|
17826
|
-
var QueryBase = class {
|
|
17827
|
-
http;
|
|
17828
|
-
config;
|
|
17829
|
-
/** Default page size for query walks (the client's resolved `pageSize`). */
|
|
17830
|
-
defaultPageSize;
|
|
17831
|
-
/** Versioned global URL segment (from the config), e.g. '/V1.0/Companies'. */
|
|
17832
|
-
get resourcePath() {
|
|
17833
|
-
return this.config.resourcePath;
|
|
17834
|
-
}
|
|
17835
|
-
/** The vendor entity this resource's ids identify (issue #116 — the `details.idRefs` label, e.g. `company`). */
|
|
17836
|
-
get entityName() {
|
|
17837
|
-
return entityNameFromPath(this.resourcePath);
|
|
17838
|
-
}
|
|
17839
|
-
/** Parent-scoped template (from the config); undefined for top-level resources. */
|
|
17840
|
-
get parentScopedPath() {
|
|
17841
|
-
return this.config.parentScopedPath;
|
|
17842
|
-
}
|
|
17843
|
-
/** Parent field used to narrow the global query (from the config). */
|
|
17844
|
-
get parentField() {
|
|
17845
|
-
return this.config.parentField;
|
|
17846
|
-
}
|
|
17847
|
-
/** The bounded client-scan caps (section 19.2; resolved per client). */
|
|
17848
|
-
get resolutionCaps() {
|
|
17849
|
-
return this.http.resolution;
|
|
17761
|
+
if (problems.length > 0) {
|
|
17762
|
+
throw new AutotaskConfigError(`expandResourceNames: ${problems.length} problem(s) found pre-request (zero wire was issued)
|
|
17763
|
+
- ` + problems.join("\n- "), {
|
|
17764
|
+
suggestedAction: "Fix every id above and call again \u2014 the ids are vendor NUMERIC resource ids only (name-to-id resolution is operations.resolveAny territory)."
|
|
17765
|
+
});
|
|
17850
17766
|
}
|
|
17851
|
-
|
|
17852
|
-
|
|
17853
|
-
|
|
17767
|
+
const unique = [];
|
|
17768
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17769
|
+
for (const id of canonical) {
|
|
17770
|
+
const k = String(id);
|
|
17771
|
+
if (!seen.has(k)) {
|
|
17772
|
+
seen.add(k);
|
|
17773
|
+
unique.push(id);
|
|
17854
17774
|
}
|
|
17855
|
-
this.http = http;
|
|
17856
|
-
this.config = config;
|
|
17857
|
-
this.defaultPageSize = clampPageSize(pageSize ?? DEFAULT_PAGE_SIZE);
|
|
17858
17775
|
}
|
|
17859
|
-
|
|
17860
|
-
|
|
17861
|
-
|
|
17862
|
-
|
|
17863
|
-
|
|
17864
|
-
const
|
|
17865
|
-
if (
|
|
17866
|
-
const
|
|
17867
|
-
|
|
17868
|
-
|
|
17869
|
-
|
|
17870
|
-
|
|
17871
|
-
|
|
17872
|
-
|
|
17873
|
-
|
|
17874
|
-
|
|
17875
|
-
|
|
17876
|
-
|
|
17776
|
+
const holder = client.resources;
|
|
17777
|
+
const cache = client.http.resourceNameCache;
|
|
17778
|
+
const fresh = opts.fresh === true;
|
|
17779
|
+
const byId = /* @__PURE__ */ new Map();
|
|
17780
|
+
for (const id of unique) {
|
|
17781
|
+
const k = String(id);
|
|
17782
|
+
if (cache !== void 0 && !fresh) {
|
|
17783
|
+
const shared = cache.inflight(id);
|
|
17784
|
+
if (shared !== void 0) {
|
|
17785
|
+
let joined;
|
|
17786
|
+
try {
|
|
17787
|
+
joined = await shared;
|
|
17788
|
+
} catch (err) {
|
|
17789
|
+
if (!isAutotaskError(err) || err.code !== "ABORTED") throw err;
|
|
17790
|
+
}
|
|
17791
|
+
if (joined !== void 0) {
|
|
17792
|
+
byId.set(k, joined);
|
|
17793
|
+
continue;
|
|
17794
|
+
}
|
|
17795
|
+
}
|
|
17796
|
+
const hit = cache.get(id);
|
|
17797
|
+
if (hit !== void 0) {
|
|
17798
|
+
byId.set(k, { name: hit.name, blind: hit.blind });
|
|
17799
|
+
continue;
|
|
17800
|
+
}
|
|
17877
17801
|
}
|
|
17878
|
-
const
|
|
17879
|
-
const
|
|
17880
|
-
|
|
17881
|
-
|
|
17882
|
-
|
|
17883
|
-
|
|
17884
|
-
|
|
17885
|
-
|
|
17886
|
-
|
|
17887
|
-
|
|
17888
|
-
|
|
17889
|
-
|
|
17890
|
-
|
|
17891
|
-
|
|
17892
|
-
|
|
17893
|
-
|
|
17894
|
-
|
|
17895
|
-
|
|
17896
|
-
|
|
17897
|
-
|
|
17898
|
-
|
|
17899
|
-
|
|
17900
|
-
|
|
17901
|
-
hasMore
|
|
17902
|
-
};
|
|
17802
|
+
const epoch = cache?.epoch ?? 0;
|
|
17803
|
+
const work = (async () => {
|
|
17804
|
+
try {
|
|
17805
|
+
const record = await holder.get(id, { fresh });
|
|
17806
|
+
const name = composeName(record);
|
|
17807
|
+
if (cache !== void 0 && name !== null) cache.set(id, { name, blind: false }, epoch);
|
|
17808
|
+
return { name, blind: false };
|
|
17809
|
+
} catch (err) {
|
|
17810
|
+
if (err instanceof NotFoundError) {
|
|
17811
|
+
const entry = { name: null, blind: true };
|
|
17812
|
+
if (cache !== void 0) cache.set(id, entry, epoch);
|
|
17813
|
+
return { name: null, blind: true };
|
|
17814
|
+
}
|
|
17815
|
+
if (isAutotaskError(err) && err.code !== "ABORTED") {
|
|
17816
|
+
const e = { code: err.code, message: err.message };
|
|
17817
|
+
if (err.httpStatus !== void 0) e.httpStatus = err.httpStatus;
|
|
17818
|
+
return { name: null, blind: false, error: e };
|
|
17819
|
+
}
|
|
17820
|
+
throw err;
|
|
17821
|
+
}
|
|
17822
|
+
})();
|
|
17823
|
+
const settled = cache !== void 0 ? await cache.track(id, work) : await work;
|
|
17824
|
+
byId.set(k, settled);
|
|
17903
17825
|
}
|
|
17904
|
-
|
|
17905
|
-
|
|
17906
|
-
const
|
|
17907
|
-
|
|
17908
|
-
return (page, pageSize) => this.fetchPage(p, page, pageSize, state);
|
|
17826
|
+
const results = [];
|
|
17827
|
+
for (const id of canonical) {
|
|
17828
|
+
const out = byId.get(String(id));
|
|
17829
|
+
results.push({ id, name: out.name, blind: out.blind, ...out.error !== void 0 ? { error: out.error } : {} });
|
|
17909
17830
|
}
|
|
17910
|
-
|
|
17911
|
-
|
|
17912
|
-
|
|
17831
|
+
const resolved = results.filter((r) => r.name !== null).length;
|
|
17832
|
+
const blind = results.filter((r) => r.blind === true).length;
|
|
17833
|
+
const failed = results.filter((r) => r.error !== void 0).length;
|
|
17834
|
+
return { results, resolved, blind, failed };
|
|
17835
|
+
}
|
|
17836
|
+
|
|
17837
|
+
// src/operations/entityType.ts
|
|
17838
|
+
function buildEntityTypeMap() {
|
|
17839
|
+
const exact = {};
|
|
17840
|
+
for (const [resource, entry] of Object.entries(RELATIONSHIP_MAP)) {
|
|
17841
|
+
const apiEntity = entry.apiEntity;
|
|
17842
|
+
if (typeof apiEntity !== "string" || apiEntity === "") continue;
|
|
17843
|
+
if (exact[apiEntity] === void 0) exact[apiEntity] = resource;
|
|
17913
17844
|
}
|
|
17914
|
-
|
|
17915
|
-
|
|
17916
|
-
|
|
17845
|
+
const aliases = {};
|
|
17846
|
+
for (const [apiEntity, resource] of Object.entries(exact)) {
|
|
17847
|
+
if (!apiEntity.endsWith("s") || apiEntity.endsWith("ss")) continue;
|
|
17848
|
+
const singular = apiEntity.slice(0, -1);
|
|
17849
|
+
if (exact[singular] !== void 0 || aliases[singular] !== void 0) continue;
|
|
17850
|
+
aliases[singular] = resource;
|
|
17917
17851
|
}
|
|
17918
|
-
|
|
17919
|
-
|
|
17920
|
-
|
|
17852
|
+
return { ...exact, ...aliases };
|
|
17853
|
+
}
|
|
17854
|
+
var ENTITY_TYPE_TO_RESOURCE = buildEntityTypeMap();
|
|
17855
|
+
var ENUMERABLE_RESOURCES = new Set(
|
|
17856
|
+
CAPABILITIES.filter((rec) => rec.resource !== "operations" && (rec.operation === "query" || rec.operation === "get")).map(
|
|
17857
|
+
(rec) => rec.resource
|
|
17858
|
+
)
|
|
17859
|
+
);
|
|
17860
|
+
function resourceForEntityType(entityType) {
|
|
17861
|
+
if (typeof entityType !== "string" || entityType === "") return null;
|
|
17862
|
+
return ENTITY_TYPE_TO_RESOURCE[entityType] ?? null;
|
|
17863
|
+
}
|
|
17864
|
+
function isEnumerableResource(resource) {
|
|
17865
|
+
if (typeof resource !== "string" || resource === "") return false;
|
|
17866
|
+
return ENUMERABLE_RESOURCES.has(resource);
|
|
17867
|
+
}
|
|
17868
|
+
|
|
17869
|
+
// src/operations/types.ts
|
|
17870
|
+
var WORKFLOW_RESOURCES = [
|
|
17871
|
+
"companies",
|
|
17872
|
+
"tickets",
|
|
17873
|
+
"contacts",
|
|
17874
|
+
"configuration_items",
|
|
17875
|
+
"projects"
|
|
17876
|
+
];
|
|
17877
|
+
|
|
17878
|
+
// src/operations/fieldCatalog.ts
|
|
17879
|
+
var MAX_FIELD_CATALOG_WIRE = 60;
|
|
17880
|
+
var FIELD_CATALOG_HONESTY_NOTE = "Live field metadata reports the SHAPE of a field (existence, requiredness, type, maxLength, read-only, queryable, picklist-ness) as FLAGS and NAMES: picklist-ness as a flag (isPickList) and picklist field names (picklistFields). With the opt-in `labels: true`, picklist rows carry the vendor's exposed picklist VALUES (labels next to each value); without it the op serves no VALUES. It cannot resolve a picklist VALUE the vendor does not expose to this API user (the W-A4 wall \u2014 e.g. the attachments Type picklist): retrying the catalog will not surface a withheld value; that gap is vendor-side, not a client defect.";
|
|
17881
|
+
var FIELD_CATALOG_OPT_KEYS = ["resources", "include", "fresh", "fields", "labels"];
|
|
17882
|
+
function projectFieldRow(raw, labels = false, udf = false) {
|
|
17883
|
+
const r = typeof raw === "object" && raw !== null ? raw : {};
|
|
17884
|
+
const name = typeof r.name === "string" ? r.name : "";
|
|
17885
|
+
const type = typeof r.dataType === "string" ? r.dataType : typeof r.type === "string" ? r.type : "";
|
|
17886
|
+
const values = labels && r.isPickList === true && Array.isArray(r.picklistValues) && r.picklistValues.length > 0 ? r.picklistValues : void 0;
|
|
17887
|
+
const referenceEntityType = typeof r.referenceEntityType === "string" ? r.referenceEntityType : "";
|
|
17888
|
+
const targetResource = resourceForEntityType(referenceEntityType);
|
|
17889
|
+
return {
|
|
17890
|
+
name,
|
|
17891
|
+
...typeof r.label === "string" && r.label !== "" ? { label: r.label } : {},
|
|
17892
|
+
type,
|
|
17893
|
+
isRequired: r.isRequired === true,
|
|
17894
|
+
isReadOnly: r.isReadOnly === true,
|
|
17895
|
+
// Issue #219 N7: a UDF row never serves the vendor's isQueryable (it is
|
|
17896
|
+
// true for UDFs while a plain filter on a UDF name is refused); it serves
|
|
17897
|
+
// the marker the filter actually needs.
|
|
17898
|
+
...udf ? { filterable: "udf-marker" } : { isQueryable: r.isQueryable === true },
|
|
17899
|
+
isPickList: r.isPickList === true,
|
|
17900
|
+
...isObjectOrArrayType(type) ? { writeShape: "replace-all" } : {},
|
|
17901
|
+
maxLength: typeof r.length === "number" ? r.length : null,
|
|
17902
|
+
referenceEntityType,
|
|
17903
|
+
targetResource,
|
|
17904
|
+
targetEnumerable: isEnumerableResource(targetResource),
|
|
17905
|
+
...values !== void 0 ? { picklistValues: values } : {}
|
|
17906
|
+
};
|
|
17907
|
+
}
|
|
17908
|
+
function isObjectOrArrayType(type) {
|
|
17909
|
+
const t = type.trim().toLowerCase();
|
|
17910
|
+
return t === "object" || t === "array" || t === "collection" || t.endsWith("[]");
|
|
17911
|
+
}
|
|
17912
|
+
function exposesPicklistValues(raw) {
|
|
17913
|
+
const r = typeof raw === "object" && raw !== null ? raw : {};
|
|
17914
|
+
return Array.isArray(r.picklistValues) && r.picklistValues.length > 0;
|
|
17915
|
+
}
|
|
17916
|
+
function applyFieldsFilter(projected, filter, row) {
|
|
17917
|
+
const kept = filter === void 0 ? projected : projected.filter((f) => filter.includes(f.name));
|
|
17918
|
+
if (filter !== void 0) {
|
|
17919
|
+
const present = new Set(projected.map((f) => f.name));
|
|
17920
|
+
row.unmatchedFields = filter.filter((n) => !present.has(n));
|
|
17921
|
+
row.projected = true;
|
|
17921
17922
|
}
|
|
17922
|
-
|
|
17923
|
-
|
|
17924
|
-
|
|
17923
|
+
row.fields = kept;
|
|
17924
|
+
row.required = kept.filter((f) => f.isRequired).map((f) => f.name);
|
|
17925
|
+
row.readOnly = kept.filter((f) => f.isReadOnly).map((f) => f.name);
|
|
17926
|
+
row.queryable = kept.filter((f) => f.isQueryable).map((f) => f.name);
|
|
17927
|
+
row.picklistFields = kept.filter((f) => f.isPickList).map((f) => f.name);
|
|
17928
|
+
}
|
|
17929
|
+
function capabilitySets() {
|
|
17930
|
+
const fieldOps = /* @__PURE__ */ new Map();
|
|
17931
|
+
const udfCapable = /* @__PURE__ */ new Set();
|
|
17932
|
+
const resources = /* @__PURE__ */ new Set();
|
|
17933
|
+
for (const rec of CAPABILITIES) {
|
|
17934
|
+
if (rec.resource === "operations") continue;
|
|
17935
|
+
resources.add(rec.resource);
|
|
17936
|
+
if (rec.operation === "getFieldInfo") fieldOps.set(rec.resource, rec);
|
|
17937
|
+
if (rec.udfCapable) udfCapable.add(rec.resource);
|
|
17925
17938
|
}
|
|
17926
|
-
|
|
17927
|
-
|
|
17928
|
-
|
|
17929
|
-
|
|
17930
|
-
|
|
17931
|
-
|
|
17932
|
-
|
|
17933
|
-
|
|
17934
|
-
|
|
17935
|
-
|
|
17936
|
-
|
|
17937
|
-
|
|
17938
|
-
|
|
17939
|
-
|
|
17940
|
-
|
|
17941
|
-
|
|
17942
|
-
|
|
17943
|
-
|
|
17944
|
-
|
|
17945
|
-
* limit). Cursor exhausted, or a short/empty page with no truncation →
|
|
17946
|
-
* false (never claims more than the cursor + the slice say exist).
|
|
17947
|
-
* - `count` is always omitted: `pageDetails.count` is the current page's
|
|
17948
|
-
* record count (vendor-documented), never a total — `<resource>.count`
|
|
17949
|
-
* is the total.
|
|
17950
|
-
* - Issue #59: `latest: N` (validated pre-request, zero wire) delegates to
|
|
17951
|
-
* `latestWalk` — the count-anchored N-highest-id mechanism; ABSENT, the
|
|
17952
|
-
* walk below is bit-for-bit the existing path.
|
|
17953
|
-
* Pure read; no mutation paths involved.
|
|
17954
|
-
*/
|
|
17955
|
-
async queryBounded(params) {
|
|
17956
|
-
if (Array.isArray(params)) {
|
|
17957
|
-
throw new AutotaskConfigError(
|
|
17958
|
-
"queryBounded: params must be an object { filter?, limit?, latest?, includeFields? } \u2014 a bare conditions array is not accepted (wrap it: { filter: [...] })"
|
|
17959
|
-
);
|
|
17939
|
+
return { fieldOps, udfCapable, resources };
|
|
17940
|
+
}
|
|
17941
|
+
var derivedIndex = null;
|
|
17942
|
+
function derivedFieldsFor(resource) {
|
|
17943
|
+
if (derivedIndex === null) {
|
|
17944
|
+
derivedIndex = /* @__PURE__ */ new Map();
|
|
17945
|
+
for (const rec of CAPABILITIES) {
|
|
17946
|
+
const declared = rec.metadata?.derivedFields;
|
|
17947
|
+
if (!Array.isArray(declared) || derivedIndex.has(rec.resource)) continue;
|
|
17948
|
+
const rows = [];
|
|
17949
|
+
for (const row of declared) {
|
|
17950
|
+
if (typeof row?.field !== "string" || typeof row.rule !== "string") continue;
|
|
17951
|
+
rows.push({
|
|
17952
|
+
field: row.field,
|
|
17953
|
+
rule: row.rule,
|
|
17954
|
+
...typeof row.writableRoute === "string" ? { writableRoute: row.writableRoute } : {}
|
|
17955
|
+
});
|
|
17956
|
+
}
|
|
17957
|
+
derivedIndex.set(rec.resource, rows);
|
|
17960
17958
|
}
|
|
17961
|
-
|
|
17962
|
-
|
|
17963
|
-
|
|
17964
|
-
|
|
17959
|
+
}
|
|
17960
|
+
return derivedIndex.get(resource) ?? [];
|
|
17961
|
+
}
|
|
17962
|
+
function apiEntityFor(rec) {
|
|
17963
|
+
const m = /^GET \/V1\.0\/([^/]+)\/entityInformation\/fields$/.exec(rec.endpoint ?? "");
|
|
17964
|
+
if (m === null) {
|
|
17965
|
+
throw new AutotaskConfigError(
|
|
17966
|
+
`fieldCatalog: the plan's getFieldInfo endpoint for "${rec.resource}" has no /V1.0/<Entity>/entityInformation/fields shape (${rec.endpoint}) \u2014 plan defect`,
|
|
17967
|
+
{ operation: "operations.fieldCatalog" }
|
|
17968
|
+
);
|
|
17969
|
+
}
|
|
17970
|
+
return m[1];
|
|
17971
|
+
}
|
|
17972
|
+
function validateCatalogOptsShape(opts) {
|
|
17973
|
+
if (opts !== void 0 && (typeof opts !== "object" || opts === null || Array.isArray(opts))) {
|
|
17974
|
+
throw new AutotaskConfigError(
|
|
17975
|
+
"fieldCatalog: opts must be an object { resources?: string[], include?: (info|fields|udf)[], fresh?: boolean, fields?: string[], labels?: boolean }"
|
|
17976
|
+
);
|
|
17977
|
+
}
|
|
17978
|
+
for (const key of Object.keys(opts ?? {})) {
|
|
17979
|
+
if (!FIELD_CATALOG_OPT_KEYS.includes(key)) {
|
|
17980
|
+
throw new AutotaskConfigError(
|
|
17981
|
+
`fieldCatalog: unknown opts key "${key}" \u2014 the bag is closed: ${FIELD_CATALOG_OPT_KEYS.join(", ")}`,
|
|
17982
|
+
{ suggestedAction: "Drop the unknown key (autotask_describe(operations.fieldCatalog) shows the exact bag)." }
|
|
17965
17983
|
);
|
|
17966
17984
|
}
|
|
17967
|
-
|
|
17968
|
-
|
|
17985
|
+
}
|
|
17986
|
+
}
|
|
17987
|
+
function resolveInclude(opts) {
|
|
17988
|
+
const includeRaw = opts?.include ?? ["fields"];
|
|
17989
|
+
if (!Array.isArray(includeRaw) || includeRaw.length === 0) {
|
|
17990
|
+
throw new AutotaskConfigError("fieldCatalog: opts.include must be a non-empty array of (info|fields|udf)");
|
|
17991
|
+
}
|
|
17992
|
+
const include = [];
|
|
17993
|
+
for (const kind of includeRaw) {
|
|
17994
|
+
if (typeof kind !== "string" || !FIELD_CACHE_KINDS.includes(kind)) {
|
|
17969
17995
|
throw new AutotaskConfigError(
|
|
17970
|
-
`
|
|
17996
|
+
`fieldCatalog: unknown include kind "${String(kind)}" \u2014 the valid kinds are: ${FIELD_CACHE_KINDS.join(", ")}`,
|
|
17997
|
+
{ suggestedAction: `Filter with one or more of: ${FIELD_CACHE_KINDS.join(", ")} (default ['fields']).` }
|
|
17971
17998
|
);
|
|
17972
17999
|
}
|
|
17973
|
-
|
|
17974
|
-
|
|
17975
|
-
|
|
17976
|
-
|
|
17977
|
-
|
|
17978
|
-
|
|
17979
|
-
|
|
17980
|
-
|
|
17981
|
-
|
|
18000
|
+
if (!include.includes(kind)) include.push(kind);
|
|
18001
|
+
}
|
|
18002
|
+
return include;
|
|
18003
|
+
}
|
|
18004
|
+
function resolveTargets(opts, fieldOps, allResources) {
|
|
18005
|
+
const resourcesRaw = opts?.resources ?? WORKFLOW_RESOURCES;
|
|
18006
|
+
if (!Array.isArray(resourcesRaw) || resourcesRaw.length === 0) {
|
|
18007
|
+
throw new AutotaskConfigError("fieldCatalog: opts.resources must be a non-empty array of plan resource keys");
|
|
18008
|
+
}
|
|
18009
|
+
const targets = [];
|
|
18010
|
+
for (const r of resourcesRaw) {
|
|
18011
|
+
if (typeof r !== "string" || r.trim() === "") {
|
|
18012
|
+
throw new AutotaskConfigError(
|
|
18013
|
+
`fieldCatalog: opts.resources must contain non-empty plan resource key strings (got ${typeof r === "string" && r.trim() === "" ? "a blank string" : typeof r})`
|
|
18014
|
+
);
|
|
18015
|
+
}
|
|
18016
|
+
const key = r.trim();
|
|
18017
|
+
const fieldOp = fieldOps.get(key);
|
|
18018
|
+
if (fieldOp === void 0) {
|
|
18019
|
+
if (allResources.has(key)) {
|
|
17982
18020
|
throw new AutotaskConfigError(
|
|
17983
|
-
`
|
|
18021
|
+
`fieldCatalog: resource "${key}" has no field catalog \u2014 the vendor defines the entityInformation trio for the ${fieldOps.size} plan entity resources only`,
|
|
18022
|
+
{ suggestedAction: `Pass one of the ${fieldOps.size} field-capable plan keys (list them with autotask_catalog).` }
|
|
17984
18023
|
);
|
|
17985
18024
|
}
|
|
17986
|
-
|
|
18025
|
+
throw new AutotaskError(
|
|
18026
|
+
`fieldCatalog: unknown resource "${key}" \u2014 the plan has ${fieldOps.size} field-capable resources`,
|
|
18027
|
+
{
|
|
18028
|
+
code: "RESOURCE_UNKNOWN",
|
|
18029
|
+
category: "validation",
|
|
18030
|
+
suggestedAction: "Pass the exact plan resource key (snake_case) from autotask_catalog (e.g. tickets, companies, configuration_items)."
|
|
18031
|
+
}
|
|
18032
|
+
);
|
|
17987
18033
|
}
|
|
17988
|
-
|
|
17989
|
-
const hasMore = (items.length >= limit || guard) && (sliced > 0 || cursor !== null);
|
|
17990
|
-
const result = { items, fetched: items.length, hasMore, pagesWalked: pages };
|
|
17991
|
-
this.stampEmptyQueryResult(result, params);
|
|
17992
|
-
return this.applyNoteChars(result, noteChars);
|
|
18034
|
+
if (!targets.includes(key)) targets.push(key);
|
|
17993
18035
|
}
|
|
17994
|
-
|
|
17995
|
-
|
|
17996
|
-
|
|
17997
|
-
|
|
17998
|
-
|
|
17999
|
-
|
|
18000
|
-
|
|
18001
|
-
|
|
18002
|
-
|
|
18003
|
-
|
|
18004
|
-
|
|
18005
|
-
|
|
18006
|
-
|
|
18007
|
-
|
|
18008
|
-
if (fields === void 0 || fields.length === 0) return result;
|
|
18009
|
-
const [items, truncated] = truncateNoteChars(result.items, noteChars, fields);
|
|
18010
|
-
if (truncated > 0) {
|
|
18011
|
-
return { ...result, items, noteCharsTruncated: truncated };
|
|
18036
|
+
return targets;
|
|
18037
|
+
}
|
|
18038
|
+
function resolveFieldsFilter(opts) {
|
|
18039
|
+
const fieldsRaw = opts?.fields;
|
|
18040
|
+
if (fieldsRaw === void 0) return void 0;
|
|
18041
|
+
if (!Array.isArray(fieldsRaw) || fieldsRaw.length === 0) {
|
|
18042
|
+
throw new AutotaskConfigError("fieldCatalog: opts.fields must be a non-empty array of vendor field name strings");
|
|
18043
|
+
}
|
|
18044
|
+
const names = [];
|
|
18045
|
+
for (const f of fieldsRaw) {
|
|
18046
|
+
if (typeof f !== "string" || f.trim() === "") {
|
|
18047
|
+
throw new AutotaskConfigError(
|
|
18048
|
+
`fieldCatalog: opts.fields must contain non-empty vendor field name strings (got ${typeof f === "string" && f.trim() === "" ? "a blank string" : typeof f})`
|
|
18049
|
+
);
|
|
18012
18050
|
}
|
|
18013
|
-
|
|
18051
|
+
const name = f.trim();
|
|
18052
|
+
if (!names.includes(name)) names.push(name);
|
|
18014
18053
|
}
|
|
18015
|
-
|
|
18016
|
-
|
|
18017
|
-
|
|
18018
|
-
|
|
18019
|
-
|
|
18020
|
-
|
|
18021
|
-
|
|
18022
|
-
|
|
18023
|
-
|
|
18024
|
-
|
|
18025
|
-
|
|
18026
|
-
|
|
18027
|
-
|
|
18028
|
-
|
|
18029
|
-
|
|
18030
|
-
|
|
18031
|
-
|
|
18054
|
+
return names;
|
|
18055
|
+
}
|
|
18056
|
+
function resolveLabels(opts) {
|
|
18057
|
+
if (opts !== void 0 && opts.labels !== void 0 && typeof opts.labels !== "boolean") {
|
|
18058
|
+
throw new AutotaskConfigError(
|
|
18059
|
+
`fieldCatalog: opts.labels must be a boolean (got ${typeof opts.labels})`
|
|
18060
|
+
);
|
|
18061
|
+
}
|
|
18062
|
+
return opts?.labels === true;
|
|
18063
|
+
}
|
|
18064
|
+
function assertWithinWireCap(targetCount, kindCount) {
|
|
18065
|
+
const cost = targetCount * kindCount;
|
|
18066
|
+
if (cost > MAX_FIELD_CATALOG_WIRE) {
|
|
18067
|
+
throw new AutotaskConfigError(
|
|
18068
|
+
`fieldCatalog: ${targetCount} resource(s) \xD7 ${kindCount} kind(s) = ${cost} metadata reads exceed the wire cap of ${MAX_FIELD_CATALOG_WIRE} \u2014 the call is refused above its cap (never truncated)`,
|
|
18069
|
+
{ suggestedAction: `Pass a smaller resources list or a narrower include set so the product stays \u2264 ${MAX_FIELD_CATALOG_WIRE}.` }
|
|
18070
|
+
);
|
|
18071
|
+
}
|
|
18072
|
+
}
|
|
18073
|
+
function kindFetches(holder, resource) {
|
|
18074
|
+
return {
|
|
18075
|
+
info: () => holder?.getEntityInfo?.() ?? Promise.reject(new AutotaskError(`fieldCatalog: the client exposes no getEntityInfo read for "${resource}"`, { code: "INVALID_RESPONSE", category: "server" })),
|
|
18076
|
+
fields: () => holder?.getFieldInfo?.() ?? Promise.reject(new AutotaskError(`fieldCatalog: the client exposes no getFieldInfo read for "${resource}"`, { code: "INVALID_RESPONSE", category: "server" })),
|
|
18077
|
+
udf: () => holder?.getUdfInfo?.() ?? Promise.reject(new AutotaskError(`fieldCatalog: the client exposes no getUdfInfo read for "${resource}"`, { code: "INVALID_RESPONSE", category: "server" }))
|
|
18078
|
+
};
|
|
18079
|
+
}
|
|
18080
|
+
async function sweepResource(ctx, resource, tally) {
|
|
18081
|
+
const { client, include, fieldsFilter, labels, fresh } = ctx;
|
|
18082
|
+
const cache = client.http.fieldCache;
|
|
18083
|
+
const fieldOp = ctx.fieldOps.get(resource);
|
|
18084
|
+
if (fieldOp === void 0) return void 0;
|
|
18085
|
+
const apiEntity = apiEntityFor(fieldOp);
|
|
18086
|
+
const holder = client[camelKey(resource)];
|
|
18087
|
+
const row = {
|
|
18088
|
+
resource,
|
|
18089
|
+
apiEntity,
|
|
18090
|
+
udfCapable: ctx.udfCapable.has(resource),
|
|
18091
|
+
// Issue #219 N7: the UDF surface is declared with the mechanism, not a boolean.
|
|
18092
|
+
...ctx.udfCapable.has(resource) ? { udfFilter: "udf-marker", udfWriteShape: "merge" } : {},
|
|
18093
|
+
unmatchedFields: [],
|
|
18094
|
+
required: [],
|
|
18095
|
+
readOnly: [],
|
|
18096
|
+
queryable: [],
|
|
18097
|
+
picklistFields: [],
|
|
18098
|
+
cache: { hit: false, age: null },
|
|
18099
|
+
wire: 0,
|
|
18100
|
+
error: null
|
|
18101
|
+
};
|
|
18102
|
+
const fetches = kindFetches(holder, resource);
|
|
18103
|
+
if (fresh) cache?.invalidate(resource);
|
|
18104
|
+
const projectList = (kind, list) => {
|
|
18105
|
+
const projected = list.map((f) => projectFieldRow(f, labels, kind === "udf"));
|
|
18106
|
+
if (kind === "fields") applyFieldsFilter(projected, fieldsFilter, row);
|
|
18107
|
+
else row.udf = projected;
|
|
18108
|
+
for (const f of list) if (exposesPicklistValues(f)) tally.anyPicklistValues = true;
|
|
18109
|
+
};
|
|
18110
|
+
let allCached = true;
|
|
18111
|
+
const ages = [];
|
|
18112
|
+
for (const kind of include) {
|
|
18113
|
+
const peek = cache !== void 0 && !fresh ? cache.peek(resource, kind) : { hit: false, age: null };
|
|
18114
|
+
if (peek.hit !== true) allCached = false;
|
|
18115
|
+
if (peek.hit === true && cache !== void 0) {
|
|
18116
|
+
const value2 = cache.get(resource, kind);
|
|
18117
|
+
if (kind === "info") row.info = value2 ?? null;
|
|
18118
|
+
else projectList(kind, Array.isArray(value2) ? value2 : []);
|
|
18119
|
+
if (peek.age !== null) ages.push(peek.age);
|
|
18120
|
+
continue;
|
|
18121
|
+
}
|
|
18122
|
+
let value;
|
|
18123
|
+
try {
|
|
18124
|
+
value = await fetches[kind]();
|
|
18125
|
+
} catch (err) {
|
|
18126
|
+
if (!isAutotaskError(err) || err.code === "ABORTED") throw err;
|
|
18127
|
+
tally.wireUsed += 1;
|
|
18128
|
+
row.wire += 1;
|
|
18129
|
+
if (row.error === null) row.error = { code: err.code, message: err.message };
|
|
18130
|
+
continue;
|
|
18131
|
+
}
|
|
18132
|
+
tally.wireUsed += 1;
|
|
18133
|
+
row.wire += 1;
|
|
18134
|
+
if (kind === "info") {
|
|
18135
|
+
row.info = value ?? null;
|
|
18136
|
+
} else {
|
|
18137
|
+
if (!Array.isArray(value)) {
|
|
18138
|
+
if (row.error === null) row.error = { code: "INVALID_RESPONSE", message: `fieldCatalog: the ${kind} response for ${resource} was not a list` };
|
|
18139
|
+
continue;
|
|
18032
18140
|
}
|
|
18033
|
-
|
|
18034
|
-
pages += 1;
|
|
18035
|
-
const remaining = limit - items.length;
|
|
18036
|
-
const take = page.items.slice(0, remaining);
|
|
18037
|
-
sliced += Math.max(0, page.items.length - remaining);
|
|
18038
|
-
items.push(...take);
|
|
18039
|
-
if (items.length >= limit) break;
|
|
18040
|
-
if (!page.hasMore || page.items.length === 0) break;
|
|
18141
|
+
projectList(kind, value);
|
|
18041
18142
|
}
|
|
18042
|
-
return { items, pages, guard, sliced, cursor: state.cursor };
|
|
18043
18143
|
}
|
|
18044
|
-
|
|
18045
|
-
|
|
18046
|
-
|
|
18047
|
-
|
|
18048
|
-
|
|
18049
|
-
|
|
18050
|
-
|
|
18144
|
+
row.cache = allCached ? { hit: true, age: ages.length > 0 ? Math.max(...ages) : 0 } : { hit: false, age: null };
|
|
18145
|
+
const declared = derivedFieldsFor(resource);
|
|
18146
|
+
if (declared.length > 0) {
|
|
18147
|
+
const byField = new Map(declared.map((d) => [d.field, d]));
|
|
18148
|
+
row.derivedFields = declared;
|
|
18149
|
+
for (const field of row.fields ?? []) {
|
|
18150
|
+
const derivation = byField.get(field.name);
|
|
18151
|
+
if (derivation === void 0) continue;
|
|
18152
|
+
field.derived = {
|
|
18153
|
+
rule: derivation.rule,
|
|
18154
|
+
...derivation.writableRoute !== void 0 ? { writableRoute: derivation.writableRoute } : {}
|
|
18155
|
+
};
|
|
18156
|
+
}
|
|
18051
18157
|
}
|
|
18052
|
-
|
|
18053
|
-
|
|
18054
|
-
|
|
18158
|
+
return row;
|
|
18159
|
+
}
|
|
18160
|
+
async function fieldCatalog(client, opts) {
|
|
18161
|
+
validateCatalogOptsShape(opts);
|
|
18162
|
+
const { fieldOps, udfCapable, resources: allResources } = capabilitySets();
|
|
18163
|
+
const include = resolveInclude(opts);
|
|
18164
|
+
const targets = resolveTargets(opts, fieldOps, allResources);
|
|
18165
|
+
const fieldsFilter = resolveFieldsFilter(opts);
|
|
18166
|
+
const labels = resolveLabels(opts);
|
|
18167
|
+
assertWithinWireCap(targets.length, include.length);
|
|
18168
|
+
const cache = client.http.fieldCache;
|
|
18169
|
+
const zone = client.config.zone ?? "unknown";
|
|
18170
|
+
const rows = [];
|
|
18171
|
+
const tally = { wireUsed: 0, anyPicklistValues: false };
|
|
18172
|
+
const ctx = { client, include, fieldsFilter, labels, fresh: opts?.fresh === true, fieldOps, udfCapable };
|
|
18173
|
+
for (const resource of targets) {
|
|
18174
|
+
const row = await sweepResource(ctx, resource, tally);
|
|
18175
|
+
if (row !== void 0) rows.push(row);
|
|
18176
|
+
}
|
|
18177
|
+
return {
|
|
18178
|
+
zone,
|
|
18179
|
+
label: zone,
|
|
18180
|
+
cached: cache !== void 0,
|
|
18181
|
+
labels,
|
|
18182
|
+
ttlMs: cache?.ttlMs ?? null,
|
|
18183
|
+
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18184
|
+
rows,
|
|
18185
|
+
wireUsed: tally.wireUsed,
|
|
18186
|
+
cap: MAX_FIELD_CATALOG_WIRE,
|
|
18187
|
+
truncated: false,
|
|
18188
|
+
counts: {
|
|
18189
|
+
resources: targets.length,
|
|
18190
|
+
fieldCapableResources: fieldOps.size,
|
|
18191
|
+
udfCapableResources: udfCapable.size,
|
|
18192
|
+
cached: rows.filter((r) => r.cache.hit === true).length
|
|
18193
|
+
},
|
|
18194
|
+
honesty: {
|
|
18195
|
+
picklistValues: tally.anyPicklistValues ? "partial" : "unexposed",
|
|
18196
|
+
note: FIELD_CATALOG_HONESTY_NOTE
|
|
18197
|
+
}
|
|
18198
|
+
};
|
|
18199
|
+
}
|
|
18200
|
+
|
|
18201
|
+
// src/field-sets.ts
|
|
18202
|
+
var MODEL_FIELD_SETS = {
|
|
18203
|
+
"appointments": ["id", "createDateTime", "creatorResourceID", "description", "endDateTime", "resourceID", "startDateTime", "title", "updateDateTime"],
|
|
18204
|
+
"billing_codes": ["id", "afterHoursWorkType", "billingCodeType", "department", "description", "externalNumber", "generalLedgerAccount", "isActive", "isExcludedFromNewContracts", "markupRate", "name", "taxCategoryID", "unitCost", "unitPrice", "useType"],
|
|
18205
|
+
"billing_item_approval_levels": ["id", "approvalDateTime", "approvalLevel", "approvalResourceID", "timeEntryID"],
|
|
18206
|
+
"billing_items": ["id", "accountManagerWhenApprovedID", "billingCodeID", "billingItemType", "companyID", "configurationItemID", "contractBlockID", "contractChargeID", "contractID", "contractServiceAdjustmentID", "contractServiceBundleAdjustmentID", "contractServiceBundleID", "contractServiceBundlePeriodID", "contractServiceID", "contractServicePeriodID", "description", "expenseItemID", "extendedPrice", "internalCurrencyExtendedPrice", "internalCurrencyRate", "internalCurrencyTaxDollars", "internalCurrencyTotalAmount", "invoiceID", "itemApproverID", "itemDate", "itemName", "lineItemFullDescription", "lineItemGroupDescription", "milestoneID", "nonBillable", "organizationalLevelAssociationID", "ourCost", "postedDate", "postedOnTime", "projectChargeID", "projectID", "purchaseOrderNumber", "quantity", "rate", "roleID", "serviceBundleID", "serviceID", "sortOrderID", "subType", "taskID", "taxDollars", "ticketChargeID", "ticketID", "timeEntryID", "totalAmount", "vendorID", "webServiceDate"],
|
|
18207
|
+
"change_request_links": ["id", "changeRequestTicketID", "problemOrIncidentTicketID"],
|
|
18208
|
+
"checklist_libraries": ["id", "description", "entityType", "isActive", "name"],
|
|
18209
|
+
"checklist_library_checklist_items": ["id", "checklistLibraryID", "isImportant", "itemName", "knowledgebaseArticleID", "position", "soapParentPropertyId"],
|
|
18210
|
+
"classification_icons": ["id", "description", "isActive", "isSystem", "name", "relativeUrl"],
|
|
18211
|
+
"companies": ["id", "additionalAddressInformation", "address1", "address2", "alternatePhone1", "alternatePhone2", "apiVendorID", "assetValue", "billToCompanyLocationID", "billToAdditionalAddressInformation", "billingAddress1", "billingAddress2", "billToAddressToUse", "billToAttention", "billToCity", "billToCountryID", "billToState", "billToZipCode", "city", "classification", "companyCategoryID", "companyName", "companyNumber", "companyType", "competitorID", "countryID", "createDate", "createdByResourceID", "currencyID", "fax", "impersonatorCreatorResourceID", "invoiceEmailMessageID", "invoiceMethod", "invoiceNonContractItemsToParentCompany", "invoiceTemplateID", "isActive", "isClientPortalActive", "isEnabledForComanaged", "isSample", "isTaskFireActive", "isTaxExempt", "lastActivityDate", "lastTrackedModifiedDateTime", "marketSegmentID", "ownerResourceID", "parentCompanyID", "phone", "postalCode", "purchaseOrderTemplateID", "quoteEmailMessageID", "quoteTemplateID", "sicCode", "state", "stockMarket", "stockSymbol", "surveyCompanyRating", "taxID", "taxRegionID", "territoryID", "webAddress", "userDefinedFields"],
|
|
18212
|
+
"company_alerts": ["id", "alertText", "alertTypeID", "companyID", "soapParentPropertyId"],
|
|
18213
|
+
"company_locations": ["id", "address1", "address2", "alternatePhone1", "alternatePhone2", "city", "companyID", "countryID", "description", "fax", "isActive", "isPrimary", "isTaxExempt", "overrideCompanyTaxSettings", "name", "phone", "postalCode", "roundtripDistance", "state", "taxRegionID", "soapParentPropertyId"],
|
|
18214
|
+
"company_notes": ["id", "actionType", "assignedResourceID", "companyID", "completedDateTime", "contactID", "createDateTime", "endDateTime", "impersonatorCreatorResourceID", "impersonatorUpdaterResourceID", "lastModifiedDate", "name", "note", "opportunityID", "startDateTime", "soapParentPropertyId"],
|
|
18215
|
+
"company_site_configurations": ["id", "companyID", "locationName", "soapParentPropertyId", "userDefinedFields"],
|
|
18216
|
+
"company_webhooks": ["id", "deactivationUrl", "isActive", "isReady", "isSubscribedToCreateEvents", "isSubscribedToDeleteEvents", "isSubscribedToUpdateEvents", "name", "notificationEmailAddress", "ownerResourceID", "secretKey", "sendThresholdExceededNotification", "webhookGUID", "webhookUrl"],
|
|
18217
|
+
"configuration_item_billing_product_associations": ["id", "billingProductID", "configurationItemID", "effectiveDate", "expirationDate", "soapParentPropertyId"],
|
|
18218
|
+
"configuration_item_categories": ["id", "displayColorRGB", "isActive", "isClientPortalDefault", "isGlobalDefault", "name", "nickname"],
|
|
18219
|
+
"configuration_item_category_udf_associations": ["id", "configurationItemCategoryID", "isRequired", "userDefinedFieldDefinitionID", "soapParentPropertyId"],
|
|
18220
|
+
"configuration_item_dns_records": ["id", "createDateTime", "data", "installedProductID", "timeToLiveSeconds", "dnsType", "soapParentPropertyId"],
|
|
18221
|
+
"configuration_item_notes": ["id", "configurationItemID", "createDateTime", "creatorResourceID", "description", "impersonatorCreatorResourceID", "impersonatorUpdaterResourceID", "lastActivityDate", "noteType", "title", "soapParentPropertyId"],
|
|
18222
|
+
"configuration_item_related_items": ["id", "configurationItemID", "relatedConfigurationItemID", "relationship", "soapParentPropertyId"],
|
|
18223
|
+
"configuration_item_ssl_subject_alternative_names": ["id", "configurationItemID", "name", "soapParentPropertyId"],
|
|
18224
|
+
"configuration_item_types": ["id", "isActive", "name"],
|
|
18225
|
+
"configuration_item_webhooks": ["id", "deactivationUrl", "isActive", "isReady", "isSubscribedToCreateEvents", "isSubscribedToDeleteEvents", "isSubscribedToUpdateEvents", "name", "notificationEmailAddress", "ownerResourceID", "secretKey", "sendThresholdExceededNotification", "webhookGUID", "webhookUrl"],
|
|
18226
|
+
"configuration_items": ["id", "apiVendorID", "configurationItemCategoryID", "companyID", "companyLocationID", "configurationItemType", "contactID", "contractID", "contractServiceBundleID", "contractServiceID", "createDate", "createdByPersonID", "dailyCost", "dattoAvailableKilobytes", "dattoDeviceMemoryMegabytes", "dattoDrivesErrors", "dattoHostname", "dattoInternalIP", "dattoKernelVersionID", "dattoLastCheckInDateTime", "dattoNICSpeedKilobitsPerSecond", "dattoNumberOfAgents", "dattoNumberOfDrives", "dattoNumberOfVolumes", "dattoOffsiteUsedBytes", "dattoOSVersionID", "dattoPercentageUsed", "dattoProtectedKilobytes", "dattoRemoteIP", "dattoSerialNumber", "dattoUptimeSeconds", "dattoUsedKilobytes", "dattoZFSVersionID", "deviceNetworkingID", "domain", "domainRegistrarID", "domainRegistrationDateTime", "domainLastUpdatedDateTime", "domainExpirationDateTime", "hourlyCost", "impersonatorCreatorResourceID", "installDate", "installedByContactID", "installedByID", "isActive", "lastActivityPersonID", "lastActivityPersonType", "lastModifiedTime", "location", "monthlyCost", "notes", "numberOfUsers", "parentConfigurationItemID", "perUseCost", "productID", "referenceNumber", "referenceTitle", "rmmDeviceAuditAntivirusStatusID", "rmmDeviceAuditArchitectureID", "rmmDeviceAuditBackupStatusID", "rmmDeviceAuditDescription", "rmmDeviceAuditDeviceTypeID", "rmmDeviceAuditDisplayAdaptorID", "rmmDeviceAuditDomainID", "rmmDeviceAuditExternalIPAddress", "rmmDeviceAuditHostname", "rmmDeviceAuditIPAddress", "rmmDeviceAuditLastUser", "rmmDeviceAuditMacAddress", "rmmDeviceAuditManufacturerID", "rmmDeviceAuditFirmwareID", "rmmDeviceAuditMemoryBytes", "rmmDeviceAuditMissingPatchCount", "rmmDeviceAuditMobileNetworkOperatorID", "rmmDeviceAuditMobileNumber", "rmmDeviceAuditModelID", "rmmDeviceAuditMotherboardID", "rmmDeviceAuditOperatingSystem", "rmmDeviceAuditPatchStatusID", "rmmDeviceAuditProcessorID", "rmmDeviceAuditServicePackID", "rmmDeviceAuditSNMPContact", "rmmDeviceAuditSNMPLocation", "rmmDeviceAuditSNMPName", "rmmDeviceAuditSoftwareStatusID", "rmmDeviceAuditStorageBytes", "rmmDeviceID", "rmmDeviceUID", "rmmVsaxDeviceID", "rmmOpenAlertCount", "serialNumber", "serviceBundleID", "serviceID", "serviceLevelAgreementID", "setupFee", "sourceChargeID", "sourceChargeType", "sslSource", "sslCommonName", "sslValidFromDateTime", "sslValidUntilDateTime", "sslIssuedBy", "sslOrganization", "sslOrganizationUnit", "sslLocation", "sslSerialNumber", "sslSignatureAlgorithm", "vendorID", "warrantyExpirationDate", "rmmIsInMaintenanceMode", "rmmIsMobileDeviceManagementEnrolled", "rmmDeviceUrl", "userDefinedFields"],
|
|
18227
|
+
"contact_group_contacts": ["id", "contactId", "contactGroupId", "soapParentPropertyId"],
|
|
18228
|
+
"contact_groups": ["id", "name", "isActive"],
|
|
18229
|
+
"contact_webhooks": ["id", "deactivationUrl", "isActive", "isReady", "isSubscribedToCreateEvents", "isSubscribedToDeleteEvents", "isSubscribedToUpdateEvents", "name", "notificationEmailAddress", "ownerResourceID", "secretKey", "sendThresholdExceededNotification", "webhookGUID", "webhookUrl"],
|
|
18230
|
+
"contacts": ["id", "additionalAddressInformation", "addressLine", "addressLine1", "alternatePhone", "apiVendorID", "bulkEmailOptOutTime", "city", "companyID", "companyLocationID", "countryID", "createDate", "emailAddress", "emailAddress2", "emailAddress3", "extension", "externalID", "facebookUrl", "faxNumber", "firstName", "impersonatorCreatorResourceID", "isActive", "isOptedOutFromBulkEmail", "lastActivityDate", "lastModifiedDate", "lastName", "linkedInUrl", "middleInitial", "mobilePhone", "namePrefix", "nameSuffix", "note", "receivesEmailNotifications", "phone", "primaryContact", "roomNumber", "solicitationOptOut", "solicitationOptOutTime", "state", "surveyOptOut", "title", "twitterUrl", "zipCode", "soapParentPropertyId", "userDefinedFields"],
|
|
18231
|
+
"contract_billing_rules": ["id", "contractID", "createChargesAsBillable", "dailyProratedCost", "dailyProratedPrice", "determineUnits", "endDate", "executionMethod", "includeItemsInChargeDescription", "invoiceDescription", "isActive", "isDailyProrationEnabled", "maximumUnits", "minimumUnits", "productID", "startDate", "soapParentPropertyId"],
|
|
18232
|
+
"contract_block_hour_factors": ["id", "blockHourMultiplier", "contractHourlyRate", "contractID", "internalCurrencyContractHourlyRate", "roleID", "soapParentPropertyId"],
|
|
18233
|
+
"contract_blocks": ["id", "contractID", "datePurchased", "endDate", "hourlyRate", "hours", "hoursApproved", "invoiceNumber", "isPaid", "paymentNumber", "paymentType", "startDate", "status", "soapParentPropertyId"],
|
|
18234
|
+
"contract_charges": ["id", "billableAmount", "billingCodeID", "chargeType", "contractID", "contractServiceBundleID", "contractServiceID", "createDate", "creatorResourceID", "datePurchased", "description", "extendedCost", "internalCurrencyBillableAmount", "internalCurrencyUnitPrice", "internalPurchaseOrderNumber", "isBillableToCompany", "isBilled", "name", "notes", "organizationalLevelAssociationID", "productID", "purchaseOrderNumber", "status", "statusLastModifiedBy", "statusLastModifiedDate", "unitCost", "unitPrice", "unitQuantity", "soapParentPropertyId"],
|
|
18235
|
+
"contract_exclusion_billing_codes": ["id", "billingCodeID", "contractID", "soapParentPropertyId"],
|
|
18236
|
+
"contract_exclusion_roles": ["id", "contractID", "roleID", "soapParentPropertyId"],
|
|
18237
|
+
"contract_exclusion_set_excluded_roles": ["id", "contractExclusionSetID", "excludedRoleID", "soapParentPropertyId"],
|
|
18238
|
+
"contract_exclusion_set_excluded_work_types": ["id", "contractExclusionSetID", "excludedWorkTypeID", "soapParentPropertyId"],
|
|
18239
|
+
"contract_exclusion_sets": ["id", "description", "isActive", "name"],
|
|
18240
|
+
"contract_milestones": ["id", "amount", "billingCodeID", "contractID", "createDate", "creatorResourceID", "dateDue", "description", "internalCurrencyAmount", "isInitialPayment", "organizationalLevelAssociationID", "status", "title", "soapParentPropertyId"],
|
|
18241
|
+
"contract_notes": ["id", "contractID", "createDateTime", "creatorResourceID", "description", "impersonatorCreatorResourceID", "impersonatorUpdaterResourceID", "lastActivityDate", "title", "soapParentPropertyId"],
|
|
18242
|
+
"contract_rates": ["id", "contractHourlyRate", "contractID", "internalCurrencyContractHourlyRate", "roleID", "soapParentPropertyId"],
|
|
18243
|
+
"contract_retainers": ["id", "amount", "amountApproved", "contractID", "datePurchased", "endDate", "internalCurrencyAmount", "internalCurrencyAmountApproved", "invoiceNumber", "isPaid", "paymentID", "paymentNumber", "startDate", "status", "soapParentPropertyId"],
|
|
18244
|
+
"contract_role_costs": ["id", "contractID", "rate", "resourceID", "roleID", "soapParentPropertyId"],
|
|
18245
|
+
"contract_service_bundle_units": ["id", "approveAndPostDate", "contractID", "contractServiceBundleID", "cost", "endDate", "internalCurrencyPrice", "organizationalLevelAssociationID", "price", "serviceBundleID", "startDate", "units", "soapParentPropertyId"],
|
|
18246
|
+
"contract_service_bundles": ["id", "adjustedPrice", "contractID", "internalCurrencyAdjustedPrice", "internalCurrencyUnitPrice", "internalDescription", "invoiceDescription", "quoteItemID", "serviceBundleID", "unitPrice", "soapParentPropertyId"],
|
|
18247
|
+
"contract_service_units": ["id", "approveAndPostDate", "contractID", "contractServiceID", "cost", "endDate", "internalCurrencyPrice", "organizationalLevelAssociationID", "price", "serviceID", "startDate", "units", "vendorCompanyID", "soapParentPropertyId"],
|
|
18248
|
+
"contract_services": ["id", "contractID", "internalCurrencyAdjustedPrice", "internalCurrencyUnitPrice", "internalDescription", "invoiceDescription", "quoteItemID", "serviceID", "unitCost", "unitPrice", "soapParentPropertyId"],
|
|
18249
|
+
"contract_ticket_purchases": ["id", "contractID", "datePurchased", "endDate", "invoiceNumber", "isPaid", "paymentNumber", "paymentType", "perTicketRate", "startDate", "status", "ticketsPurchased", "ticketsUsed", "soapParentPropertyId"],
|
|
18250
|
+
"contracts": ["id", "billingPreference", "billToCompanyContactID", "billToCompanyID", "companyID", "contactID", "contactName", "contractCategory", "contractExclusionSetID", "contractName", "contractNumber", "contractPeriodType", "contractType", "description", "endDate", "estimatedCost", "estimatedHours", "estimatedRevenue", "exclusionContractID", "internalCurrencyOverageBillingRate", "internalCurrencySetupFee", "isCompliant", "isDefaultContract", "lastModifiedDateTime", "opportunityID", "organizationalLevelAssociationID", "overageBillingRate", "purchaseOrderNumber", "renewedContractID", "serviceLevelAgreementID", "setupFee", "setupFeeBillingCodeID", "startDate", "status", "timeReportingRequiresStartAndStopTimes", "userDefinedFields"],
|
|
18251
|
+
"countries": ["id", "addressFormatID", "countryCode", "displayName", "invoiceTemplateID", "isActive", "isDefaultCountry", "name", "purchaseOrderTemplateID", "quoteTemplateID"],
|
|
18252
|
+
"currencies": ["id", "currencyNegativeFormat", "currencyPositiveFormat", "description", "displaySymbol", "exchangeRate", "isActive", "isInternalCurrency", "lastModifiedDateTime", "name", "updateResourceId"],
|
|
18253
|
+
"departments": ["id", "description", "name", "number", "primaryLocationID"],
|
|
18254
|
+
"domain_registrars": ["id", "name", "url"],
|
|
18255
|
+
"expense_item_attachments": ["id", "attachDate", "attachedByContactID", "attachedByResourceID", "attachmentType", "contentType", "creatorType", "expenseItemID", "expenseReportID", "fileSize", "fullPath", "impersonatorCreatorResourceID", "opportunityID", "parentID", "publish", "title", "data", "parentType", "soapParentPropertyId", "isTaskAttachment"],
|
|
18256
|
+
"expense_items": ["id", "companyID", "description", "destination", "entertainmentLocation", "expenseCategory", "expenseCurrencyExpenseAmount", "expenseCurrencyID", "expenseDate", "expenseReportID", "glCode", "haveReceipt", "internalCurrencyExpenseAmount", "internalCurrencyReimbursementAmount", "isBillableToCompany", "isReimbursable", "isRejected", "miles", "odometerEnd", "odometerStart", "origin", "paymentType", "projectID", "purchaseOrderNumber", "reimbursementCurrencyReimbursementAmount", "taskID", "ticketID", "workType", "soapParentPropertyId"],
|
|
18257
|
+
"expense_reports": ["id", "amountDue", "approvedDate", "approverID", "departmentNumber", "internalCurrencyCashAdvanceAmount", "internalCurrencyExpenseTotal", "name", "organizationalLevelAssociationID", "quickBooksReferenceNumber", "reimbursementCurrencyAmountDue", "reimbursementCurrencyCashAdvanceAmount", "reimbursementCurrencyID", "rejectionReason", "status", "submit", "submitDate", "submitterID", "weekEnding"],
|
|
18258
|
+
"holiday_sets": ["id", "holidaySetDescription", "holidaySetName"],
|
|
18259
|
+
"holidays": ["id", "holidayDate", "holidayName", "holidaySetID", "soapParentPropertyId"],
|
|
18260
|
+
"inventory_products": ["id", "availableUnits", "backOrderQuantity", "bin", "createDateTime", "createdByResourceID", "inventoryLocationID", "onHandUnits", "pickedUnits", "productID", "quantityMaximum", "quantityMinimum", "referenceNumber", "reservedUnits", "unitsOnOrder"],
|
|
18261
|
+
"invoice_templates": ["id", "coveredByBlockRetainerContractLabel", "coveredByRecurringServiceFixedPricePerTicketContractLabel", "currencyNegativeFormat", "currencyPositiveFormat", "dateFormat", "displayFixedPriceContractLabor", "displayRecurringServiceContractLabor", "displaySeparateLineItemForEachTax", "displayTaxCategory", "displayTaxCategorySuperscripts", "displayZeroAmountRecurringServicesAndBundles", "groupBy", "itemizeItemsInEachGroup", "itemizeServicesAndBundles", "name", "nonBillableLaborLabel", "numberFormat", "pageLayout", "pageNumberFormat", "paymentTerms", "rateCostExpression", "showGridHeader", "showVerticalGridLines", "sortBy", "timeFormat"],
|
|
18262
|
+
"invoices": ["id", "batchID", "comments", "companyID", "createDateTime", "creatorResourceID", "dueDate", "fromDate", "invoiceDateTime", "invoiceEditorTemplateID", "invoiceNumber", "invoiceTotal", "isVoided", "orderNumber", "paidDate", "paymentTerm", "taxGroup", "taxRegionName", "toDate", "totalTaxValue", "voidedByResourceID", "voidedDate", "webServiceDate"],
|
|
18263
|
+
"notification_history": ["id", "companyID", "entityNumber", "entityTitle", "initiatingContactID", "initiatingResourceID", "isActive", "isDeleted", "isTemplateJob", "notificationHistoryTypeID", "notificationSentTime", "opportunityID", "projectID", "quoteID", "recipientDisplayName", "recipientEmailAddress", "taskID", "templateName", "ticketID", "timeEntryID"],
|
|
18264
|
+
"opportunities": ["id", "advancedField1", "advancedField2", "advancedField3", "advancedField4", "advancedField5", "amount", "assessmentScore", "barriers", "closedDate", "companyID", "contactID", "cost", "createDate", "creatorResourceID", "description", "helpNeeded", "impersonatorCreatorResourceID", "lastActivity", "leadSource", "lossReason", "lossReasonDetail", "lostDate", "market", "monthlyCost", "monthlyRevenue", "nextStep", "onetimeCost", "onetimeRevenue", "opportunityCategoryID", "organizationalLevelAssociationID", "ownerResourceID", "primaryCompetitor", "probability", "productID", "projectedCloseDate", "promisedFulfillmentDate", "promotionName", "quarterlyCost", "quarterlyRevenue", "rating", "relationshipAssessmentScore", "revenueSpread", "revenueSpreadUnit", "salesOrderID", "salesProcessPercentComplete", "semiannualCost", "semiannualRevenue", "stage", "startDate", "status", "technicalAssessmentScore", "throughDate", "title", "totalAmountMonths", "useQuoteTotals", "winReason", "winReasonDetail", "yearlyCost", "yearlyRevenue", "userDefinedFields"],
|
|
18265
|
+
"opportunity_attachments": ["id", "attachDate", "attachedByContactID", "attachedByResourceID", "attachmentType", "contentType", "creatorType", "fileSize", "fullPath", "impersonatorCreatorResourceID", "opportunityID", "parentAttachmentID", "parentID", "publish", "title", "data", "parentType", "soapParentPropertyId", "isTaskAttachment"],
|
|
18266
|
+
"opportunity_categories": ["id", "displayColorRGB", "isActive", "isGlobalDefault", "name", "nickname"],
|
|
18267
|
+
"organizational_level1s": ["id", "description", "isActive", "name"],
|
|
18268
|
+
"organizational_level2s": ["id", "description", "isActive", "name"],
|
|
18269
|
+
"organizational_level_associations": ["id", "isActive", "organizationalLevel1ID", "organizationalLevel2ID"],
|
|
18270
|
+
"phases": ["id", "createDate", "creatorResourceID", "description", "dueDate", "estimatedHours", "externalID", "isScheduled", "lastActivityDateTime", "parentPhaseID", "phaseNumber", "projectID", "startDate", "title", "soapParentPropertyId"],
|
|
18271
|
+
"product_vendors": ["id", "isActive", "isDefault", "productID", "vendorCost", "vendorID", "vendorPartNumber", "soapParentPropertyId"],
|
|
18272
|
+
"products": ["id", "billingType", "chargeBillingCodeID", "createdByResourceID", "createdTime", "defaultVendorID", "description", "doesNotRequireProcurement", "externalProductID", "impersonatorCreatorResourceID", "internalProductID", "isActive", "isEligibleForRma", "isSerialized", "link", "manufacturerName", "manufacturerProductName", "markupRate", "msrp", "name", "periodType", "priceCostMethod", "productBillingCodeID", "productCategory", "sku", "unitCost", "unitPrice", "vendorProductNumber", "defaultInstalledProductCategoryID", "userDefinedFields"],
|
|
18273
|
+
"project_charges": ["id", "billableAmount", "billingCodeID", "chargeType", "contractServiceBundleID", "contractServiceID", "createDate", "creatorResourceID", "datePurchased", "description", "estimatedCost", "extendedCost", "internalCurrencyBillableAmount", "internalCurrencyUnitPrice", "internalPurchaseOrderNumber", "isBillableToCompany", "isBilled", "name", "notes", "organizationalLevelAssociationID", "productID", "projectID", "purchaseOrderNumber", "status", "statusLastModifiedBy", "statusLastModifiedDate", "unitCost", "unitPrice", "unitQuantity", "soapParentPropertyId"],
|
|
18274
|
+
"project_notes": ["id", "createDateTime", "createdByContactID", "creatorResourceID", "description", "impersonatorCreatorResourceID", "impersonatorUpdaterResourceID", "isAnnouncement", "lastActivityDate", "noteType", "projectID", "publish", "title", "soapParentPropertyId"],
|
|
18275
|
+
"projects": ["id", "actualBilledHours", "actualHours", "changeOrdersBudget", "changeOrdersRevenue", "companyID", "companyOwnerResourceID", "completedDateTime", "completedPercentage", "contractID", "createDateTime", "creatorResourceID", "department", "description", "duration", "endDateTime", "estimatedSalesCost", "estimatedTime", "extProjectNumber", "extProjectType", "impersonatorCreatorResourceID", "laborEstimatedCosts", "laborEstimatedMarginPercentage", "laborEstimatedRevenue", "lastActivityDateTime", "lastActivityPersonType", "lastActivityResourceID", "opportunityID", "organizationalLevelAssociationID", "originalEstimatedRevenue", "projectCostEstimatedMarginPercentage", "projectCostsBudget", "projectCostsRevenue", "projectLeadResourceID", "projectName", "projectNumber", "projectType", "purchaseOrderNumber", "sgda", "startDateTime", "status", "statusDateTime", "statusDetail", "userDefinedFields"],
|
|
18276
|
+
"purchase_approvals": ["id", "costType", "isApproved", "rejectNote"],
|
|
18277
|
+
"purchase_order_item_receiving": ["id", "purchaseOrderItemID", "quantityBackOrdered", "quantityNowReceiving", "quantityPreviouslyReceived", "receiveDate", "receivedByResourceID", "serialNumber", "vendorInvoiceNumber", "soapParentPropertyId"],
|
|
18278
|
+
"purchase_order_items": ["id", "chargeID", "contractID", "estimatedArrivalDate", "internalCurrencyUnitCost", "inventoryLocationID", "memo", "orderID", "productID", "projectID", "quantity", "salesOrderID", "ticketID", "unitCost", "soapParentPropertyId"],
|
|
18279
|
+
"purchase_orders": ["id", "cancelDateTime", "createDateTime", "creatorResourceID", "externalPONumber", "fax", "freight", "generalMemo", "impersonatorCreatorResourceID", "internalCurrencyFreight", "latestEstimatedArrivalDate", "paymentTerm", "phone", "purchaseForCompanyID", "purchaseOrderNumber", "purchaseOrderTemplateID", "shippingDate", "shippingType", "shipToAddress1", "shipToAddress2", "shipToCity", "shipToName", "shipToPostalCode", "shipToState", "showEachTaxInGroup", "showTaxCategory", "status", "submitDateTime", "taxRegionID", "useItemDescriptionsFrom", "vendorID", "vendorInvoiceNumber", "additionalVendorInvoiceNumbers"],
|
|
18280
|
+
"quote_items": ["id", "averageCost", "chargeID", "description", "expenseID", "highestCost", "internalCurrencyLineDiscount", "internalCurrencyUnitDiscount", "internalCurrencyUnitPrice", "isOptional", "isTaxable", "laborID", "lineDiscount", "markupRate", "name", "percentageDiscount", "periodType", "productID", "quantity", "quoteID", "quoteItemType", "serviceBundleID", "serviceID", "shippingID", "sortOrderID", "taxCategoryID", "totalEffectiveTax", "unitCost", "unitDiscount", "unitPrice", "soapParentPropertyId"],
|
|
18281
|
+
"quote_locations": ["id", "address1", "address2", "city", "postalCode", "state"],
|
|
18282
|
+
"quote_templates": ["id", "calculateTaxSeparately", "createDate", "createdBy", "currencyNegativeFormat", "currencyPositiveFormat", "dateFormat", "description", "displayTaxCategorySuperscripts", "isActive", "lastActivityBy", "lastActivityDate", "name", "numberFormat", "pageLayout", "pageNumberFormat", "showEachTaxInGroup", "showGridHeader", "showTaxCategory", "showVerticalGridLines"],
|
|
18283
|
+
"quotes": ["id", "approvalStatus", "approvalStatusChangedByResourceID", "approvalStatusChangedDate", "billToLocationID", "calculateTaxSeparately", "comment", "companyID", "contactID", "createDate", "creatorResourceID", "description", "effectiveDate", "expirationDate", "extApprovalContactResponse", "extApprovalResponseDate", "extApprovalResponseSignature", "externalQuoteNumber", "groupByID", "impersonatorCreatorResourceID", "isActive", "lastActivityDate", "lastModifiedBy", "lastPublishedByResourceID", "lastPublishedDateTime", "name", "opportunityID", "paymentTerm", "paymentType", "primaryQuote", "proposalProjectID", "purchaseOrderNumber", "quoteNumber", "quoteTemplateID", "shippingType", "shipToLocationID", "showEachTaxInGroup", "showTaxCategory", "soldToLocationID", "taxRegionID"],
|
|
18284
|
+
"resource_role_queues": ["id", "queueID", "resourceID", "soapParentPropertyId"],
|
|
18285
|
+
"resource_roles": ["id", "departmentID", "isActive", "queueID", "resourceID", "roleID", "soapParentPropertyId"],
|
|
18286
|
+
"resource_skills": ["id", "resourceID", "skillDescription", "skillID", "skillLevel", "soapParentPropertyId"],
|
|
18287
|
+
"resource_time_off_approvers": ["id", "resourceID", "approverResourceID", "approvalLevel", "soapParentPropertyId"],
|
|
18288
|
+
"resource_time_off_balances": ["id", "resourceID", "floatingHolidayAnnualAllowance", "floatingHolidayBalance", "floatingHolidayPlanned", "floatingHolidayUsed", "floatingHolidayWaitingApproval", "personalTimeAnnualAllowance", "personalTimeBalance", "personalTimePlanned", "personalTimeUsed", "personalTimeWaitingApproval", "sickTimeAnnualAllowance", "sickTimeBalance", "sickTimePlanned", "sickTimeUsed", "sickTimeWaitingApproval", "vacationTimeAnnualAllowance", "vacationTimeBalance", "vacationTimePlanned", "vacationTimeUsed", "vacationTimeWaitingApproval", "year", "soapParentPropertyId"],
|
|
18289
|
+
"resources": ["id", "accountingReferenceID", "dateFormat", "defaultServiceDeskRoleID", "email", "email2", "email3", "emailTypeCode", "emailTypeCode2", "emailTypeCode3", "firstName", "gender", "greeting", "hireDate", "homePhone", "initials", "internalCost", "isActive", "lastName", "licenseType", "locationID", "middleName", "mobilePhone", "numberFormat", "officeExtension", "officePhone", "payrollIdentifier", "payrollType", "resourceType", "suffix", "surveyResourceRating", "timeFormat", "title", "travelAvailabilityPct", "userName", "userType"],
|
|
18290
|
+
"roles": ["id", "description", "hourlyFactor", "hourlyRate", "isActive", "isExcludedFromNewContracts", "isSystemRole", "name", "quoteItemDefaultTaxCategoryId", "roleType"],
|
|
18291
|
+
"sales_orders": ["id", "additionalBillToAddressInformation", "additionalShipToAddressInformation", "billingAddress1", "billingAddress2", "billToCity", "billToCountryID", "billToPostalCode", "billToState", "companyID", "contactID", "impersonatorCreatorResourceID", "opportunityID", "organizationalLevelAssociationID", "ownerResourceID", "promisedFulfillmentDate", "salesOrderDate", "shipToAddress1", "shipToAddress2", "shipToCity", "shipToCountryID", "shipToPostalCode", "shipToState", "status", "title", "soapParentPropertyId", "userDefinedFields"],
|
|
18292
|
+
"service_call_task_resources": ["id", "resourceID", "serviceCallTaskID", "soapParentPropertyId"],
|
|
18293
|
+
"service_call_tasks": ["id", "serviceCallID", "taskID", "soapParentPropertyId"],
|
|
18294
|
+
"service_call_ticket_resources": ["id", "resourceID", "serviceCallTicketID", "soapParentPropertyId"],
|
|
18295
|
+
"service_call_tickets": ["id", "serviceCallID", "ticketID", "soapParentPropertyId"],
|
|
18296
|
+
"service_calls": ["id", "cancelationNoticeHours", "canceledByResourceID", "canceledDateTime", "companyID", "companyLocationID", "createDateTime", "creatorResourceID", "description", "duration", "endDateTime", "impersonatorCreatorResourceID", "isComplete", "lastModifiedDateTime", "startDateTime", "status"],
|
|
18297
|
+
"service_level_agreement_results": ["id", "firstResponseElapsedHours", "firstResponseInitiatingResourceID", "firstResponseResourceID", "isFirstResponseMet", "isResolutionMet", "isResolutionPlanMet", "resolutionElapsedHours", "resolutionPlanElapsedHours", "resolutionPlanResourceID", "resolutionResourceID", "serviceLevelAgreementName", "ticketID", "soapParentPropertyId"],
|
|
18298
|
+
"services": ["id", "billingCodeID", "createDate", "creatorResourceID", "description", "invoiceDescription", "isActive", "lastModifiedDate", "markupRate", "name", "periodType", "serviceLevelAgreementID", "unitCost", "unitPrice", "updateResourceID", "vendorCompanyID", "manufacturerServiceProvider", "manufacturerServiceProviderProductNumber", "catalogNumberPartNumber", "sku", "internalID", "externalID", "url", "userDefinedFields"],
|
|
18299
|
+
"skills": ["id", "categoryID", "description", "isActive", "name"],
|
|
18300
|
+
"subscription_periods": ["id", "periodCost", "periodDate", "periodPrice", "postedDate", "purchaseOrderNumber", "subscriptionID", "soapParentPropertyId"],
|
|
18301
|
+
"subscriptions": ["id", "configurationItemID", "description", "effectiveDate", "expirationDate", "impersonatorCreatorResourceID", "materialCodeID", "organizationalLevelAssociationID", "periodCost", "periodPrice", "periodType", "purchaseOrderNumber", "status", "subscriptionName", "totalCost", "totalPrice", "vendorID", "userDefinedFields"],
|
|
18302
|
+
"survey_results": ["id", "companyID", "companyRating", "completeDate", "contactID", "contactRating", "resourceRating", "sendDate", "surveyID", "surveyRating", "ticketID"],
|
|
18303
|
+
"surveys": ["id", "description", "displayName", "name"],
|
|
18304
|
+
"tag_aliases": ["id", "alias", "tagID", "soapParentPropertyId"],
|
|
18305
|
+
"tag_groups": ["id", "displayColor", "isActive", "isSystem", "label"],
|
|
18306
|
+
"tags": ["id", "createDateTime", "isActive", "isExcludedFromAutomaticTagging", "isSystem", "label", "lastModifiedDateTime", "tagGroupID"],
|
|
18307
|
+
"task_secondary_resources": ["id", "resourceID", "roleID", "taskID", "soapParentPropertyId"],
|
|
18308
|
+
"tasks": ["id", "assignedResourceID", "assignedResourceRoleID", "billingCodeID", "canClientPortalUserCompleteTask", "companyLocationID", "completedByResourceID", "completedByType", "completedDateTime", "createDateTime", "creatorResourceID", "creatorType", "departmentID", "description", "endDateTime", "estimatedHours", "externalID", "hoursToBeScheduled", "isTaskBillable", "isVisibleInClientPortal", "lastActivityDateTime", "lastActivityPersonType", "lastActivityResourceID", "phaseID", "priority", "priorityLabel", "projectID", "purchaseOrderNumber", "remainingHours", "startDateTime", "status", "taskCategoryID", "taskNumber", "taskType", "title", "soapParentPropertyId", "userDefinedFields"],
|
|
18309
|
+
"tax_regions": ["id", "isActive", "name"],
|
|
18310
|
+
"ticket_additional_configuration_items": ["id", "configurationItemID", "ticketID", "soapParentPropertyId"],
|
|
18311
|
+
"ticket_additional_contacts": ["id", "contactID", "ticketID", "soapParentPropertyId"],
|
|
18312
|
+
"ticket_attachments": ["id", "attachDate", "attachedByContactID", "attachedByResourceID", "attachmentType", "contentType", "creatorType", "fileSize", "fullPath", "impersonatorCreatorResourceID", "opportunityID", "parentAttachmentID", "parentID", "publish", "ticketID", "ticketNoteID", "timeEntryID", "title", "data", "parentType", "soapParentPropertyId", "isTaskAttachment"],
|
|
18313
|
+
"ticket_categories": ["id", "displayColorRGB", "isActive", "isApiOnly", "isGlobalDefault", "name", "nickname"],
|
|
18314
|
+
"ticket_category_field_defaults": ["id", "description", "estimatedHours", "issueTypeID", "organizationalLevelAssociationID", "priority", "purchaseOrderNumber", "queueID", "resolution", "serviceLevelAgreementID", "sourceID", "status", "subIssueTypeID", "ticketCategoryID", "ticketTypeID", "title", "workTypeID", "soapParentPropertyId"],
|
|
18315
|
+
"ticket_change_request_approvals": ["id", "approveRejectDateTime", "approveRejectNote", "contactID", "isApproved", "resourceID", "ticketID", "soapParentPropertyId"],
|
|
18316
|
+
"ticket_charges": ["id", "billableAmount", "billingCodeID", "chargeType", "contractServiceBundleID", "contractServiceID", "createDate", "creatorResourceID", "datePurchased", "description", "extendedCost", "internalCurrencyBillableAmount", "internalCurrencyUnitPrice", "internalPurchaseOrderNumber", "isBillableToCompany", "isBilled", "name", "notes", "organizationalLevelAssociationID", "productID", "purchaseOrderNumber", "status", "statusLastModifiedBy", "statusLastModifiedDate", "ticketID", "unitCost", "unitPrice", "unitQuantity", "soapParentPropertyId"],
|
|
18317
|
+
"ticket_checklist_items": ["id", "completedByResourceID", "completedDateTime", "isCompleted", "isImportant", "itemName", "knowledgebaseArticleID", "position", "ticketID", "soapParentPropertyId"],
|
|
18318
|
+
"ticket_history": ["id", "action", "date", "detail", "resourceID", "ticketID", "soapParentPropertyId"],
|
|
18319
|
+
"ticket_note_attachments": ["id", "attachDate", "attachedByContactID", "attachedByResourceID", "attachmentType", "contentType", "creatorType", "fileSize", "fullPath", "impersonatorCreatorResourceID", "opportunityID", "parentID", "publish", "ticketID", "ticketNoteID", "title", "data", "parentType", "soapParentPropertyId", "isTaskAttachment"],
|
|
18320
|
+
"ticket_note_webhooks": ["id", "deactivationUrl", "isActive", "isReady", "isSubscribedToCreateEvents", "isSubscribedToDeleteEvents", "isSubscribedToUpdateEvents", "name", "notificationEmailAddress", "ownerResourceID", "secretKey", "sendThresholdExceededNotification", "webhookGUID", "webhookUrl"],
|
|
18321
|
+
"ticket_notes": ["id", "createDateTime", "createdByContactID", "creatorResourceID", "description", "impersonatorCreatorResourceID", "impersonatorUpdaterResourceID", "lastActivityDate", "noteType", "publish", "ticketID", "title", "soapParentPropertyId"],
|
|
18322
|
+
"ticket_secondary_resources": ["id", "resourceID", "roleID", "ticketID", "soapParentPropertyId"],
|
|
18323
|
+
"ticket_webhooks": ["id", "deactivationUrl", "isActive", "isReady", "isSubscribedToCreateEvents", "isSubscribedToDeleteEvents", "isSubscribedToUpdateEvents", "name", "notificationEmailAddress", "ownerResourceID", "secretKey", "sendThresholdExceededNotification", "webhookGUID", "webhookUrl"],
|
|
18324
|
+
"tickets": ["id", "apiVendorID", "assignedResourceID", "assignedResourceRoleID", "billingCodeID", "changeApprovalBoard", "changeApprovalStatus", "changeApprovalType", "changeInfoField1", "changeInfoField2", "changeInfoField3", "changeInfoField4", "changeInfoField5", "companyID", "companyLocationID", "completedByResourceID", "completedDate", "configurationItemID", "contactID", "contractID", "contractServiceBundleID", "contractServiceID", "createDate", "createdByContactID", "creatorResourceID", "creatorType", "currentServiceThermometerRating", "description", "dueDateTime", "estimatedHours", "externalID", "firstResponseAssignedResourceID", "firstResponseDateTime", "firstResponseDueDateTime", "firstResponseInitiatingResourceID", "hoursToBeScheduled", "impersonatorCreatorResourceID", "isAssignedToComanaged", "issueType", "isVisibleToComanaged", "lastActivityDate", "lastActivityPersonType", "lastActivityResourceID", "lastCustomerNotificationDateTime", "lastCustomerVisibleActivityDateTime", "lastTrackedModificationDateTime", "monitorID", "monitorTypeID", "opportunityID", "organizationalLevelAssociationID", "previousServiceThermometerRating", "priority", "problemTicketId", "projectID", "purchaseOrderNumber", "queueID", "resolution", "resolutionPlanDateTime", "resolutionPlanDueDateTime", "resolvedDateTime", "resolvedDueDateTime", "rmaStatus", "rmaType", "rmmAlertID", "serviceLevelAgreementHasBeenMet", "serviceLevelAgreementID", "serviceLevelAgreementPausedNextEventHours", "serviceThermometerTemperature", "source", "status", "subIssueType", "ticketCategory", "ticketNumber", "ticketType", "title", "userDefinedFields"],
|
|
18325
|
+
"time_entries": ["id", "billingApprovalDateTime", "billingApprovalLevelMostRecent", "billingApprovalResourceID", "billingCodeID", "contractID", "contractServiceBundleID", "contractServiceID", "createDateTime", "creatorUserID", "dateWorked", "endDateTime", "hoursToBill", "hoursWorked", "impersonatorCreatorResourceID", "impersonatorUpdaterResourceID", "internalBillingCodeID", "internalNotes", "isInternalNotesVisibleToComanaged", "isNonBillable", "lastModifiedDateTime", "lastModifiedUserID", "offsetHours", "resourceID", "roleID", "showOnInvoice", "startDateTime", "summaryNotes", "taskID", "ticketID", "timeEntryType"],
|
|
18326
|
+
"time_entry_attachments": ["id", "attachDate", "attachedByContactID", "attachedByResourceID", "attachmentType", "contentType", "creatorType", "fileSize", "fullPath", "impersonatorCreatorResourceID", "opportunityID", "parentID", "publish", "taskID", "ticketID", "timeEntryID", "title", "data", "parentType", "soapParentPropertyId", "isTaskAttachment"],
|
|
18327
|
+
"time_off_requests": ["id", "approvedDateTime", "approveRejectResourceID", "createdByResourceID", "createDateTime", "endTime", "hours", "impersonatorApproveRejectResourceID", "lastModifiedByResourceID", "lastModifiedDateTime", "lastApprovedLevel", "reason", "rejectReason", "requestDate", "resourceID", "startTime", "status", "timeOffRequestType", "soapParentPropertyId"]
|
|
18328
|
+
};
|
|
18329
|
+
var MODEL_DATE_FIELDS = {
|
|
18330
|
+
"appointments": ["createDateTime", "endDateTime", "startDateTime", "updateDateTime"],
|
|
18331
|
+
"billing_item_approval_levels": ["approvalDateTime"],
|
|
18332
|
+
"billing_items": ["itemDate", "postedDate", "postedOnTime", "webServiceDate"],
|
|
18333
|
+
"companies": ["createDate", "lastActivityDate", "lastTrackedModifiedDateTime"],
|
|
18334
|
+
"company_notes": ["completedDateTime", "createDateTime", "endDateTime", "lastModifiedDate", "startDateTime"],
|
|
18335
|
+
"configuration_item_billing_product_associations": ["effectiveDate", "expirationDate"],
|
|
18336
|
+
"configuration_item_dns_records": ["createDateTime"],
|
|
18337
|
+
"configuration_item_notes": ["createDateTime", "lastActivityDate"],
|
|
18338
|
+
"configuration_items": ["createDate", "dattoLastCheckInDateTime", "domainRegistrationDateTime", "domainLastUpdatedDateTime", "domainExpirationDateTime", "installDate", "lastModifiedTime", "sslValidFromDateTime", "sslValidUntilDateTime", "warrantyExpirationDate"],
|
|
18339
|
+
"contacts": ["bulkEmailOptOutTime", "createDate", "lastActivityDate", "lastModifiedDate", "solicitationOptOutTime"],
|
|
18340
|
+
"contract_billing_rules": ["endDate", "startDate"],
|
|
18341
|
+
"contract_blocks": ["datePurchased", "endDate", "startDate"],
|
|
18342
|
+
"contract_charges": ["createDate", "datePurchased", "statusLastModifiedDate"],
|
|
18343
|
+
"contract_milestones": ["createDate", "dateDue"],
|
|
18344
|
+
"contract_notes": ["createDateTime", "lastActivityDate"],
|
|
18345
|
+
"contract_retainers": ["datePurchased", "endDate", "startDate"],
|
|
18346
|
+
"contract_service_bundle_units": ["approveAndPostDate", "endDate", "startDate"],
|
|
18347
|
+
"contract_service_units": ["approveAndPostDate", "endDate", "startDate"],
|
|
18348
|
+
"contract_ticket_purchases": ["datePurchased", "endDate", "startDate"],
|
|
18349
|
+
"contracts": ["endDate", "lastModifiedDateTime", "startDate"],
|
|
18350
|
+
"currencies": ["lastModifiedDateTime"],
|
|
18351
|
+
"expense_item_attachments": ["attachDate"],
|
|
18352
|
+
"expense_items": ["expenseDate"],
|
|
18353
|
+
"expense_reports": ["approvedDate", "submitDate", "weekEnding"],
|
|
18354
|
+
"holidays": ["holidayDate"],
|
|
18355
|
+
"inventory_products": ["createDateTime"],
|
|
18356
|
+
"invoices": ["createDateTime", "dueDate", "fromDate", "invoiceDateTime", "paidDate", "toDate", "voidedDate", "webServiceDate"],
|
|
18357
|
+
"notification_history": ["notificationSentTime"],
|
|
18358
|
+
"opportunities": ["closedDate", "createDate", "lastActivity", "lostDate", "projectedCloseDate", "promisedFulfillmentDate", "startDate", "throughDate"],
|
|
18359
|
+
"opportunity_attachments": ["attachDate"],
|
|
18360
|
+
"phases": ["createDate", "dueDate", "lastActivityDateTime", "startDate"],
|
|
18361
|
+
"products": ["createdTime"],
|
|
18362
|
+
"project_charges": ["createDate", "datePurchased", "statusLastModifiedDate"],
|
|
18363
|
+
"project_notes": ["createDateTime", "lastActivityDate"],
|
|
18364
|
+
"projects": ["completedDateTime", "createDateTime", "endDateTime", "lastActivityDateTime", "startDateTime", "statusDateTime"],
|
|
18365
|
+
"purchase_order_item_receiving": ["receiveDate"],
|
|
18366
|
+
"purchase_order_items": ["estimatedArrivalDate"],
|
|
18367
|
+
"purchase_orders": ["cancelDateTime", "createDateTime", "latestEstimatedArrivalDate", "shippingDate", "submitDateTime"],
|
|
18368
|
+
"quote_templates": ["createDate", "lastActivityDate"],
|
|
18369
|
+
"quotes": ["approvalStatusChangedDate", "createDate", "effectiveDate", "expirationDate", "extApprovalResponseDate", "lastActivityDate", "lastPublishedDateTime"],
|
|
18370
|
+
"resources": ["hireDate"],
|
|
18371
|
+
"sales_orders": ["promisedFulfillmentDate", "salesOrderDate"],
|
|
18372
|
+
"service_calls": ["canceledDateTime", "createDateTime", "endDateTime", "lastModifiedDateTime", "startDateTime"],
|
|
18373
|
+
"services": ["createDate", "lastModifiedDate"],
|
|
18374
|
+
"subscription_periods": ["periodDate", "postedDate"],
|
|
18375
|
+
"subscriptions": ["effectiveDate", "expirationDate"],
|
|
18376
|
+
"survey_results": ["completeDate", "sendDate"],
|
|
18377
|
+
"tags": ["createDateTime", "lastModifiedDateTime"],
|
|
18378
|
+
"tasks": ["completedDateTime", "createDateTime", "endDateTime", "lastActivityDateTime", "startDateTime"],
|
|
18379
|
+
"ticket_attachments": ["attachDate"],
|
|
18380
|
+
"ticket_change_request_approvals": ["approveRejectDateTime"],
|
|
18381
|
+
"ticket_charges": ["createDate", "datePurchased", "statusLastModifiedDate"],
|
|
18382
|
+
"ticket_checklist_items": ["completedDateTime"],
|
|
18383
|
+
"ticket_history": ["date"],
|
|
18384
|
+
"ticket_note_attachments": ["attachDate"],
|
|
18385
|
+
"ticket_notes": ["createDateTime", "lastActivityDate"],
|
|
18386
|
+
"tickets": ["completedDate", "createDate", "dueDateTime", "firstResponseDateTime", "firstResponseDueDateTime", "lastActivityDate", "lastCustomerNotificationDateTime", "lastCustomerVisibleActivityDateTime", "lastTrackedModificationDateTime", "resolutionPlanDateTime", "resolutionPlanDueDateTime", "resolvedDateTime", "resolvedDueDateTime"],
|
|
18387
|
+
"time_entries": ["billingApprovalDateTime", "createDateTime", "dateWorked", "endDateTime", "lastModifiedDateTime", "startDateTime"],
|
|
18388
|
+
"time_entry_attachments": ["attachDate"],
|
|
18389
|
+
"time_off_requests": ["approvedDateTime", "createDateTime", "endTime", "lastModifiedDateTime", "requestDate", "startTime"]
|
|
18390
|
+
};
|
|
18391
|
+
|
|
18392
|
+
// src/resources/base/types.ts
|
|
18393
|
+
var BOUNDED_QUERY_DEFAULT_LIMIT = 500;
|
|
18394
|
+
var BOUNDED_QUERY_MAX_LIMIT = 2500;
|
|
18395
|
+
var LATEST_MAX_COUNT_PROBES = 64;
|
|
18396
|
+
var MARKER_RECOVERY_LIMIT = 5;
|
|
18397
|
+
|
|
18398
|
+
// src/resources/base/helpers.ts
|
|
18399
|
+
function describeResponseShape(body) {
|
|
18400
|
+
if (body === null) return "null";
|
|
18401
|
+
if (Array.isArray(body)) return `array(len=${body.length})`;
|
|
18402
|
+
if (typeof body !== "object") return typeof body;
|
|
18403
|
+
const rec = body;
|
|
18404
|
+
const keys = Object.keys(rec).sort();
|
|
18405
|
+
const typeOf = (v) => v === null ? "null" : Array.isArray(v) ? "array" : typeof v;
|
|
18406
|
+
const parts = keys.slice(0, 16).map((k) => `${k}:${typeOf(rec[k])}`);
|
|
18407
|
+
const more = keys.length > 16 ? `, +${keys.length - 16} more` : "";
|
|
18408
|
+
return `{ ${parts.join(", ")}${more} }`;
|
|
18409
|
+
}
|
|
18410
|
+
function defaultMarkerToken(data) {
|
|
18411
|
+
for (const field of ["name", "title"]) {
|
|
18412
|
+
const v = data[field];
|
|
18413
|
+
if (typeof v === "string" && v.trim() !== "") return v.trim();
|
|
18414
|
+
}
|
|
18415
|
+
return null;
|
|
18416
|
+
}
|
|
18417
|
+
var CONTEXT_ABSENT_MARKER = "#context-absent";
|
|
18418
|
+
function contextAbsentKey(path) {
|
|
18419
|
+
return path + "\0context-absent";
|
|
18420
|
+
}
|
|
18421
|
+
function noteCharsTruncationMarker(charsRemoved) {
|
|
18422
|
+
return `\u2026 [+${charsRemoved} chars truncated]`;
|
|
18423
|
+
}
|
|
18424
|
+
function truncateNoteChars(items, cap, fields) {
|
|
18425
|
+
let truncated = 0;
|
|
18426
|
+
const out = items.map((item) => {
|
|
18427
|
+
if (!isRecord2(item)) return item;
|
|
18428
|
+
let changed = false;
|
|
18429
|
+
const copy = { ...item };
|
|
18430
|
+
for (const field of fields) {
|
|
18431
|
+
const v = copy[field];
|
|
18432
|
+
if (typeof v === "string" && v.length > cap) {
|
|
18433
|
+
const end = cap > 0 && (v.charCodeAt(cap - 1) & 64512) === 55296 ? cap - 1 : cap;
|
|
18434
|
+
copy[field] = v.slice(0, end) + noteCharsTruncationMarker(v.length - end);
|
|
18435
|
+
truncated += 1;
|
|
18436
|
+
changed = true;
|
|
18437
|
+
}
|
|
18438
|
+
}
|
|
18439
|
+
return changed ? copy : item;
|
|
18440
|
+
});
|
|
18441
|
+
return [out, truncated];
|
|
18442
|
+
}
|
|
18443
|
+
function blindEmptyReason(stamp) {
|
|
18444
|
+
return {
|
|
18445
|
+
surface: stamp.surface,
|
|
18446
|
+
kind: "blind-surface",
|
|
18447
|
+
note: stamp.note,
|
|
18448
|
+
authority: stamp.authority,
|
|
18449
|
+
remedy: "the note is the remedy \u2014 the authority op answers for a record"
|
|
18450
|
+
};
|
|
18451
|
+
}
|
|
18452
|
+
function attachUpdateTargetMissReason(err, key) {
|
|
18453
|
+
const pinned = EMPTY_REASON_STAMPS[key]?.update;
|
|
18454
|
+
if (pinned === void 0) return;
|
|
18455
|
+
err.details = { ...err.details ?? {}, emptyReason: pinned };
|
|
18456
|
+
}
|
|
18457
|
+
function buildDryRunResult(spec, policy = { redactPersonalData: true }) {
|
|
18458
|
+
if (spec.operation === "") {
|
|
18459
|
+
throw new AutotaskConfigError("dry-run: the operation name is required");
|
|
18460
|
+
}
|
|
18461
|
+
const wouldApply = spec.checks.every((check) => check.ok);
|
|
18462
|
+
const resource = spec.operation.split(".")[0] ?? spec.operation;
|
|
18463
|
+
const diffPart = spec.diff !== void 0 ? {
|
|
18464
|
+
diff: spec.diff.map((entry) => {
|
|
18465
|
+
if (entry.to === void 0) return entry;
|
|
18466
|
+
const to = isRedactedFieldName(entry.field, policy) ? REDACTED : redactResponseBody(entry.to, policy);
|
|
18467
|
+
return { ...entry, to };
|
|
18468
|
+
})
|
|
18469
|
+
} : {};
|
|
18470
|
+
const dataPart = spec.data !== void 0 ? { data: redactResponseBody(spec.data, policy) } : {};
|
|
18471
|
+
return {
|
|
18472
|
+
operation: spec.operation,
|
|
18473
|
+
wouldApply,
|
|
18474
|
+
target: { resource, ids: spec.ids },
|
|
18475
|
+
request: { method: spec.method, path: spec.path },
|
|
18476
|
+
...diffPart,
|
|
18477
|
+
checks: spec.checks,
|
|
18478
|
+
impact: spec.impact,
|
|
18479
|
+
simulated: true,
|
|
18480
|
+
warnings: spec.warnings ?? [],
|
|
18481
|
+
...dataPart,
|
|
18482
|
+
...spec.emptyReason !== void 0 ? { emptyReason: spec.emptyReason } : {}
|
|
18483
|
+
};
|
|
18484
|
+
}
|
|
18485
|
+
function computeFieldDiff(current, data) {
|
|
18486
|
+
const out = [];
|
|
18487
|
+
for (const [field, to] of Object.entries(data)) {
|
|
18488
|
+
if (field === "id" || to === void 0) continue;
|
|
18489
|
+
const from = current[field];
|
|
18490
|
+
if (from !== void 0 && JSON.stringify(from) === JSON.stringify(to)) continue;
|
|
18491
|
+
out.push({ field, from: from === void 0 ? null : from, to });
|
|
18492
|
+
}
|
|
18493
|
+
return out;
|
|
18494
|
+
}
|
|
18495
|
+
function mergePartialBody(current, data) {
|
|
18496
|
+
const merged = { ...current };
|
|
18497
|
+
for (const [key, value] of Object.entries(data)) {
|
|
18498
|
+
if (value === void 0) continue;
|
|
18499
|
+
merged[key] = value;
|
|
18500
|
+
}
|
|
18501
|
+
return merged;
|
|
18502
|
+
}
|
|
18503
|
+
function normalizeUdfInput(value) {
|
|
18504
|
+
if (Array.isArray(value)) {
|
|
18505
|
+
return value.map((entry) => {
|
|
18506
|
+
if (!isRecord2(entry) || typeof entry.name !== "string" || entry.name === "") {
|
|
18507
|
+
throw new AutotaskConfigError("userDefinedFields array entries must be { name, value } objects");
|
|
18508
|
+
}
|
|
18509
|
+
return { name: entry.name, value: entry.value };
|
|
18510
|
+
});
|
|
18511
|
+
}
|
|
18512
|
+
if (isRecord2(value)) {
|
|
18513
|
+
return Object.entries(value).map(([name, entryValue]) => ({ name, value: entryValue }));
|
|
18514
|
+
}
|
|
18515
|
+
throw new AutotaskConfigError("userDefinedFields must be an array of { name, value } entries or a name-to-value map");
|
|
18516
|
+
}
|
|
18517
|
+
function blindStampFor(stamp, filter) {
|
|
18518
|
+
if (stamp === void 0) return void 0;
|
|
18519
|
+
if (stamp.kind !== "unfiltered-blind") return stamp;
|
|
18520
|
+
const conditions = normalizedConditions(filter);
|
|
18521
|
+
return conditions === void 0 || conditions.length === 0 ? stamp : void 0;
|
|
18522
|
+
}
|
|
18523
|
+
function decodeFileData(body) {
|
|
18524
|
+
if (isRecord2(body)) {
|
|
18525
|
+
const data = body.data;
|
|
18526
|
+
if (typeof data === "string") return Buffer.from(data, "base64");
|
|
18527
|
+
if (Array.isArray(data) && data.every((b) => typeof b === "number")) {
|
|
18528
|
+
return Buffer.from(data);
|
|
18529
|
+
}
|
|
18530
|
+
}
|
|
18531
|
+
if (typeof body === "string") return Buffer.from(body, "base64");
|
|
18532
|
+
throw new AutotaskError(
|
|
18533
|
+
"Expected the file envelope FileQueryResultModel { data } (or a base64 string); got a different shape",
|
|
18534
|
+
{ code: "INVALID_RESPONSE", category: "server" }
|
|
18535
|
+
);
|
|
18536
|
+
}
|
|
18537
|
+
var DEPLURAL_EXCEPTIONS = {
|
|
18538
|
+
tag_aliase: "tag_alias"
|
|
18539
|
+
};
|
|
18540
|
+
function entityNameFromPath(resourcePath) {
|
|
18541
|
+
const segment = resourcePath.slice(resourcePath.lastIndexOf("/") + 1);
|
|
18542
|
+
const snake = segment.replace(/([A-Z])/g, (_m, c, i) => (i > 0 ? `_` : "") + c.toLowerCase()).replace(/_{2,}/g, "_");
|
|
18543
|
+
if (snake.endsWith("ies")) return `${snake.slice(0, -3)}y`;
|
|
18544
|
+
if (snake.endsWith("s")) {
|
|
18545
|
+
const stripped = snake.slice(0, -1);
|
|
18546
|
+
return DEPLURAL_EXCEPTIONS[stripped] ?? stripped;
|
|
18547
|
+
}
|
|
18548
|
+
return snake;
|
|
18549
|
+
}
|
|
18550
|
+
|
|
18551
|
+
// src/resources/special/_shared/bounded-part.ts
|
|
18552
|
+
async function boundedPart(http, path, conditions, limit, operation, includeFields) {
|
|
18553
|
+
const body = { MaxRecords: limit, filter: conditions };
|
|
18554
|
+
if (includeFields !== void 0) body.IncludeFields = includeFields;
|
|
18555
|
+
const res = await http.request({
|
|
18556
|
+
method: "POST",
|
|
18557
|
+
idempotent: true,
|
|
18558
|
+
path,
|
|
18559
|
+
body,
|
|
18560
|
+
operation
|
|
18561
|
+
});
|
|
18562
|
+
const { items, pageDetails } = unwrapItems(res, operation);
|
|
18563
|
+
const capped = items.length >= limit;
|
|
18564
|
+
return { items, capped, hasMore: capped && pageDetails.nextPageUrl != null };
|
|
18565
|
+
}
|
|
18566
|
+
|
|
18567
|
+
// src/resources/special/_shared/relation-filters.ts
|
|
18568
|
+
var JOIN_SET_CAP = 300;
|
|
18569
|
+
function capJoinSet(ids2, cap = JOIN_SET_CAP) {
|
|
18570
|
+
const sorted = [...new Set(ids2)].sort((a, b) => a - b);
|
|
18571
|
+
if (sorted.length <= cap) return { ids: sorted, truncated: false };
|
|
18572
|
+
return { ids: sorted.slice(0, cap), truncated: true };
|
|
18573
|
+
}
|
|
18574
|
+
var RELATION_TARGETS = {
|
|
18575
|
+
ticket: { resource: "tickets", path: "/V1.0/Tickets/query" },
|
|
18576
|
+
task: { resource: "tasks", path: "/V1.0/Tasks/query" },
|
|
18577
|
+
project: { resource: "projects", path: "/V1.0/Projects/query" }
|
|
18578
|
+
};
|
|
18579
|
+
function relationsFor(resourceKey) {
|
|
18580
|
+
const fields = MODEL_FIELD_SETS[resourceKey] ?? [];
|
|
18581
|
+
return Object.keys(RELATION_TARGETS).filter((rel) => fields.includes(`${rel}ID`));
|
|
18582
|
+
}
|
|
18583
|
+
var isRelationLeaf = (c) => isRecord2(c) && c.op !== "and" && c.op !== "or" && typeof c.field === "string" && c.field.includes(".");
|
|
18584
|
+
function hasRelationConditions(conditions) {
|
|
18585
|
+
if (conditions === void 0) return false;
|
|
18586
|
+
return conditions.some((c) => isRelationLeaf(c) || isRecord2(c) && Array.isArray(c.items) && hasRelationConditions(c.items));
|
|
18587
|
+
}
|
|
18588
|
+
function validateRelationLeaves(resourceKey, conditions, op) {
|
|
18589
|
+
const valid = relationsFor(resourceKey);
|
|
18590
|
+
const walk = (conds) => {
|
|
18591
|
+
for (const c of conds) {
|
|
18592
|
+
if (isRecord2(c) && Array.isArray(c.items)) walk(c.items);
|
|
18593
|
+
if (!isRelationLeaf(c)) continue;
|
|
18594
|
+
const field = c.field;
|
|
18595
|
+
const [rel, sub, ...rest] = field.split(".");
|
|
18596
|
+
const fail = (why) => {
|
|
18597
|
+
throw new AutotaskConfigError(`${op}: relation filter "${field}" ${why} \u2014 the request was not issued`, {
|
|
18598
|
+
operation: op,
|
|
18599
|
+
suggestedAction: valid.length > 0 ? `Use ${valid.map((r) => `${r}.<${RELATION_TARGETS[r].resource} field>`).join(" or ")} (one hop; field names from operations.fieldCatalog).` : `${resourceKey} has no relation filters; filter on its own fields.`
|
|
18600
|
+
});
|
|
18601
|
+
};
|
|
18602
|
+
if (c.udf === true) fail("cannot be a UDF condition");
|
|
18603
|
+
if (rest.length > 0 || !sub) fail("is not <relation>.<field> (multi-hop relations are not supported)");
|
|
18604
|
+
if (!valid.includes(rel)) fail(`names an unknown relation for ${resourceKey} (valid: ${valid.join(", ") || "none"})`);
|
|
18605
|
+
const parentFields = MODEL_FIELD_SETS[RELATION_TARGETS[rel].resource];
|
|
18606
|
+
if (parentFields !== void 0 && !parentFields.includes(sub)) {
|
|
18607
|
+
fail(`names a field unknown on ${RELATION_TARGETS[rel].resource}`);
|
|
18608
|
+
}
|
|
18609
|
+
}
|
|
18610
|
+
};
|
|
18611
|
+
walk(conditions);
|
|
18612
|
+
}
|
|
18613
|
+
async function rewriteRelationFilters(http, resourceKey, conditions, op) {
|
|
18614
|
+
if (!hasRelationConditions(conditions)) return void 0;
|
|
18615
|
+
const conds = conditions;
|
|
18616
|
+
validateRelationLeaves(resourceKey, conds, op);
|
|
18617
|
+
const parentIds = async (rel, leaves) => {
|
|
18618
|
+
const target = RELATION_TARGETS[rel];
|
|
18619
|
+
const parentConds = buildFilter(leaves.map((l) => ({ ...l, field: l.field.slice(rel.length + 1) }))).filter;
|
|
18620
|
+
const res = await boundedPart(http, target.path, parentConds, JOIN_SET_CAP, op, ["id"]);
|
|
18621
|
+
if (res.hasMore) {
|
|
18622
|
+
throw new AutotaskConfigError(
|
|
18623
|
+
`${op}: the ${rel}.* conditions match more than ${JOIN_SET_CAP} ${target.resource} \u2014 the ${resourceKey} query was not issued (a partial ${rel} set would give partial results)`,
|
|
18624
|
+
{ operation: op, suggestedAction: `Narrow the ${rel}.* conditions (e.g. add a ${rel} date or status condition) or filter ${resourceKey} by ${rel}ID directly.` }
|
|
18625
|
+
);
|
|
18626
|
+
}
|
|
18627
|
+
return capJoinSet(res.items.map((r) => Number(r.id)).filter((n) => Number.isFinite(n) && n > 0)).ids;
|
|
18628
|
+
};
|
|
18629
|
+
const NEVER = null;
|
|
18630
|
+
const rewrite = async (list, mode) => {
|
|
18631
|
+
const out = [];
|
|
18632
|
+
const byRel = /* @__PURE__ */ new Map();
|
|
18633
|
+
for (const c of list) {
|
|
18634
|
+
if (isRelationLeaf(c)) {
|
|
18635
|
+
const rel = c.field.split(".")[0];
|
|
18636
|
+
if (mode === "and") {
|
|
18637
|
+
byRel.set(rel, [...byRel.get(rel) ?? [], c]);
|
|
18638
|
+
continue;
|
|
18639
|
+
}
|
|
18640
|
+
const ids2 = await parentIds(rel, [c]);
|
|
18641
|
+
if (ids2.length > 0) out.push({ field: `${rel}ID`, op: "in", value: ids2 });
|
|
18642
|
+
continue;
|
|
18643
|
+
}
|
|
18644
|
+
if (isRecord2(c) && (c.op === "and" || c.op === "or") && Array.isArray(c.items) && hasRelationConditions(c.items)) {
|
|
18645
|
+
const items = await rewrite(c.items, c.op);
|
|
18646
|
+
if (items === NEVER) {
|
|
18647
|
+
if (mode === "and") return NEVER;
|
|
18648
|
+
continue;
|
|
18649
|
+
}
|
|
18650
|
+
out.push({ ...c, items });
|
|
18651
|
+
continue;
|
|
18652
|
+
}
|
|
18653
|
+
out.push(c);
|
|
18654
|
+
}
|
|
18655
|
+
for (const [rel, leaves] of byRel) {
|
|
18656
|
+
const ids2 = await parentIds(rel, leaves);
|
|
18657
|
+
if (ids2.length === 0) return NEVER;
|
|
18658
|
+
out.push({ field: `${rel}ID`, op: "in", value: ids2 });
|
|
18659
|
+
}
|
|
18660
|
+
return out.length === 0 && mode === "or" ? NEVER : out;
|
|
18661
|
+
};
|
|
18662
|
+
const rewritten = await rewrite(conds, "and");
|
|
18663
|
+
if (rewritten === NEVER) {
|
|
18664
|
+
return { never: true, note: `no parent record matched the relation conditions \u2014 0 ${resourceKey} rows (the ${resourceKey} query was not issued)` };
|
|
18665
|
+
}
|
|
18666
|
+
return { never: false, conditions: rewritten };
|
|
18667
|
+
}
|
|
18668
|
+
|
|
18669
|
+
// src/resources/base/query.ts
|
|
18670
|
+
var QueryBase = class {
|
|
18671
|
+
http;
|
|
18672
|
+
config;
|
|
18673
|
+
/** Default page size for query walks (the client's resolved `pageSize`). */
|
|
18674
|
+
defaultPageSize;
|
|
18675
|
+
/** Versioned global URL segment (from the config), e.g. '/V1.0/Companies'. */
|
|
18676
|
+
get resourcePath() {
|
|
18677
|
+
return this.config.resourcePath;
|
|
18678
|
+
}
|
|
18679
|
+
/** The vendor entity this resource's ids identify (issue #116 — the `details.idRefs` label, e.g. `company`). */
|
|
18680
|
+
get entityName() {
|
|
18681
|
+
return entityNameFromPath(this.resourcePath);
|
|
18682
|
+
}
|
|
18683
|
+
/** Parent-scoped template (from the config); undefined for top-level resources. */
|
|
18684
|
+
get parentScopedPath() {
|
|
18685
|
+
return this.config.parentScopedPath;
|
|
18686
|
+
}
|
|
18687
|
+
/** Parent field used to narrow the global query (from the config). */
|
|
18688
|
+
get parentField() {
|
|
18689
|
+
return this.config.parentField;
|
|
18690
|
+
}
|
|
18691
|
+
/** The bounded client-scan caps (section 19.2; resolved per client). */
|
|
18692
|
+
get resolutionCaps() {
|
|
18693
|
+
return this.http.resolution;
|
|
18694
|
+
}
|
|
18695
|
+
constructor(http, config, pageSize) {
|
|
18696
|
+
if (config.key === "" || config.resourcePath === "") {
|
|
18697
|
+
throw new AutotaskConfigError("BaseResource requires a non-empty key and resourcePath");
|
|
18698
|
+
}
|
|
18699
|
+
this.http = http;
|
|
18700
|
+
this.config = config;
|
|
18701
|
+
this.defaultPageSize = clampPageSize(pageSize ?? DEFAULT_PAGE_SIZE);
|
|
18702
|
+
}
|
|
18703
|
+
// ------------------------------------------------------------------
|
|
18704
|
+
// Query walk (wired to the G3 walker; cursor-follow per the pinned wire)
|
|
18705
|
+
// ------------------------------------------------------------------
|
|
18706
|
+
/** One page fetch: page 1 POSTs the QueryModel body; later pages POST the same body (`buildQueryBody`) to the absolute cursor (issue #118). */
|
|
18707
|
+
async fetchPage(params, page, pageSize, state) {
|
|
18708
|
+
const operation = `${this.config.key}.query`;
|
|
18709
|
+
if (page === 1) {
|
|
18710
|
+
const req = buildQueryRequest(this.http.baseUrl, this.resourcePath, { ...params, maxRecords: pageSize });
|
|
18711
|
+
const body2 = await this.http.request({ ...req, operation });
|
|
18712
|
+
return this.readPage(body2, page, pageSize, state);
|
|
18713
|
+
}
|
|
18714
|
+
const cursor = state.cursor;
|
|
18715
|
+
if (cursor === null) {
|
|
18716
|
+
throw new AutotaskError(`No pagination cursor available for page ${page} of ${operation}`, {
|
|
18717
|
+
code: "INVALID_RESPONSE",
|
|
18718
|
+
category: "server",
|
|
18719
|
+
operation
|
|
18720
|
+
});
|
|
18721
|
+
}
|
|
18722
|
+
const body = buildQueryBody({ ...params, maxRecords: pageSize });
|
|
18723
|
+
const res = await this.http.request({
|
|
18724
|
+
method: "POST",
|
|
18725
|
+
path: cursorToPath(this.http.baseUrl, cursor),
|
|
18726
|
+
body,
|
|
18727
|
+
operation,
|
|
18728
|
+
idempotent: true
|
|
18729
|
+
});
|
|
18730
|
+
return this.readPage(res, page, pageSize, state);
|
|
18731
|
+
}
|
|
18732
|
+
/** @internal */
|
|
18733
|
+
readPage(body, page, pageSize, state) {
|
|
18734
|
+
const { items, pageDetails } = unwrapItems(body, `${this.config.key}.query`);
|
|
18735
|
+
state.cursor = pageDetails.nextPageUrl ?? null;
|
|
18736
|
+
const hasMore = state.cursor !== null;
|
|
18737
|
+
return {
|
|
18738
|
+
items,
|
|
18739
|
+
page,
|
|
18740
|
+
pageSize,
|
|
18741
|
+
// Verbatim vendor echo of MaxRecords — NOT a match count (live-verified).
|
|
18742
|
+
requestCount: pageDetails.requestCount,
|
|
18743
|
+
// No vendor total exists: the pages known so far (exact on the last page).
|
|
18744
|
+
totalPages: hasMore ? page + 1 : items.length === 0 ? page - 1 : page,
|
|
18745
|
+
hasMore
|
|
18746
|
+
};
|
|
18747
|
+
}
|
|
18748
|
+
/** Build the per-walk fetcher (closure keeps cursor state private to this walk). */
|
|
18749
|
+
pageFetcher(params) {
|
|
18750
|
+
const state = { cursor: null };
|
|
18751
|
+
const p = params ?? {};
|
|
18752
|
+
return (page, pageSize) => this.fetchPage(p, page, pageSize, state);
|
|
18753
|
+
}
|
|
18754
|
+
/** @internal */
|
|
18755
|
+
walkPageSize(params) {
|
|
18756
|
+
return clampPageSize(params?.maxRecords ?? this.defaultPageSize);
|
|
18757
|
+
}
|
|
18758
|
+
/** Issue #281: relation filters (`ticket.<field>` …) resolve before a walk,
|
|
18759
|
+
* through the same rewrite as queryBounded / count. `undefined` = no parent
|
|
18760
|
+
* matched, so nothing can match (the walk is skipped: zero child wire). */
|
|
18761
|
+
async relationWalkParams(params, op) {
|
|
18762
|
+
const p = params ?? {};
|
|
18763
|
+
const relation = await rewriteRelationFilters(this.http, this.config.key, normalizedConditions(p.filter), op);
|
|
18764
|
+
if (relation?.never === true) return void 0;
|
|
18765
|
+
return relation === void 0 ? p : { ...p, filter: relation.conditions };
|
|
18766
|
+
}
|
|
18767
|
+
/**
|
|
18768
|
+
* Stream items across pages (the resource `query` surface — the MCP
|
|
18769
|
+
* server's `output_path` walk). Issue #281: relation filters resolve first
|
|
18770
|
+
* (as queryBounded), and each page's rows carry the default label siblings
|
|
18771
|
+
* (issue #276, the same `labelRows`) unless `names: false`. Labelling runs
|
|
18772
|
+
* per page, so the cold name-read cap (20) applies per page; a capped id
|
|
18773
|
+
* gets a null `*Name` (an iterator has no result envelope for the `names`
|
|
18774
|
+
* summary). A resource built without a client (bare `HttpClient`) yields
|
|
18775
|
+
* raw rows.
|
|
18776
|
+
*/
|
|
18777
|
+
async *queryItems(params) {
|
|
18778
|
+
const { names, ...rest } = params ?? {};
|
|
18779
|
+
const label = readNamesOpt(names, `${this.config.key}.query`);
|
|
18780
|
+
const walk = await this.relationWalkParams(rest, `${this.config.key}.query`);
|
|
18781
|
+
if (walk === void 0) return;
|
|
18782
|
+
const client = this.http.client;
|
|
18783
|
+
if (!label || client === void 0) {
|
|
18784
|
+
yield* paginateItems(this.pageFetcher(walk), { pageSize: this.walkPageSize(walk) });
|
|
18785
|
+
return;
|
|
18786
|
+
}
|
|
18787
|
+
for await (const page of paginate(this.pageFetcher(walk), { pageSize: this.walkPageSize(walk) })) {
|
|
18788
|
+
const labelled = await labelRows(client, this.config.key, page.items);
|
|
18789
|
+
yield* labelled.rows;
|
|
18790
|
+
}
|
|
18791
|
+
}
|
|
18792
|
+
/** Stream page objects (the resource `queryPages` surface; relation filters resolve first, #281). */
|
|
18793
|
+
async *queryPageIter(params) {
|
|
18794
|
+
const walk = await this.relationWalkParams(params, `${this.config.key}.query`);
|
|
18795
|
+
if (walk === void 0) return;
|
|
18796
|
+
yield* paginate(this.pageFetcher(walk), { pageSize: this.walkPageSize(walk) });
|
|
18797
|
+
}
|
|
18798
|
+
/** Collect every item across all pages (the resource `queryAll` surface; 1000-page guarded; relation filters resolve first, #281). */
|
|
18799
|
+
async queryAllItems(params) {
|
|
18800
|
+
const walk = await this.relationWalkParams(params, `${this.config.key}.query`);
|
|
18801
|
+
if (walk === void 0) return [];
|
|
18802
|
+
return collectAll(this.pageFetcher(walk), { pageSize: this.walkPageSize(walk), maxRecords: walk.maxTotalRecords });
|
|
18803
|
+
}
|
|
18804
|
+
/**
|
|
18805
|
+
* `queryBounded` — server-side pagination (the MCP query surface): ONE call
|
|
18806
|
+
* walks the vendor cursor internally until `limit` TOTAL records are
|
|
18807
|
+
* collected or the cursor is exhausted, and returns a bounded,
|
|
18808
|
+
* complete-enough result. The client never owns the walk:
|
|
18809
|
+
* - `limit` = total records (default 500, hard max 2500 — above it throws
|
|
18810
|
+
* AutotaskConfigError; it is NOT a page size). The page-1 request uses
|
|
18811
|
+
* the internal page size min(500, limit) (the `latest` mechanism sizes
|
|
18812
|
+
* its walk from the matching count); every follow-up page POSTs the
|
|
18813
|
+
* SAME QueryModel body (`buildQueryBody`: MaxRecords, IncludeFields and
|
|
18814
|
+
* the preserved filter) to the absolute cursor (issue #118 — the vendor
|
|
18815
|
+
* honours IncludeFields there and ignores MaxRecords, serving its own
|
|
18816
|
+
* page size), so the walk reuses `fetchPage` (the shared cursor machinery)
|
|
18817
|
+
* with a per-walk closure state (walks never share state).
|
|
18818
|
+
* - The 1000-page guard still applies as a SOFT stop: a walk that would
|
|
18819
|
+
* fetch a 1001st page stops with `hasMore: true` (never a throw — this
|
|
18820
|
+
* is a bounded read, not the `paginate` generator's error path).
|
|
18821
|
+
* - `hasMore` = (truncated by limit OR guard) AND (a non-null next cursor
|
|
18822
|
+
* was seen at the stop point OR the final page was sliced to meet the
|
|
18823
|
+
* limit). Cursor exhausted, or a short/empty page with no truncation →
|
|
18824
|
+
* false (never claims more than the cursor + the slice say exist).
|
|
18825
|
+
* - `count` is always omitted: `pageDetails.count` is the current page's
|
|
18826
|
+
* record count (vendor-documented), never a total — `<resource>.count`
|
|
18827
|
+
* is the total.
|
|
18828
|
+
* - Issue #59: `latest: N` (validated pre-request, zero wire) delegates to
|
|
18829
|
+
* `latestWalk` — the count-anchored N-highest-id mechanism; ABSENT, the
|
|
18830
|
+
* walk below is bit-for-bit the existing path.
|
|
18831
|
+
* Pure read; no mutation paths involved.
|
|
18832
|
+
*/
|
|
18833
|
+
async queryBounded(params) {
|
|
18834
|
+
if (Array.isArray(params)) {
|
|
18835
|
+
throw new AutotaskConfigError(
|
|
18836
|
+
"queryBounded: params must be an object { filter?, limit?, latest?, includeFields? } \u2014 a bare conditions array is not accepted (wrap it: { filter: [...] })"
|
|
18837
|
+
);
|
|
18838
|
+
}
|
|
18839
|
+
const limit = params?.limit ?? BOUNDED_QUERY_DEFAULT_LIMIT;
|
|
18840
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > BOUNDED_QUERY_MAX_LIMIT) {
|
|
18841
|
+
throw new AutotaskConfigError(
|
|
18842
|
+
`queryBounded: limit must be an integer 1..${BOUNDED_QUERY_MAX_LIMIT} (got ${String(limit)}) \u2014 it is the TOTAL record cap, not a page size`
|
|
18843
|
+
);
|
|
18844
|
+
}
|
|
18845
|
+
const noteChars = params?.noteChars;
|
|
18846
|
+
if (noteChars !== void 0 && (!Number.isInteger(noteChars) || noteChars < 1)) {
|
|
18847
|
+
throw new AutotaskConfigError(
|
|
18848
|
+
`queryBounded: noteChars must be an integer >= 1 (got ${String(noteChars)}) \u2014 it is the per-field char cap on the untrusted-class free-text fields (absent = the raw, untruncated result)`
|
|
18849
|
+
);
|
|
18850
|
+
}
|
|
18851
|
+
let walkParams = params?.fieldsPreset !== void 0 ? { ...params, includeFields: resolveFieldsPreset(params.fieldsPreset, this.config.key, params.includeFields) } : params ?? {};
|
|
18852
|
+
const latest = walkParams.latest;
|
|
18853
|
+
if (latest !== void 0) {
|
|
18854
|
+
if (!Number.isInteger(latest) || latest < 1 || latest > BOUNDED_QUERY_MAX_LIMIT) {
|
|
18855
|
+
throw new AutotaskConfigError(
|
|
18856
|
+
`queryBounded: latest must be an integer 1..${BOUNDED_QUERY_MAX_LIMIT} (got ${String(latest)}) \u2014 it is the count of the highest-id rows to return`
|
|
18857
|
+
);
|
|
18858
|
+
}
|
|
18859
|
+
if (params?.limit !== void 0 && limit < latest) {
|
|
18860
|
+
throw new AutotaskConfigError(
|
|
18861
|
+
`queryBounded: limit (${String(limit)}) must be >= latest (${String(latest)}) \u2014 latest governs the returned count and limit is the walk window`
|
|
18862
|
+
);
|
|
18863
|
+
}
|
|
18864
|
+
}
|
|
18865
|
+
const relation = await rewriteRelationFilters(this.http, this.config.key, normalizedConditions(walkParams.filter), `${this.config.key}.query`);
|
|
18866
|
+
if (relation?.never === true) return { items: [], fetched: 0, hasMore: false, pagesWalked: 0, relationNote: relation.note };
|
|
18867
|
+
if (relation !== void 0) walkParams = { ...walkParams, filter: relation.conditions };
|
|
18868
|
+
if (latest !== void 0) {
|
|
18869
|
+
return this.applyNoteChars(await this.latestWalk(walkParams, Math.max(limit, latest), latest), noteChars);
|
|
18870
|
+
}
|
|
18871
|
+
const { items, pages, guard, sliced, cursor } = await this.walkBounded(walkParams, limit);
|
|
18872
|
+
const hasMore = (items.length >= limit || guard) && (sliced > 0 || cursor !== null);
|
|
18873
|
+
const result = { items, fetched: items.length, hasMore, pagesWalked: pages };
|
|
18874
|
+
this.stampEmptyQueryResult(result, params);
|
|
18875
|
+
return this.applyNoteChars(result, noteChars);
|
|
18876
|
+
}
|
|
18877
|
+
/** Issue #61 (slice A): the noteChars truncation at the SINGLE chokepoint
|
|
18878
|
+
* of `queryBounded` — the normal walk and the `latest` paths both
|
|
18879
|
+
* produced their `BoundedQueryResult` here (the single chokepoint the
|
|
18880
|
+
* plan pins: R5). Copy-on-truncate (the input records are never
|
|
18881
|
+
* mutated); the additive `noteCharsTruncated` stamp = the truncated-
|
|
18882
|
+
* field count, ABSENT when 0. The resource's untrusted-class field set
|
|
18883
|
+
* is the generated stamp (the SAME plan column the registry's
|
|
18884
|
+
* `metadata.untrustedFields` channel stamps — zero second source of
|
|
18885
|
+
* truth); an absent set = the cap is a no-op for the resource (bit-for-
|
|
18886
|
+
* bit the pre-slice result).
|
|
18887
|
+
* @internal */
|
|
18888
|
+
applyNoteChars(result, noteChars) {
|
|
18889
|
+
if (noteChars === void 0) return result;
|
|
18890
|
+
const fields = UNTRUSTED_FIELDS_BY_RESOURCE[this.config.key];
|
|
18891
|
+
if (fields === void 0 || fields.length === 0) return result;
|
|
18892
|
+
const [items, truncated] = truncateNoteChars(result.items, noteChars, fields);
|
|
18893
|
+
if (truncated > 0) {
|
|
18894
|
+
return { ...result, items, noteCharsTruncated: truncated };
|
|
18895
|
+
}
|
|
18896
|
+
return result;
|
|
18897
|
+
}
|
|
18898
|
+
/** The bounded walk loop (shared by the normal `queryBounded` path and the
|
|
18899
|
+
* `latest` modes): walks the vendor cursor until `limit` TOTAL records
|
|
18900
|
+
* are collected or the cursor is exhausted; the 1000-page guard is a
|
|
18901
|
+
* SOFT stop (never a throw). ONE per-walk closure (walks never share
|
|
18902
|
+
* state).
|
|
18903
|
+
* @internal */
|
|
18904
|
+
async walkBounded(p, limit) {
|
|
18905
|
+
const state = { cursor: null };
|
|
18906
|
+
const items = [];
|
|
18907
|
+
let pages = 0;
|
|
18908
|
+
let sliced = 0;
|
|
18909
|
+
let guard = false;
|
|
18910
|
+
const pageSize = Math.min(MAX_PAGE_SIZE, limit);
|
|
18911
|
+
for (; ; ) {
|
|
18912
|
+
if (pages >= MAX_PAGES) {
|
|
18913
|
+
guard = true;
|
|
18914
|
+
break;
|
|
18915
|
+
}
|
|
18916
|
+
const page = await this.fetchPage(p, pages + 1, pageSize, state);
|
|
18917
|
+
pages += 1;
|
|
18918
|
+
const remaining = limit - items.length;
|
|
18919
|
+
const take = page.items.slice(0, remaining);
|
|
18920
|
+
sliced += Math.max(0, page.items.length - remaining);
|
|
18921
|
+
items.push(...take);
|
|
18922
|
+
if (items.length >= limit) break;
|
|
18923
|
+
if (!page.hasMore || page.items.length === 0) break;
|
|
18924
|
+
}
|
|
18925
|
+
return { items, pages, guard, sliced, cursor: state.cursor };
|
|
18926
|
+
}
|
|
18927
|
+
/** A walk stop is TRUNCATED when the 1000-page guard fired, or the limit
|
|
18928
|
+
* was met with a live next cursor (or a sliced final page): rows exist
|
|
18929
|
+
* past the stop point, and a top-N-by-id over the collected set alone is
|
|
18930
|
+
* unprovable (the `latest` mechanism refuses instead of guessing).
|
|
18931
|
+
* @internal */
|
|
18932
|
+
walkTruncated(limit, stop) {
|
|
18933
|
+
return stop.guard || stop.itemsLength >= limit && (stop.sliced > 0 || stop.cursor !== null);
|
|
18934
|
+
}
|
|
18935
|
+
/** The 0-item result stamping — the ONE source of truth (the normal walk
|
|
18936
|
+
* and the `latest` empty outcome both ride it): the typed blind-surface
|
|
18937
|
+
* sentinel + the pinned empty-reason, through the filter-aware predicate
|
|
18055
18938
|
* `blindStampFor` (`unfiltered-blind` surfaces stamp ONLY the unfiltered
|
|
18056
18939
|
* call — a filtered empty result is a true absence; live-verified
|
|
18057
18940
|
* S12-B P1: the filtered tickets.query returns data on ww29). Additive:
|
|
@@ -18335,7 +19218,9 @@ var QueryBase = class {
|
|
|
18335
19218
|
* or, on a plan-pinned blind surface, the `{ count: 0, blindSurface }`
|
|
18336
19219
|
* typed sentinel (S12-C T10; see `BlindCountResult`). */
|
|
18337
19220
|
async countGlobal(params) {
|
|
18338
|
-
const
|
|
19221
|
+
const relation = await rewriteRelationFilters(this.http, this.config.key, normalizedConditions(params?.filter), `${this.config.key}.count`);
|
|
19222
|
+
if (relation?.never === true) return 0;
|
|
19223
|
+
const conditions = relation !== void 0 ? relation.conditions : normalizedConditions(params?.filter);
|
|
18339
19224
|
const body = {};
|
|
18340
19225
|
body.filter = conditions === void 0 ? [] : buildFilter(conditions).filter;
|
|
18341
19226
|
const res = await this.http.request({
|
|
@@ -19385,1453 +20270,1075 @@ var MutationBase = class extends MetaBase {
|
|
|
19385
20270
|
* `lastModifiedDate` and the model carries the field → fetch the current
|
|
19386
20271
|
* record and compare before the mutating request (mismatch throws
|
|
19387
20272
|
* STALE_OBJECT; a vanished record propagates the vendor's NotFoundError).
|
|
19388
|
-
*/
|
|
19389
|
-
async guardStaleBeforeMutation(id, opts, operation, parentId) {
|
|
19390
|
-
if (opts.lastModifiedDate === void 0 || this.config.staleField === void 0) return;
|
|
19391
|
-
let current;
|
|
19392
|
-
try {
|
|
19393
|
-
current = parentId === void 0 ? await this.getGlobal(id, true) : this.config.attachmentGet ? await this.getScopedModel(parentId, id, true) : await this.getScopedRaw(parentId, id, true);
|
|
19394
|
-
} catch (err) {
|
|
19395
|
-
if (err instanceof NotFoundError) attachUpdateTargetMissReason(err, this.config.key);
|
|
19396
|
-
throw err;
|
|
19397
|
-
}
|
|
19398
|
-
this.staleObjectCheck(current, opts, operation);
|
|
19399
|
-
}
|
|
19400
|
-
/**
|
|
19401
|
-
* `create` dry-run: input validation + parent verification (scoped
|
|
19402
|
-
* resources) + the pinned envelope. NO request is issued (scoped parent
|
|
19403
|
-
* verification is a read). Duplicate detection has no reliable lookup for
|
|
19404
|
-
* a plain create (that is `createIfNotExists`), so it is not claimed.
|
|
19405
|
-
*/
|
|
19406
|
-
async runCreateDryRun(data, parentId) {
|
|
19407
|
-
const checks = [];
|
|
19408
|
-
const warnings = [];
|
|
19409
|
-
checks.push({ name: "input-validation", ok: isRecord2(data) && Object.keys(data).length > 0 });
|
|
19410
|
-
const deleteBoundaryNote = DELETE_BOUNDARY_NOTES[this.config.key];
|
|
19411
|
-
if (deleteBoundaryNote !== void 0) {
|
|
19412
|
-
checks.push({ name: "delete-boundary", ok: true });
|
|
19413
|
-
warnings.push(deleteBoundaryNote);
|
|
19414
|
-
}
|
|
19415
|
-
let path = this.resourcePath;
|
|
19416
|
-
if (parentId !== void 0) {
|
|
19417
|
-
path = this.scopedCollectionPath(parentId);
|
|
19418
|
-
const parentPath = this.config.parentPath;
|
|
19419
|
-
if (parentPath === void 0) {
|
|
19420
|
-
throw new AutotaskConfigError(
|
|
19421
|
-
`dry-run: ${this.config.key} is parent-scoped but has no parentPath configured`
|
|
19422
|
-
);
|
|
19423
|
-
}
|
|
19424
|
-
let parent = null;
|
|
19425
|
-
if (typeof parentId === "number" && Number.isInteger(parentId) && parentId >= 0) {
|
|
19426
|
-
try {
|
|
19427
|
-
parent = await this.fetchRecordRaw(parentPath, parentId, true);
|
|
19428
|
-
} catch (err) {
|
|
19429
|
-
if (!(err instanceof NotFoundError)) throw err;
|
|
19430
|
-
}
|
|
19431
|
-
}
|
|
19432
|
-
checks.push({ name: "parent-exists", ok: parent !== null });
|
|
19433
|
-
if (parent === null) {
|
|
19434
|
-
warnings.push(`parent ${parentId} not found; the vendor would reject the create`);
|
|
19435
|
-
}
|
|
19436
|
-
}
|
|
19437
|
-
if (isRecord2(data)) {
|
|
19438
|
-
const eff = effectiveRequiredFields(void 0, RELATIONSHIP_MAP[this.config.key]?.zoneFacts ?? null);
|
|
19439
|
-
const missing = eff.fields.filter((f) => f !== this.config.parentField && data[f] === void 0);
|
|
19440
|
-
if (missing.length > 0) {
|
|
19441
|
-
warnings.push(`required field(s) not in the data: ${missing.join(", ")} (${eff.from}); the vendor would likely reject the create`);
|
|
19442
|
-
}
|
|
19443
|
-
}
|
|
19444
|
-
if (this.config.key === "companies") {
|
|
19445
|
-
warnings.push('the vendor auto-creates a "Primary Address" location for a new company (rename it with company_locations.update; it cannot be deleted via the API)');
|
|
19446
|
-
}
|
|
19447
|
-
return buildDryRunResult({
|
|
19448
|
-
operation: `${this.config.key}.create`,
|
|
19449
|
-
method: "POST",
|
|
19450
|
-
path,
|
|
19451
|
-
ids: [],
|
|
19452
|
-
checks,
|
|
19453
|
-
impact: { affected: 1, scope: "single-record", reversible: true },
|
|
19454
|
-
warnings,
|
|
19455
|
-
data
|
|
19456
|
-
}, { redactPersonalData: this.http.redactPersonalData });
|
|
19457
|
-
}
|
|
19458
|
-
/**
|
|
19459
|
-
* `update` dry-run: input validation + current-record fetch (a missing
|
|
19460
|
-
* record carries `warnings` and no `diff` — pinned), the stale-object
|
|
19461
|
-
* check (mismatch throws STALE_OBJECT) and the best-effort `diff`. NO
|
|
19462
|
-
* mutating request is issued.
|
|
19463
|
-
*/
|
|
19464
|
-
async runUpdateDryRun(id, data, opts = {}, parentId) {
|
|
19465
|
-
const operation = `${this.config.key}.update`;
|
|
19466
|
-
const checks = [];
|
|
19467
|
-
const warnings = [];
|
|
19468
|
-
checks.push({ name: "input-validation", ok: isRecord2(data) && Object.keys(data).length > 0 });
|
|
19469
|
-
const scopedUpdate = parentId !== void 0 && this.config.parentScopedPath !== void 0;
|
|
19470
|
-
const collectionUpdate = scopedUpdate || this.config.collectionUpdate === true;
|
|
19471
|
-
const path = scopedUpdate ? this.scopedCollectionPath(parentId) : collectionUpdate ? this.resourcePath : `${this.resourcePath}/${id}`;
|
|
19472
|
-
const method = scopedUpdate ? "PATCH" : "PUT";
|
|
19473
|
-
let current = null;
|
|
19474
|
-
try {
|
|
19475
|
-
current = parentId === void 0 ? await this.getGlobal(id, true) : this.config.attachmentGet ? await this.getScopedModel(parentId, id, true) : await this.getScopedRaw(parentId, id, true);
|
|
19476
|
-
} catch (err) {
|
|
19477
|
-
if (err instanceof NotFoundError) {
|
|
19478
|
-
const updateReason = EMPTY_REASON_STAMPS[this.config.key]?.update;
|
|
19479
|
-
checks.push({
|
|
19480
|
-
name: "current-record-exists",
|
|
19481
|
-
ok: false,
|
|
19482
|
-
...updateReason !== void 0 ? { note: updateReason.note } : {}
|
|
19483
|
-
});
|
|
19484
|
-
warnings.push(`current record ${id} not fetchable; no diff`);
|
|
19485
|
-
return buildDryRunResult({
|
|
19486
|
-
operation,
|
|
19487
|
-
method,
|
|
19488
|
-
path,
|
|
19489
|
-
ids: [id],
|
|
19490
|
-
checks,
|
|
19491
|
-
impact: { affected: 1, scope: "single-record", reversible: true },
|
|
19492
|
-
warnings,
|
|
19493
|
-
data,
|
|
19494
|
-
...updateReason !== void 0 ? { emptyReason: updateReason } : {}
|
|
19495
|
-
}, { redactPersonalData: this.http.redactPersonalData });
|
|
19496
|
-
}
|
|
19497
|
-
throw err;
|
|
19498
|
-
}
|
|
19499
|
-
checks.push({ name: "current-record-exists", ok: true });
|
|
19500
|
-
if (this.staleObjectCheck(current, opts, operation)) {
|
|
19501
|
-
checks.push({ name: "stale-check", ok: true });
|
|
19502
|
-
}
|
|
19503
|
-
let envelopeData = data;
|
|
19504
|
-
if (parentId === void 0 && this.config.collectionUpdate === true && opts.raw !== true) {
|
|
19505
|
-
envelopeData = mergePartialBody(
|
|
19506
|
-
current,
|
|
19507
|
-
this.normalizeMutationBody(data)
|
|
19508
|
-
);
|
|
19509
|
-
}
|
|
19510
|
-
return buildDryRunResult({
|
|
19511
|
-
operation,
|
|
19512
|
-
method,
|
|
19513
|
-
path,
|
|
19514
|
-
ids: [id],
|
|
19515
|
-
checks,
|
|
19516
|
-
diff: computeFieldDiff(current, data),
|
|
19517
|
-
impact: { affected: 1, scope: "single-record", reversible: true },
|
|
19518
|
-
warnings,
|
|
19519
|
-
data: envelopeData
|
|
19520
|
-
}, { redactPersonalData: this.http.redactPersonalData });
|
|
19521
|
-
}
|
|
19522
|
-
/**
|
|
19523
|
-
* `delete` dry-run: current-record verification + the stale-object check +
|
|
19524
|
-
* the pinned envelope (no diff — a delete has no before/after fields). NO
|
|
19525
|
-
* mutating request is issued.
|
|
19526
|
-
*/
|
|
19527
|
-
async runDeleteDryRun(id, opts = {}, parentId) {
|
|
19528
|
-
const operation = `${this.config.key}.delete`;
|
|
19529
|
-
const checks = [];
|
|
19530
|
-
const warnings = [];
|
|
19531
|
-
const path = parentId === void 0 ? `${this.resourcePath}/${id}` : this.scopedItemPath(parentId, id);
|
|
19532
|
-
let current = null;
|
|
20273
|
+
*/
|
|
20274
|
+
async guardStaleBeforeMutation(id, opts, operation, parentId) {
|
|
20275
|
+
if (opts.lastModifiedDate === void 0 || this.config.staleField === void 0) return;
|
|
20276
|
+
let current;
|
|
19533
20277
|
try {
|
|
19534
20278
|
current = parentId === void 0 ? await this.getGlobal(id, true) : this.config.attachmentGet ? await this.getScopedModel(parentId, id, true) : await this.getScopedRaw(parentId, id, true);
|
|
19535
20279
|
} catch (err) {
|
|
19536
|
-
if (err instanceof NotFoundError)
|
|
19537
|
-
checks.push({ name: "current-record-exists", ok: false });
|
|
19538
|
-
warnings.push(`record ${id} not fetchable; the vendor would reject the delete`);
|
|
19539
|
-
return buildDryRunResult({
|
|
19540
|
-
operation,
|
|
19541
|
-
method: "DELETE",
|
|
19542
|
-
path,
|
|
19543
|
-
ids: [id],
|
|
19544
|
-
checks,
|
|
19545
|
-
impact: { affected: 1, scope: "single-record", reversible: false },
|
|
19546
|
-
warnings
|
|
19547
|
-
}, { redactPersonalData: this.http.redactPersonalData });
|
|
19548
|
-
}
|
|
20280
|
+
if (err instanceof NotFoundError) attachUpdateTargetMissReason(err, this.config.key);
|
|
19549
20281
|
throw err;
|
|
19550
20282
|
}
|
|
19551
|
-
|
|
19552
|
-
if (this.staleObjectCheck(current, opts, operation)) {
|
|
19553
|
-
checks.push({ name: "stale-check", ok: true });
|
|
19554
|
-
}
|
|
19555
|
-
return buildDryRunResult({
|
|
19556
|
-
operation,
|
|
19557
|
-
method: "DELETE",
|
|
19558
|
-
path,
|
|
19559
|
-
ids: [id],
|
|
19560
|
-
checks,
|
|
19561
|
-
impact: { affected: 1, scope: "single-record", reversible: false },
|
|
19562
|
-
warnings
|
|
19563
|
-
}, { redactPersonalData: this.http.redactPersonalData });
|
|
19564
|
-
}
|
|
19565
|
-
};
|
|
19566
|
-
|
|
19567
|
-
// src/resources/base/BaseResource.ts
|
|
19568
|
-
var BaseResource = class extends MutationBase {
|
|
19569
|
-
};
|
|
19570
|
-
|
|
19571
|
-
// src/resources/billing_codes.ts
|
|
19572
|
-
var BillingCodesResource = class extends BaseResource {
|
|
19573
|
-
constructor(http, pageSize) {
|
|
19574
|
-
super(http, {
|
|
19575
|
-
key: "billing_codes",
|
|
19576
|
-
resourcePath: "/V1.0/BillingCodes",
|
|
19577
|
-
deletable: false,
|
|
19578
|
-
udfCapable: false,
|
|
19579
|
-
searchFields: ["name"]
|
|
19580
|
-
}, pageSize);
|
|
19581
|
-
}
|
|
19582
|
-
/** Query BillingCode records with the G3 filter surface (server-side JSON filter). */
|
|
19583
|
-
query(params) {
|
|
19584
|
-
return this.queryItems(params);
|
|
19585
|
-
}
|
|
19586
|
-
/** Get every BillingCode record (the MCP-preferred read; 1000-page guarded). */
|
|
19587
|
-
async queryAll(params) {
|
|
19588
|
-
return this.queryAllItems(params);
|
|
19589
|
-
}
|
|
19590
|
-
/** Count matching records (queryCount envelope unwrapped to a plain number). */
|
|
19591
|
-
async count(params) {
|
|
19592
|
-
return this.countGlobal(params);
|
|
19593
|
-
}
|
|
19594
|
-
/** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
|
|
19595
|
-
async get(id, opts = {}) {
|
|
19596
|
-
return this.getGlobal(id, opts?.fresh === true);
|
|
19597
|
-
}
|
|
19598
|
-
/** Resolve a BillingCode from a numeric id (or, where the vendor indexes a primary text field,
|
|
19599
|
-
* from an exact match on name). A numeric id miss throws NOT_FOUND; a complete
|
|
19600
|
-
* non-match returns null; ambiguity throws RESOLUTION_AMBIGUOUS (candidate ids) and a
|
|
19601
|
-
* cap-tripped bounded client scan throws RESOLUTION_TRUNCATED (never a silent null).
|
|
19602
|
-
* The compact shape is the structural alias of the full record; `expand: true` is the
|
|
19603
|
-
* type-level escape hatch. */
|
|
19604
|
-
async resolve(identifier, opts = {}) {
|
|
19605
|
-
void opts;
|
|
19606
|
-
return this.resolveBase(identifier);
|
|
19607
|
-
}
|
|
19608
|
-
/** Search BillingCode records: bounded contains on name (no vendor free-text search exists);
|
|
19609
|
-
* limit defaults to 25, max 500. */
|
|
19610
|
-
async search(query, opts) {
|
|
19611
|
-
return this.searchBase(query, { limit: opts?.limit });
|
|
19612
|
-
}
|
|
19613
|
-
};
|
|
19614
|
-
|
|
19615
|
-
// src/resources/special/dateWindow.ts
|
|
19616
|
-
var DAY_MS = 864e5;
|
|
19617
|
-
var DEFAULT_LIMIT = 500;
|
|
19618
|
-
var MAX_LIMIT = 500;
|
|
19619
|
-
function clampWindowLimit(limit) {
|
|
19620
|
-
const n = limit === void 0 ? DEFAULT_LIMIT : Math.trunc(limit);
|
|
19621
|
-
if (!Number.isInteger(n) || n < 1) {
|
|
19622
|
-
throw new AutotaskConfigError(`limit must be an integer >= 1 (got ${String(limit)})`);
|
|
19623
|
-
}
|
|
19624
|
-
return Math.min(n, MAX_LIMIT);
|
|
19625
|
-
}
|
|
19626
|
-
var iso = (ms) => new Date(ms).toISOString().slice(0, 19) + "Z";
|
|
19627
|
-
var WEEKDAYS = { Sun: 0, Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6 };
|
|
19628
|
-
function wallParts(tz, ms) {
|
|
19629
|
-
const parts = new Intl.DateTimeFormat("en-US", {
|
|
19630
|
-
timeZone: tz,
|
|
19631
|
-
year: "numeric",
|
|
19632
|
-
month: "2-digit",
|
|
19633
|
-
day: "2-digit",
|
|
19634
|
-
hour: "2-digit",
|
|
19635
|
-
minute: "2-digit",
|
|
19636
|
-
second: "2-digit",
|
|
19637
|
-
hour12: false,
|
|
19638
|
-
weekday: "short"
|
|
19639
|
-
}).formatToParts(new Date(ms));
|
|
19640
|
-
const w = {};
|
|
19641
|
-
let weekday = -1;
|
|
19642
|
-
for (const p of parts) {
|
|
19643
|
-
switch (p.type) {
|
|
19644
|
-
case "year":
|
|
19645
|
-
w.y = Number(p.value);
|
|
19646
|
-
break;
|
|
19647
|
-
case "month":
|
|
19648
|
-
w.m = Number(p.value);
|
|
19649
|
-
break;
|
|
19650
|
-
case "day":
|
|
19651
|
-
w.d = Number(p.value);
|
|
19652
|
-
break;
|
|
19653
|
-
case "hour":
|
|
19654
|
-
w.h = Number(p.value) % 24;
|
|
19655
|
-
break;
|
|
19656
|
-
case "minute":
|
|
19657
|
-
w.mi = Number(p.value);
|
|
19658
|
-
break;
|
|
19659
|
-
case "second":
|
|
19660
|
-
w.s = Number(p.value);
|
|
19661
|
-
break;
|
|
19662
|
-
case "weekday":
|
|
19663
|
-
weekday = WEEKDAYS[p.value] ?? -1;
|
|
19664
|
-
break;
|
|
19665
|
-
}
|
|
19666
|
-
}
|
|
19667
|
-
if (weekday < 0 || [w.y, w.m, w.d, w.h, w.mi, w.s].some((v) => v === void 0 || Number.isNaN(v))) {
|
|
19668
|
-
throw new AutotaskConfigError(`dateWindow: could not read the wall-clock parts for timezone "${tz}" (the Intl format returned an incomplete or unmapped part)`);
|
|
19669
|
-
}
|
|
19670
|
-
return { y: w.y, m: w.m, d: w.d, h: w.h, mi: w.mi, s: w.s, weekday };
|
|
19671
|
-
}
|
|
19672
|
-
function tzOffsetMs(tz, ms) {
|
|
19673
|
-
const p = wallParts(tz, ms);
|
|
19674
|
-
return Date.UTC(p.y, p.m - 1, p.d, p.h, p.mi, p.s) - ms;
|
|
19675
|
-
}
|
|
19676
|
-
function localDayStartMs(y, m, d, tz) {
|
|
19677
|
-
if (tz === void 0) return Date.UTC(y, m - 1, d);
|
|
19678
|
-
const candidate2 = Date.UTC(y, m - 1, d);
|
|
19679
|
-
const offset = tzOffsetMs(tz, candidate2);
|
|
19680
|
-
let start = candidate2 - offset;
|
|
19681
|
-
if (tzOffsetMs(tz, start) !== offset) {
|
|
19682
|
-
start = candidate2 - tzOffsetMs(tz, start);
|
|
19683
|
-
}
|
|
19684
|
-
const p = wallParts(tz, start);
|
|
19685
|
-
if (p.d !== d) start += 864e5 - ((p.h * 60 + p.mi) * 60 + p.s) * 1e3;
|
|
19686
|
-
return start;
|
|
19687
|
-
}
|
|
19688
|
-
function localDayEndMs(y, m, d, tz) {
|
|
19689
|
-
if (tz === void 0) return Date.UTC(y, m - 1, d) + DAY_MS - 1;
|
|
19690
|
-
const next = new Date(Date.UTC(y, m - 1, d + 1));
|
|
19691
|
-
return localDayStartMs(next.getUTCFullYear(), next.getUTCMonth() + 1, next.getUTCDate(), tz) - 1;
|
|
19692
|
-
}
|
|
19693
|
-
function validateZone(tz, opName = "dateWindow") {
|
|
19694
|
-
if (typeof tz !== "string" || tz.length === 0) {
|
|
19695
|
-
throw new AutotaskConfigError(
|
|
19696
|
-
`${opName}: tz must be a non-empty IANA timezone string (got ${JSON.stringify(tz)}; e.g. 'Australia/Sydney' \u2014 omitted tz = UTC)`,
|
|
19697
|
-
{ operation: opName }
|
|
19698
|
-
);
|
|
19699
|
-
}
|
|
19700
|
-
try {
|
|
19701
|
-
new Intl.DateTimeFormat("en-US", { timeZone: tz });
|
|
19702
|
-
} catch {
|
|
19703
|
-
throw new AutotaskConfigError(
|
|
19704
|
-
`${opName}: tz is not a valid IANA timezone (${JSON.stringify(tz)}; e.g. 'Australia/Sydney' \u2014 omitted tz = UTC)`,
|
|
19705
|
-
{ operation: opName }
|
|
19706
|
-
);
|
|
20283
|
+
this.staleObjectCheck(current, opts, operation);
|
|
19707
20284
|
}
|
|
19708
|
-
|
|
19709
|
-
|
|
19710
|
-
|
|
19711
|
-
|
|
19712
|
-
|
|
19713
|
-
|
|
19714
|
-
|
|
19715
|
-
|
|
19716
|
-
|
|
19717
|
-
|
|
19718
|
-
|
|
19719
|
-
|
|
19720
|
-
|
|
19721
|
-
|
|
19722
|
-
to = utcDayEnd(t);
|
|
19723
|
-
break;
|
|
19724
|
-
case "yesterday":
|
|
19725
|
-
from = utcDayStart(t - DAY_MS);
|
|
19726
|
-
to = utcDayEnd(t - DAY_MS);
|
|
19727
|
-
break;
|
|
19728
|
-
case "last24h":
|
|
19729
|
-
from = t - DAY_MS;
|
|
19730
|
-
to = t;
|
|
19731
|
-
break;
|
|
19732
|
-
case "last7":
|
|
19733
|
-
case "last14":
|
|
19734
|
-
case "last30":
|
|
19735
|
-
case "last90": {
|
|
19736
|
-
const days = Number(kind.slice(4));
|
|
19737
|
-
from = utcDayStart(t - days * DAY_MS);
|
|
19738
|
-
to = utcDayEnd(t);
|
|
19739
|
-
break;
|
|
19740
|
-
}
|
|
19741
|
-
case "lastFullWeek":
|
|
19742
|
-
case "lastFullFortnight": {
|
|
19743
|
-
const d = new Date(t);
|
|
19744
|
-
const daysSinceMonday = (d.getUTCDay() + 6) % 7;
|
|
19745
|
-
const thisMonday = utcDayStart(t) - daysSinceMonday * DAY_MS;
|
|
19746
|
-
const daysBack = kind === "lastFullWeek" ? 7 : 14;
|
|
19747
|
-
from = thisMonday - daysBack * DAY_MS;
|
|
19748
|
-
to = thisMonday - 1;
|
|
19749
|
-
break;
|
|
20285
|
+
/**
|
|
20286
|
+
* `create` dry-run: input validation + parent verification (scoped
|
|
20287
|
+
* resources) + the pinned envelope. NO request is issued (scoped parent
|
|
20288
|
+
* verification is a read). Duplicate detection has no reliable lookup for
|
|
20289
|
+
* a plain create (that is `createIfNotExists`), so it is not claimed.
|
|
20290
|
+
*/
|
|
20291
|
+
async runCreateDryRun(data, parentId) {
|
|
20292
|
+
const checks = [];
|
|
20293
|
+
const warnings = [];
|
|
20294
|
+
checks.push({ name: "input-validation", ok: isRecord2(data) && Object.keys(data).length > 0 });
|
|
20295
|
+
const deleteBoundaryNote = DELETE_BOUNDARY_NOTES[this.config.key];
|
|
20296
|
+
if (deleteBoundaryNote !== void 0) {
|
|
20297
|
+
checks.push({ name: "delete-boundary", ok: true });
|
|
20298
|
+
warnings.push(deleteBoundaryNote);
|
|
19750
20299
|
}
|
|
19751
|
-
|
|
19752
|
-
|
|
19753
|
-
|
|
19754
|
-
const
|
|
19755
|
-
|
|
19756
|
-
|
|
19757
|
-
|
|
20300
|
+
let path = this.resourcePath;
|
|
20301
|
+
if (parentId !== void 0) {
|
|
20302
|
+
path = this.scopedCollectionPath(parentId);
|
|
20303
|
+
const parentPath = this.config.parentPath;
|
|
20304
|
+
if (parentPath === void 0) {
|
|
20305
|
+
throw new AutotaskConfigError(
|
|
20306
|
+
`dry-run: ${this.config.key} is parent-scoped but has no parentPath configured`
|
|
20307
|
+
);
|
|
20308
|
+
}
|
|
20309
|
+
let parent = null;
|
|
20310
|
+
if (typeof parentId === "number" && Number.isInteger(parentId) && parentId >= 0) {
|
|
20311
|
+
try {
|
|
20312
|
+
parent = await this.fetchRecordRaw(parentPath, parentId, true);
|
|
20313
|
+
} catch (err) {
|
|
20314
|
+
if (!(err instanceof NotFoundError)) throw err;
|
|
20315
|
+
}
|
|
20316
|
+
}
|
|
20317
|
+
checks.push({ name: "parent-exists", ok: parent !== null });
|
|
20318
|
+
if (parent === null) {
|
|
20319
|
+
warnings.push(`parent ${parentId} not found; the vendor would reject the create`);
|
|
20320
|
+
}
|
|
19758
20321
|
}
|
|
19759
|
-
|
|
19760
|
-
const
|
|
19761
|
-
|
|
19762
|
-
|
|
19763
|
-
|
|
19764
|
-
q = 3;
|
|
19765
|
-
y -= 1;
|
|
20322
|
+
if (isRecord2(data)) {
|
|
20323
|
+
const eff = effectiveRequiredFields(void 0, RELATIONSHIP_MAP[this.config.key]?.zoneFacts ?? null);
|
|
20324
|
+
const missing = eff.fields.filter((f) => f !== this.config.parentField && data[f] === void 0);
|
|
20325
|
+
if (missing.length > 0) {
|
|
20326
|
+
warnings.push(`required field(s) not in the data: ${missing.join(", ")} (${eff.from}); the vendor would likely reject the create`);
|
|
19766
20327
|
}
|
|
19767
|
-
const first = q * 3;
|
|
19768
|
-
from = Date.UTC(y, first, 1);
|
|
19769
|
-
to = Date.UTC(y, first + 3, 1) - 1;
|
|
19770
|
-
break;
|
|
19771
20328
|
}
|
|
20329
|
+
if (this.config.key === "companies") {
|
|
20330
|
+
warnings.push('the vendor auto-creates a "Primary Address" location for a new company (rename it with company_locations.update; it cannot be deleted via the API)');
|
|
20331
|
+
}
|
|
20332
|
+
return buildDryRunResult({
|
|
20333
|
+
operation: `${this.config.key}.create`,
|
|
20334
|
+
method: "POST",
|
|
20335
|
+
path,
|
|
20336
|
+
ids: [],
|
|
20337
|
+
checks,
|
|
20338
|
+
impact: { affected: 1, scope: "single-record", reversible: true },
|
|
20339
|
+
warnings,
|
|
20340
|
+
data
|
|
20341
|
+
}, { redactPersonalData: this.http.redactPersonalData });
|
|
19772
20342
|
}
|
|
19773
|
-
|
|
19774
|
-
|
|
19775
|
-
|
|
19776
|
-
|
|
19777
|
-
|
|
19778
|
-
|
|
19779
|
-
|
|
19780
|
-
|
|
19781
|
-
|
|
19782
|
-
|
|
19783
|
-
|
|
19784
|
-
|
|
19785
|
-
const
|
|
19786
|
-
|
|
19787
|
-
|
|
19788
|
-
|
|
19789
|
-
|
|
19790
|
-
|
|
19791
|
-
|
|
19792
|
-
|
|
19793
|
-
|
|
19794
|
-
|
|
19795
|
-
|
|
19796
|
-
|
|
19797
|
-
|
|
19798
|
-
|
|
19799
|
-
|
|
19800
|
-
|
|
19801
|
-
|
|
19802
|
-
|
|
19803
|
-
|
|
19804
|
-
|
|
19805
|
-
|
|
19806
|
-
|
|
19807
|
-
|
|
19808
|
-
|
|
19809
|
-
|
|
19810
|
-
|
|
19811
|
-
|
|
19812
|
-
|
|
19813
|
-
to = dayEndAt(t);
|
|
19814
|
-
break;
|
|
20343
|
+
/**
|
|
20344
|
+
* `update` dry-run: input validation + current-record fetch (a missing
|
|
20345
|
+
* record carries `warnings` and no `diff` — pinned), the stale-object
|
|
20346
|
+
* check (mismatch throws STALE_OBJECT) and the best-effort `diff`. NO
|
|
20347
|
+
* mutating request is issued.
|
|
20348
|
+
*/
|
|
20349
|
+
async runUpdateDryRun(id, data, opts = {}, parentId) {
|
|
20350
|
+
const operation = `${this.config.key}.update`;
|
|
20351
|
+
const checks = [];
|
|
20352
|
+
const warnings = [];
|
|
20353
|
+
checks.push({ name: "input-validation", ok: isRecord2(data) && Object.keys(data).length > 0 });
|
|
20354
|
+
const scopedUpdate = parentId !== void 0 && this.config.parentScopedPath !== void 0;
|
|
20355
|
+
const collectionUpdate = scopedUpdate || this.config.collectionUpdate === true;
|
|
20356
|
+
const path = scopedUpdate ? this.scopedCollectionPath(parentId) : collectionUpdate ? this.resourcePath : `${this.resourcePath}/${id}`;
|
|
20357
|
+
const method = scopedUpdate ? "PATCH" : "PUT";
|
|
20358
|
+
let current = null;
|
|
20359
|
+
try {
|
|
20360
|
+
current = parentId === void 0 ? await this.getGlobal(id, true) : this.config.attachmentGet ? await this.getScopedModel(parentId, id, true) : await this.getScopedRaw(parentId, id, true);
|
|
20361
|
+
} catch (err) {
|
|
20362
|
+
if (err instanceof NotFoundError) {
|
|
20363
|
+
const updateReason = EMPTY_REASON_STAMPS[this.config.key]?.update;
|
|
20364
|
+
checks.push({
|
|
20365
|
+
name: "current-record-exists",
|
|
20366
|
+
ok: false,
|
|
20367
|
+
...updateReason !== void 0 ? { note: updateReason.note } : {}
|
|
20368
|
+
});
|
|
20369
|
+
warnings.push(`current record ${id} not fetchable; no diff`);
|
|
20370
|
+
return buildDryRunResult({
|
|
20371
|
+
operation,
|
|
20372
|
+
method,
|
|
20373
|
+
path,
|
|
20374
|
+
ids: [id],
|
|
20375
|
+
checks,
|
|
20376
|
+
impact: { affected: 1, scope: "single-record", reversible: true },
|
|
20377
|
+
warnings,
|
|
20378
|
+
data,
|
|
20379
|
+
...updateReason !== void 0 ? { emptyReason: updateReason } : {}
|
|
20380
|
+
}, { redactPersonalData: this.http.redactPersonalData });
|
|
20381
|
+
}
|
|
20382
|
+
throw err;
|
|
19815
20383
|
}
|
|
19816
|
-
|
|
19817
|
-
|
|
19818
|
-
|
|
19819
|
-
const daysBack = kind === "lastFullWeek" ? 7 : 14;
|
|
19820
|
-
from = dayStart(-(daysSinceMonday + daysBack));
|
|
19821
|
-
to = dayStart(-daysSinceMonday) - 1;
|
|
19822
|
-
break;
|
|
20384
|
+
checks.push({ name: "current-record-exists", ok: true });
|
|
20385
|
+
if (this.staleObjectCheck(current, opts, operation)) {
|
|
20386
|
+
checks.push({ name: "stale-check", ok: true });
|
|
19823
20387
|
}
|
|
19824
|
-
|
|
19825
|
-
|
|
19826
|
-
|
|
19827
|
-
|
|
19828
|
-
|
|
20388
|
+
let envelopeData = data;
|
|
20389
|
+
if (parentId === void 0 && this.config.collectionUpdate === true && opts.raw !== true) {
|
|
20390
|
+
envelopeData = mergePartialBody(
|
|
20391
|
+
current,
|
|
20392
|
+
this.normalizeMutationBody(data)
|
|
20393
|
+
);
|
|
19829
20394
|
}
|
|
19830
|
-
|
|
19831
|
-
|
|
19832
|
-
|
|
19833
|
-
|
|
19834
|
-
|
|
19835
|
-
|
|
19836
|
-
|
|
20395
|
+
return buildDryRunResult({
|
|
20396
|
+
operation,
|
|
20397
|
+
method,
|
|
20398
|
+
path,
|
|
20399
|
+
ids: [id],
|
|
20400
|
+
checks,
|
|
20401
|
+
diff: computeFieldDiff(current, data),
|
|
20402
|
+
impact: { affected: 1, scope: "single-record", reversible: true },
|
|
20403
|
+
warnings,
|
|
20404
|
+
data: envelopeData
|
|
20405
|
+
}, { redactPersonalData: this.http.redactPersonalData });
|
|
20406
|
+
}
|
|
20407
|
+
/**
|
|
20408
|
+
* `delete` dry-run: current-record verification + the stale-object check +
|
|
20409
|
+
* the pinned envelope (no diff — a delete has no before/after fields). NO
|
|
20410
|
+
* mutating request is issued.
|
|
20411
|
+
*/
|
|
20412
|
+
async runDeleteDryRun(id, opts = {}, parentId) {
|
|
20413
|
+
const operation = `${this.config.key}.delete`;
|
|
20414
|
+
const checks = [];
|
|
20415
|
+
const warnings = [];
|
|
20416
|
+
const path = parentId === void 0 ? `${this.resourcePath}/${id}` : this.scopedItemPath(parentId, id);
|
|
20417
|
+
let current = null;
|
|
20418
|
+
try {
|
|
20419
|
+
current = parentId === void 0 ? await this.getGlobal(id, true) : this.config.attachmentGet ? await this.getScopedModel(parentId, id, true) : await this.getScopedRaw(parentId, id, true);
|
|
20420
|
+
} catch (err) {
|
|
20421
|
+
if (err instanceof NotFoundError) {
|
|
20422
|
+
checks.push({ name: "current-record-exists", ok: false });
|
|
20423
|
+
warnings.push(`record ${id} not fetchable; the vendor would reject the delete`);
|
|
20424
|
+
return buildDryRunResult({
|
|
20425
|
+
operation,
|
|
20426
|
+
method: "DELETE",
|
|
20427
|
+
path,
|
|
20428
|
+
ids: [id],
|
|
20429
|
+
checks,
|
|
20430
|
+
impact: { affected: 1, scope: "single-record", reversible: false },
|
|
20431
|
+
warnings
|
|
20432
|
+
}, { redactPersonalData: this.http.redactPersonalData });
|
|
20433
|
+
}
|
|
20434
|
+
throw err;
|
|
20435
|
+
}
|
|
20436
|
+
checks.push({ name: "current-record-exists", ok: true });
|
|
20437
|
+
if (this.staleObjectCheck(current, opts, operation)) {
|
|
20438
|
+
checks.push({ name: "stale-check", ok: true });
|
|
19837
20439
|
}
|
|
20440
|
+
return buildDryRunResult({
|
|
20441
|
+
operation,
|
|
20442
|
+
method: "DELETE",
|
|
20443
|
+
path,
|
|
20444
|
+
ids: [id],
|
|
20445
|
+
checks,
|
|
20446
|
+
impact: { affected: 1, scope: "single-record", reversible: false },
|
|
20447
|
+
warnings
|
|
20448
|
+
}, { redactPersonalData: this.http.redactPersonalData });
|
|
19838
20449
|
}
|
|
19839
|
-
|
|
20450
|
+
};
|
|
20451
|
+
|
|
20452
|
+
// src/resources/base/BaseResource.ts
|
|
20453
|
+
var BaseResource = class extends MutationBase {
|
|
20454
|
+
};
|
|
20455
|
+
|
|
20456
|
+
// src/operations/compositeNames.ts
|
|
20457
|
+
var PICKLIST_VALUE_NOT_SERVED_NOTE = "The picklist is served to this API user, but this record's value is not in it (an inactive or unexposed value) \u2014 the label cannot be resolved and is null; refreshing the field catalog will not surface it. (A picklist the vendor does not expose at all is reported with the field-catalog honesty note instead.)";
|
|
20458
|
+
var DEFAULT_MAX_NAME_READS = 20;
|
|
20459
|
+
var TICKET_LABEL_FIELDS = ["status", "priority", "ticketType", "issueType", "subIssueType", "source", "queueID"];
|
|
20460
|
+
function isVendorId(value) {
|
|
20461
|
+
if (typeof value === "number") return Number.isSafeInteger(value) && value >= 0;
|
|
20462
|
+
if (typeof value === "string") return /^\d+$/.test(value);
|
|
20463
|
+
return false;
|
|
19840
20464
|
}
|
|
19841
|
-
function
|
|
19842
|
-
|
|
19843
|
-
const
|
|
19844
|
-
const
|
|
19845
|
-
const
|
|
19846
|
-
|
|
19847
|
-
|
|
20465
|
+
function createNamePlan(labelFields = [], labelResource) {
|
|
20466
|
+
const resourceIds = [];
|
|
20467
|
+
const resSeen = /* @__PURE__ */ new Set();
|
|
20468
|
+
const recordFks = [];
|
|
20469
|
+
const fkSeen = /* @__PURE__ */ new Set();
|
|
20470
|
+
let lineOfBusinessId;
|
|
20471
|
+
let lineOfBusinessSource;
|
|
20472
|
+
let lineOfBusinessContractId;
|
|
20473
|
+
return {
|
|
20474
|
+
plan: () => ({
|
|
20475
|
+
resourceIds,
|
|
20476
|
+
recordFks,
|
|
20477
|
+
labelFields,
|
|
20478
|
+
...labelResource !== void 0 ? { labelResource } : {},
|
|
20479
|
+
...lineOfBusinessId !== void 0 ? { lineOfBusinessId } : {},
|
|
20480
|
+
...lineOfBusinessSource !== void 0 ? { lineOfBusinessSource } : {},
|
|
20481
|
+
...lineOfBusinessContractId !== void 0 ? { lineOfBusinessContractId } : {}
|
|
20482
|
+
}),
|
|
20483
|
+
addResource: (value) => {
|
|
20484
|
+
if (!isVendorId(value)) return;
|
|
20485
|
+
const k = String(value);
|
|
20486
|
+
if (!resSeen.has(k)) {
|
|
20487
|
+
resSeen.add(k);
|
|
20488
|
+
resourceIds.push(value);
|
|
20489
|
+
}
|
|
20490
|
+
},
|
|
20491
|
+
addFk: (table, value) => {
|
|
20492
|
+
if (!isVendorId(value)) return;
|
|
20493
|
+
const k = `${table}:${String(value)}`;
|
|
20494
|
+
if (!fkSeen.has(k)) {
|
|
20495
|
+
fkSeen.add(k);
|
|
20496
|
+
recordFks.push({ table, id: value });
|
|
20497
|
+
}
|
|
20498
|
+
},
|
|
20499
|
+
addLineOfBusinessFromContract: (value) => {
|
|
20500
|
+
if (!isVendorId(value)) return;
|
|
20501
|
+
lineOfBusinessContractId = value;
|
|
20502
|
+
},
|
|
20503
|
+
addLineOfBusiness: (value, origin) => {
|
|
20504
|
+
if (!isVendorId(value)) return;
|
|
20505
|
+
lineOfBusinessId = value;
|
|
20506
|
+
if (origin !== void 0) {
|
|
20507
|
+
lineOfBusinessSource = origin.source;
|
|
20508
|
+
if (origin.contractId !== void 0) lineOfBusinessContractId = origin.contractId;
|
|
20509
|
+
}
|
|
20510
|
+
}
|
|
20511
|
+
};
|
|
19848
20512
|
}
|
|
19849
|
-
|
|
19850
|
-
const
|
|
19851
|
-
|
|
19852
|
-
|
|
19853
|
-
{ field, op: "gte", value: from },
|
|
19854
|
-
{ field, op: "lte", value: to }
|
|
19855
|
-
];
|
|
19856
|
-
const res = await http.request({
|
|
19857
|
-
method: "POST",
|
|
19858
|
-
idempotent: true,
|
|
19859
|
-
path,
|
|
19860
|
-
body: { MaxRecords: limit, filter },
|
|
19861
|
-
operation
|
|
19862
|
-
});
|
|
19863
|
-
return unwrapItems(res).items;
|
|
20513
|
+
function addNameSibling(row, field, resolve) {
|
|
20514
|
+
const value = row[field];
|
|
20515
|
+
if (value === void 0) return;
|
|
20516
|
+
row[`${field}Name`] = isVendorId(value) ? resolve(value) : null;
|
|
19864
20517
|
}
|
|
19865
|
-
|
|
19866
|
-
|
|
19867
|
-
|
|
19868
|
-
|
|
19869
|
-
throw new AutotaskConfigError(
|
|
19870
|
-
zone === void 0 ? `${opName}: ${label} must be a UTC 'YYYY-MM-DD' date (got ${JSON.stringify(value)})` : `${opName}: ${label} must be a 'YYYY-MM-DD' local calendar day in ${zone} (got ${JSON.stringify(value)})`,
|
|
19871
|
-
{ operation: opName }
|
|
19872
|
-
);
|
|
19873
|
-
}
|
|
19874
|
-
const y = Number(m[1]);
|
|
19875
|
-
const mo = Number(m[2]);
|
|
19876
|
-
const d = Number(m[3]);
|
|
19877
|
-
const probe = new Date(Date.UTC(y, mo - 1, d));
|
|
19878
|
-
const real = probe.getUTCFullYear() === y && probe.getUTCMonth() === mo - 1 && probe.getUTCDate() === d;
|
|
19879
|
-
const ms = !real ? NaN : zone === void 0 ? Date.UTC(y, mo - 1, d) : localDayStartMs(y, mo, d, zone);
|
|
19880
|
-
if (!Number.isFinite(ms)) {
|
|
19881
|
-
throw new AutotaskConfigError(
|
|
19882
|
-
zone === void 0 ? `${opName}: ${label} is not a valid UTC date (${JSON.stringify(value)})` : `${opName}: ${label} is not a valid calendar day in ${zone} (${JSON.stringify(value)})`,
|
|
19883
|
-
{ operation: opName }
|
|
19884
|
-
);
|
|
19885
|
-
}
|
|
19886
|
-
return { y, mo, d, ms };
|
|
20518
|
+
function addLabelSibling(row, field, names) {
|
|
20519
|
+
const value = row[field];
|
|
20520
|
+
if (value === void 0) return;
|
|
20521
|
+
row[`${field}Label`] = names.labelFor(field, value);
|
|
19887
20522
|
}
|
|
19888
|
-
|
|
19889
|
-
|
|
19890
|
-
|
|
19891
|
-
|
|
19892
|
-
|
|
19893
|
-
|
|
19894
|
-
|
|
19895
|
-
|
|
19896
|
-
|
|
20523
|
+
var FK_TABLE_PATH = {
|
|
20524
|
+
role: "/V1.0/Roles",
|
|
20525
|
+
billingCode: "/V1.0/BillingCodes",
|
|
20526
|
+
contract: "/V1.0/Contracts",
|
|
20527
|
+
company: "/V1.0/Companies",
|
|
20528
|
+
ticket: "/V1.0/Tickets"
|
|
20529
|
+
};
|
|
20530
|
+
var FK_NAME_FIELD = {
|
|
20531
|
+
role: "name",
|
|
20532
|
+
billingCode: "name",
|
|
20533
|
+
contract: "contractName",
|
|
20534
|
+
company: "companyName",
|
|
20535
|
+
ticket: "ticketNumber"
|
|
20536
|
+
};
|
|
20537
|
+
function fkName(record, table) {
|
|
20538
|
+
if (table === "ticket") {
|
|
20539
|
+
const parts = [record["ticketNumber"], record["title"]].filter((v) => typeof v === "string" && v.trim() !== "").map((v) => v.trim());
|
|
20540
|
+
return parts.length > 0 ? parts.join(" ") : null;
|
|
19897
20541
|
}
|
|
19898
|
-
const
|
|
19899
|
-
|
|
19900
|
-
{ field, op: "gte", value: isoUtc(fromB.ms) },
|
|
19901
|
-
{ field, op: "lte", value: isoUtc(toEndMs) }
|
|
19902
|
-
];
|
|
19903
|
-
const res = await http.request({
|
|
19904
|
-
method: "POST",
|
|
19905
|
-
idempotent: true,
|
|
19906
|
-
path,
|
|
19907
|
-
body: { MaxRecords: limit, filter },
|
|
19908
|
-
operation
|
|
19909
|
-
});
|
|
19910
|
-
return unwrapItems(res).items;
|
|
19911
|
-
}
|
|
19912
|
-
|
|
19913
|
-
// src/resources/special/billing_items.ts
|
|
19914
|
-
async function windowQuery(_resource, kind, http, opts) {
|
|
19915
|
-
const now = opts?.now ?? /* @__PURE__ */ new Date();
|
|
19916
|
-
const tz = opts?.tz === void 0 ? void 0 : validateZone(opts.tz, `billing_items.${kind}`);
|
|
19917
|
-
return dateWindowQuery(
|
|
19918
|
-
http,
|
|
19919
|
-
"/V1.0/BillingItems/query",
|
|
19920
|
-
"itemDate",
|
|
19921
|
-
kind,
|
|
19922
|
-
clampWindowLimit(opts?.limit),
|
|
19923
|
-
`billing_items.${kind}`,
|
|
19924
|
-
now,
|
|
19925
|
-
tz
|
|
19926
|
-
);
|
|
20542
|
+
const value = record[FK_NAME_FIELD[table]];
|
|
20543
|
+
return typeof value === "string" && value.trim() !== "" ? value : null;
|
|
19927
20544
|
}
|
|
19928
|
-
|
|
19929
|
-
const
|
|
19930
|
-
return
|
|
19931
|
-
http,
|
|
19932
|
-
"/V1.0/BillingItems/query",
|
|
19933
|
-
"itemDate",
|
|
19934
|
-
from,
|
|
19935
|
-
to,
|
|
19936
|
-
clampWindowLimit(opts?.limit),
|
|
19937
|
-
"billing_items.customRange",
|
|
19938
|
-
zone
|
|
19939
|
-
);
|
|
20545
|
+
function faultRow(err) {
|
|
20546
|
+
const e = err;
|
|
20547
|
+
return e.httpStatus !== void 0 ? { code: e.code, message: e.message, httpStatus: e.httpStatus } : { code: e.code, message: e.message };
|
|
19940
20548
|
}
|
|
19941
|
-
|
|
19942
|
-
|
|
19943
|
-
|
|
19944
|
-
|
|
19945
|
-
|
|
19946
|
-
key: "billing_items",
|
|
19947
|
-
resourcePath: "/V1.0/BillingItems",
|
|
19948
|
-
deletable: false,
|
|
19949
|
-
udfCapable: false,
|
|
19950
|
-
collectionUpdate: true
|
|
19951
|
-
// D1: the spec's item path is GET-only — confirmed updates PUT the collection (id in the body)
|
|
19952
|
-
}, pageSize);
|
|
19953
|
-
}
|
|
19954
|
-
/** Query BillingItems records with the G3 filter surface (server-side JSON filter). */
|
|
19955
|
-
query(params) {
|
|
19956
|
-
return this.queryItems(params);
|
|
19957
|
-
}
|
|
19958
|
-
/** Get every BillingItems record (the MCP-preferred read; 1000-page guarded). */
|
|
19959
|
-
async queryAll(params) {
|
|
19960
|
-
return this.queryAllItems(params);
|
|
19961
|
-
}
|
|
19962
|
-
/** Count matching records (queryCount envelope unwrapped to a plain number). */
|
|
19963
|
-
async count(params) {
|
|
19964
|
-
return this.countGlobal(params);
|
|
19965
|
-
}
|
|
19966
|
-
/** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
|
|
19967
|
-
async get(id, opts = {}) {
|
|
19968
|
-
return this.getGlobal(id, opts?.fresh === true);
|
|
19969
|
-
}
|
|
19970
|
-
/** Update a record (PUT on the COLLECTION — the vendored spec's item path is GET-only; the vendor
|
|
19971
|
-
* keys the record by the id in the body). The vendor validates the FULL model on this route (V2), so a
|
|
19972
|
-
* partial body is merged into the CURRENT record first: one fresh GET (which also serves the
|
|
19973
|
-
* stale-object guard's read) then the collection PUT of the merged model — an explicit null in the
|
|
19974
|
-
* caller body sends null; an omitted field keeps the current value. `raw: true` in the opts bag sends
|
|
19975
|
-
* the body as-is (full model or intentional partial — the compatibility escape hatch; no merge read).
|
|
19976
|
-
* `dryRun: true` returns the pinned envelope whose `data` is the merged body (the exact confirmed
|
|
19977
|
-
* wire) with the best-effort diff, from that same single read; a caller-supplied lastModifiedDate that
|
|
19978
|
-
* mismatches the vendor's current value throws STALE_OBJECT before the mutation. */
|
|
19979
|
-
async update(id, data, opts = {}) {
|
|
19980
|
-
if (opts.dryRun === true) return this.runUpdateDryRun(id, data, opts);
|
|
19981
|
-
return this.updateGlobal(id, data, opts);
|
|
19982
|
-
}
|
|
19983
|
-
/** Resolve a BillingItems from a numeric id (or, where the vendor indexes a primary text field,
|
|
19984
|
-
* from a numeric id — this resource has no primary text field). A numeric id miss throws NOT_FOUND;
|
|
19985
|
-
* a bare value rejects with a config error (ids only);
|
|
19986
|
-
* non-match returns null; ambiguity throws RESOLUTION_AMBIGUOUS (candidate ids) and a
|
|
19987
|
-
* cap-tripped bounded client scan throws RESOLUTION_TRUNCATED (never a silent null).
|
|
19988
|
-
* The compact shape is the structural alias of the full record; `expand: true` is the
|
|
19989
|
-
* type-level escape hatch. */
|
|
19990
|
-
async resolve(identifier, opts = {}) {
|
|
19991
|
-
void opts;
|
|
19992
|
-
return this.resolveBase(identifier);
|
|
19993
|
-
}
|
|
19994
|
-
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
19995
|
-
customRange(from, to, opts) {
|
|
19996
|
-
return customRange(this, from, to, this.http, opts);
|
|
19997
|
-
}
|
|
19998
|
-
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
19999
|
-
last14(opts) {
|
|
20000
|
-
return windowQuery(this, "last14", this.http, opts);
|
|
20001
|
-
}
|
|
20002
|
-
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
20003
|
-
last24h(opts) {
|
|
20004
|
-
return windowQuery(this, "last24h", this.http, opts);
|
|
20005
|
-
}
|
|
20006
|
-
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
20007
|
-
last30(opts) {
|
|
20008
|
-
return windowQuery(this, "last30", this.http, opts);
|
|
20009
|
-
}
|
|
20010
|
-
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
20011
|
-
last7(opts) {
|
|
20012
|
-
return windowQuery(this, "last7", this.http, opts);
|
|
20013
|
-
}
|
|
20014
|
-
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
20015
|
-
last90(opts) {
|
|
20016
|
-
return windowQuery(this, "last90", this.http, opts);
|
|
20017
|
-
}
|
|
20018
|
-
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
20019
|
-
lastFullFortnight(opts) {
|
|
20020
|
-
return windowQuery(this, "lastFullFortnight", this.http, opts);
|
|
20021
|
-
}
|
|
20022
|
-
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
20023
|
-
lastFullMonth(opts) {
|
|
20024
|
-
return windowQuery(this, "lastFullMonth", this.http, opts);
|
|
20025
|
-
}
|
|
20026
|
-
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
20027
|
-
lastFullQuarter(opts) {
|
|
20028
|
-
return windowQuery(this, "lastFullQuarter", this.http, opts);
|
|
20029
|
-
}
|
|
20030
|
-
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
20031
|
-
lastFullWeek(opts) {
|
|
20032
|
-
return windowQuery(this, "lastFullWeek", this.http, opts);
|
|
20549
|
+
function vendorId(value) {
|
|
20550
|
+
if (typeof value === "number") return Number.isSafeInteger(value) && value >= 1 ? value : void 0;
|
|
20551
|
+
if (typeof value === "string" && /^[0-9]+$/.test(value.trim())) {
|
|
20552
|
+
const n = Number(value.trim());
|
|
20553
|
+
return Number.isSafeInteger(n) && n >= 1 ? n : void 0;
|
|
20033
20554
|
}
|
|
20034
|
-
|
|
20035
|
-
|
|
20036
|
-
|
|
20555
|
+
return void 0;
|
|
20556
|
+
}
|
|
20557
|
+
async function resolveLineOfBusinessChain(http, associationId, options) {
|
|
20558
|
+
const operation = options.operation ?? "compositeNames";
|
|
20559
|
+
const degrade = options.onError === "degrade";
|
|
20560
|
+
const get = (path) => {
|
|
20561
|
+
const res = http.request({ method: "GET", path, operation }).then((body) => {
|
|
20562
|
+
const b = body;
|
|
20563
|
+
if (b === null || !("item" in b) || b.item === null || b.item === void 0) return null;
|
|
20564
|
+
return b.item;
|
|
20565
|
+
});
|
|
20566
|
+
if (!degrade) {
|
|
20567
|
+
return res.catch((e) => {
|
|
20568
|
+
if (e instanceof NotFoundError) return null;
|
|
20569
|
+
throw e;
|
|
20570
|
+
});
|
|
20571
|
+
}
|
|
20572
|
+
return res.catch((e) => {
|
|
20573
|
+
if (e instanceof NotFoundError || isAutotaskError(e) && e.code !== "ABORTED") return null;
|
|
20574
|
+
throw e;
|
|
20575
|
+
});
|
|
20576
|
+
};
|
|
20577
|
+
let remaining = options.budget;
|
|
20578
|
+
if (remaining < 1) {
|
|
20579
|
+
return {
|
|
20580
|
+
lineOfBusiness: { association: null, organizationalLevel2: null, organizationalLevel1: null, markers: { association: "capped", organizationalLevel2: "capped", organizationalLevel1: "capped" } },
|
|
20581
|
+
used: 0,
|
|
20582
|
+
truncated: true
|
|
20583
|
+
};
|
|
20037
20584
|
}
|
|
20038
|
-
|
|
20039
|
-
|
|
20040
|
-
|
|
20585
|
+
remaining -= 1;
|
|
20586
|
+
const association = await get(`/V1.0/OrganizationalLevelAssociations/${associationId}`);
|
|
20587
|
+
const associationMarker = association === null ? "unresolved" : "ok";
|
|
20588
|
+
if (association === null) {
|
|
20589
|
+
return {
|
|
20590
|
+
lineOfBusiness: { association: null, organizationalLevel2: null, organizationalLevel1: null, markers: { association: associationMarker, organizationalLevel2: "missing", organizationalLevel1: "missing" } },
|
|
20591
|
+
used: 1,
|
|
20592
|
+
truncated: false
|
|
20593
|
+
};
|
|
20041
20594
|
}
|
|
20042
|
-
|
|
20043
|
-
|
|
20044
|
-
|
|
20045
|
-
|
|
20046
|
-
|
|
20047
|
-
|
|
20048
|
-
|
|
20049
|
-
|
|
20050
|
-
|
|
20595
|
+
const l2Id = vendorId(association.organizationalLevel2ID);
|
|
20596
|
+
const l1Id = vendorId(association.organizationalLevel1ID);
|
|
20597
|
+
let l2Marker = l2Id !== void 0 ? "unresolved" : "missing";
|
|
20598
|
+
let l1Marker = l1Id !== void 0 ? "unresolved" : "missing";
|
|
20599
|
+
const need = (l2Id !== void 0 ? 1 : 0) + (l1Id !== void 0 ? 1 : 0);
|
|
20600
|
+
let l2 = null;
|
|
20601
|
+
let l1 = null;
|
|
20602
|
+
let used = 1;
|
|
20603
|
+
let truncated = false;
|
|
20604
|
+
if (need > 0 && remaining < need) {
|
|
20605
|
+
truncated = true;
|
|
20606
|
+
if (l2Id !== void 0) l2Marker = "capped";
|
|
20607
|
+
if (l1Id !== void 0) l1Marker = "capped";
|
|
20608
|
+
} else {
|
|
20609
|
+
used += need;
|
|
20610
|
+
const [r2, r1] = await Promise.all([
|
|
20611
|
+
l2Id !== void 0 ? get(`/V1.0/OrganizationalLevel2s/${l2Id}`) : Promise.resolve(null),
|
|
20612
|
+
l1Id !== void 0 ? get(`/V1.0/OrganizationalLevel1s/${l1Id}`) : Promise.resolve(null)
|
|
20613
|
+
]);
|
|
20614
|
+
l2 = r2;
|
|
20615
|
+
l1 = r1;
|
|
20616
|
+
if (l2Id !== void 0) l2Marker = l2 === null ? "unresolved" : "ok";
|
|
20617
|
+
if (l1Id !== void 0) l1Marker = l1 === null ? "unresolved" : "ok";
|
|
20051
20618
|
}
|
|
20052
|
-
return
|
|
20619
|
+
return {
|
|
20620
|
+
lineOfBusiness: { association, organizationalLevel2: l2, organizationalLevel1: l1, markers: { association: associationMarker, organizationalLevel2: l2Marker, organizationalLevel1: l1Marker } },
|
|
20621
|
+
used,
|
|
20622
|
+
truncated
|
|
20623
|
+
};
|
|
20053
20624
|
}
|
|
20054
|
-
async function
|
|
20055
|
-
if (opts.
|
|
20056
|
-
throw new AutotaskConfigError(`
|
|
20625
|
+
async function resolveCompositeNames(http, plan, opts = {}) {
|
|
20626
|
+
if (opts.maxNameReads !== void 0 && (!Number.isInteger(opts.maxNameReads) || opts.maxNameReads < 1)) {
|
|
20627
|
+
throw new AutotaskConfigError(`composite names: maxNameReads must be an integer >= 1 (got ${String(opts.maxNameReads)})`);
|
|
20057
20628
|
}
|
|
20058
|
-
const
|
|
20059
|
-
|
|
20060
|
-
|
|
20061
|
-
|
|
20062
|
-
{ suggestedAction: "Pass at least one vendor numeric resource id \u2014 safe integer, or the canonical integer string for ids above 2^53." }
|
|
20063
|
-
);
|
|
20629
|
+
const budget = Math.min(DEFAULT_MAX_NAME_READS, opts.maxNameReads ?? DEFAULT_MAX_NAME_READS);
|
|
20630
|
+
const client = http.client;
|
|
20631
|
+
if (client === void 0) {
|
|
20632
|
+
throw new AutotaskConfigError("composite names: the transport is not wired to an AutotaskClient (the composite must run through the client)");
|
|
20064
20633
|
}
|
|
20065
|
-
|
|
20066
|
-
|
|
20067
|
-
|
|
20068
|
-
|
|
20069
|
-
|
|
20634
|
+
const readCache = http.readCache;
|
|
20635
|
+
const nameCache = http.resourceNameCache;
|
|
20636
|
+
const section = { truncated: false, rows: {}, reasons: {} };
|
|
20637
|
+
let remaining = budget;
|
|
20638
|
+
let contractLobPointer;
|
|
20639
|
+
let contractLobUnread;
|
|
20640
|
+
const coldResource = [];
|
|
20641
|
+
for (const id of plan.resourceIds) {
|
|
20642
|
+
if (nameCache !== void 0 && nameCache.get(id) !== void 0) continue;
|
|
20643
|
+
if (readCache !== void 0 && readCache.get(`/V1.0/Resources/${id}`) !== void 0) continue;
|
|
20644
|
+
coldResource.push(id);
|
|
20070
20645
|
}
|
|
20071
|
-
const
|
|
20072
|
-
const
|
|
20073
|
-
|
|
20074
|
-
|
|
20075
|
-
|
|
20076
|
-
|
|
20077
|
-
|
|
20078
|
-
|
|
20079
|
-
|
|
20080
|
-
|
|
20081
|
-
|
|
20082
|
-
|
|
20083
|
-
|
|
20084
|
-
|
|
20646
|
+
const withinBudget = coldResource.slice(0, remaining);
|
|
20647
|
+
const overBudgetResource = coldResource.slice(remaining);
|
|
20648
|
+
remaining -= withinBudget.length;
|
|
20649
|
+
for (const id of overBudgetResource) {
|
|
20650
|
+
section.rows[`resource:${id}`] = { name: null, blind: false, capped: true };
|
|
20651
|
+
section.truncated = true;
|
|
20652
|
+
}
|
|
20653
|
+
const overSet = new Set(overBudgetResource.map((id) => String(id)));
|
|
20654
|
+
const opIds = plan.resourceIds.filter((id) => !overSet.has(String(id)));
|
|
20655
|
+
if (opIds.length > 0) {
|
|
20656
|
+
let pass = opIds;
|
|
20657
|
+
if (pass.length > 500) {
|
|
20658
|
+
for (const id of pass.slice(500)) {
|
|
20659
|
+
section.rows[`resource:${id}`] = { name: null, blind: false, capped: true };
|
|
20660
|
+
section.truncated = true;
|
|
20085
20661
|
}
|
|
20662
|
+
pass = pass.slice(0, 500);
|
|
20086
20663
|
}
|
|
20087
|
-
|
|
20088
|
-
|
|
20089
|
-
|
|
20090
|
-
|
|
20091
|
-
|
|
20092
|
-
});
|
|
20093
|
-
}
|
|
20094
|
-
const unique = [];
|
|
20095
|
-
const seen = /* @__PURE__ */ new Set();
|
|
20096
|
-
for (const id of canonical) {
|
|
20097
|
-
const k = String(id);
|
|
20098
|
-
if (!seen.has(k)) {
|
|
20099
|
-
seen.add(k);
|
|
20100
|
-
unique.push(id);
|
|
20664
|
+
const res = await expandResourceNames(client, pass);
|
|
20665
|
+
for (const r of res.results) {
|
|
20666
|
+
const row = { name: r.name, blind: r.blind, capped: false };
|
|
20667
|
+
if (r.error !== void 0) row.error = faultRow(r.error);
|
|
20668
|
+
section.rows[`resource:${r.id}`] = row;
|
|
20101
20669
|
}
|
|
20102
20670
|
}
|
|
20103
|
-
const
|
|
20104
|
-
|
|
20105
|
-
|
|
20106
|
-
|
|
20107
|
-
|
|
20108
|
-
|
|
20109
|
-
|
|
20110
|
-
|
|
20111
|
-
|
|
20112
|
-
|
|
20113
|
-
|
|
20114
|
-
|
|
20115
|
-
|
|
20116
|
-
|
|
20117
|
-
|
|
20118
|
-
|
|
20119
|
-
byId.set(k, joined);
|
|
20120
|
-
continue;
|
|
20671
|
+
const holders = {
|
|
20672
|
+
role: client.roles,
|
|
20673
|
+
billingCode: client.billingCodes,
|
|
20674
|
+
contract: client.contracts,
|
|
20675
|
+
company: client.companies,
|
|
20676
|
+
ticket: client.tickets
|
|
20677
|
+
};
|
|
20678
|
+
for (const fk of plan.recordFks) {
|
|
20679
|
+
const key = `${fk.table}:${fk.id}`;
|
|
20680
|
+
const cold = readCache === void 0 || readCache.get(`${FK_TABLE_PATH[fk.table]}/${fk.id}`) === void 0;
|
|
20681
|
+
if (cold) {
|
|
20682
|
+
if (remaining < 1) {
|
|
20683
|
+
section.rows[key] = { name: null, blind: false, capped: true };
|
|
20684
|
+
section.truncated = true;
|
|
20685
|
+
if (fk.table === "contract" && plan.lineOfBusinessContractId !== void 0 && String(fk.id) === String(plan.lineOfBusinessContractId)) {
|
|
20686
|
+
contractLobUnread = "capped";
|
|
20121
20687
|
}
|
|
20122
|
-
}
|
|
20123
|
-
const hit = cache.get(id);
|
|
20124
|
-
if (hit !== void 0) {
|
|
20125
|
-
byId.set(k, { name: hit.name, blind: hit.blind });
|
|
20126
20688
|
continue;
|
|
20127
20689
|
}
|
|
20690
|
+
remaining -= 1;
|
|
20128
20691
|
}
|
|
20129
|
-
|
|
20130
|
-
|
|
20131
|
-
|
|
20132
|
-
|
|
20133
|
-
|
|
20134
|
-
|
|
20135
|
-
|
|
20136
|
-
|
|
20137
|
-
|
|
20138
|
-
|
|
20139
|
-
|
|
20140
|
-
|
|
20141
|
-
|
|
20142
|
-
if (isAutotaskError(err) && err.code !== "ABORTED") {
|
|
20143
|
-
const e = { code: err.code, message: err.message };
|
|
20144
|
-
if (err.httpStatus !== void 0) e.httpStatus = err.httpStatus;
|
|
20145
|
-
return { name: null, blind: false, error: e };
|
|
20692
|
+
try {
|
|
20693
|
+
const record = await holders[fk.table].get(fk.id);
|
|
20694
|
+
if (fk.table === "contract" && plan.lineOfBusinessId === void 0 && plan.lineOfBusinessContractId !== void 0 && String(fk.id) === String(plan.lineOfBusinessContractId)) {
|
|
20695
|
+
contractLobPointer = vendorId(record["organizationalLevelAssociationID"]);
|
|
20696
|
+
}
|
|
20697
|
+
section.rows[key] = { name: fkName(record, fk.table), blind: false, capped: false };
|
|
20698
|
+
} catch (err) {
|
|
20699
|
+
if (err instanceof NotFoundError) {
|
|
20700
|
+
section.rows[key] = { name: null, blind: true, capped: false };
|
|
20701
|
+
} else if (isAutotaskError(err) && err.code !== "ABORTED") {
|
|
20702
|
+
section.rows[key] = { name: null, blind: false, capped: false, error: faultRow(err) };
|
|
20703
|
+
if (fk.table === "contract" && plan.lineOfBusinessContractId !== void 0 && String(fk.id) === String(plan.lineOfBusinessContractId)) {
|
|
20704
|
+
contractLobUnread = "unresolved";
|
|
20146
20705
|
}
|
|
20706
|
+
} else {
|
|
20147
20707
|
throw err;
|
|
20148
20708
|
}
|
|
20149
|
-
}
|
|
20150
|
-
const settled = cache !== void 0 ? await cache.track(id, work) : await work;
|
|
20151
|
-
byId.set(k, settled);
|
|
20709
|
+
}
|
|
20152
20710
|
}
|
|
20153
|
-
const
|
|
20154
|
-
|
|
20155
|
-
|
|
20156
|
-
|
|
20711
|
+
const picklists = {};
|
|
20712
|
+
if (plan.labelFields.length > 0) {
|
|
20713
|
+
let usable = true;
|
|
20714
|
+
try {
|
|
20715
|
+
const labelResource = plan.labelResource ?? "tickets";
|
|
20716
|
+
const catalog = await fieldCatalog(client, { resources: [labelResource], labels: true, fields: [...plan.labelFields] });
|
|
20717
|
+
const row = catalog.rows.find((r) => r.resource === labelResource);
|
|
20718
|
+
if (row === void 0 || row.error !== null || row.fields === void 0) {
|
|
20719
|
+
usable = false;
|
|
20720
|
+
} else {
|
|
20721
|
+
for (const f of plan.labelFields) {
|
|
20722
|
+
const frow = row.fields.find((x) => x.name === f);
|
|
20723
|
+
picklists[f] = frow?.picklistValues;
|
|
20724
|
+
}
|
|
20725
|
+
}
|
|
20726
|
+
} catch (err) {
|
|
20727
|
+
if (isAutotaskError(err) && err.code !== "ABORTED") usable = false;
|
|
20728
|
+
else throw err;
|
|
20729
|
+
}
|
|
20730
|
+
if (!usable) for (const f of plan.labelFields) section.reasons[f] = FIELD_CATALOG_HONESTY_NOTE;
|
|
20157
20731
|
}
|
|
20158
|
-
|
|
20159
|
-
const
|
|
20160
|
-
|
|
20161
|
-
|
|
20732
|
+
let lineOfBusiness2 = null;
|
|
20733
|
+
const lobId = plan.lineOfBusinessId ?? contractLobPointer;
|
|
20734
|
+
if (lobId !== void 0) {
|
|
20735
|
+
const chain = await resolveLineOfBusinessChain(http, lobId, { budget: remaining, onError: "degrade" });
|
|
20736
|
+
remaining -= chain.used;
|
|
20737
|
+
if (chain.truncated) section.truncated = true;
|
|
20738
|
+
const fromContract = plan.lineOfBusinessId === void 0;
|
|
20739
|
+
lineOfBusiness2 = {
|
|
20740
|
+
...chain.lineOfBusiness,
|
|
20741
|
+
...fromContract ? { source: "contract", contractId: plan.lineOfBusinessContractId } : plan.lineOfBusinessSource !== void 0 ? { source: plan.lineOfBusinessSource } : {},
|
|
20742
|
+
...!fromContract && plan.lineOfBusinessContractId !== void 0 ? { contractId: plan.lineOfBusinessContractId } : {}
|
|
20743
|
+
};
|
|
20744
|
+
} else if (contractLobUnread !== void 0 && plan.lineOfBusinessContractId !== void 0) {
|
|
20745
|
+
lineOfBusiness2 = {
|
|
20746
|
+
source: "contract",
|
|
20747
|
+
contractId: plan.lineOfBusinessContractId,
|
|
20748
|
+
association: null,
|
|
20749
|
+
organizationalLevel2: null,
|
|
20750
|
+
organizationalLevel1: null,
|
|
20751
|
+
markers: { association: contractLobUnread, organizationalLevel2: "missing", organizationalLevel1: "missing" }
|
|
20752
|
+
};
|
|
20753
|
+
}
|
|
20754
|
+
return {
|
|
20755
|
+
section,
|
|
20756
|
+
lineOfBusiness: lineOfBusiness2,
|
|
20757
|
+
resourceName: (id) => section.rows[`resource:${id}`]?.name ?? null,
|
|
20758
|
+
recordName: (table, id) => section.rows[`${table}:${id}`]?.name ?? null,
|
|
20759
|
+
labelFor: (field, value) => {
|
|
20760
|
+
if (value === null || value === void 0) return null;
|
|
20761
|
+
const values = picklists[field];
|
|
20762
|
+
if (values === void 0) {
|
|
20763
|
+
section.reasons[field] = FIELD_CATALOG_HONESTY_NOTE;
|
|
20764
|
+
return null;
|
|
20765
|
+
}
|
|
20766
|
+
const hit = values.find((v) => String(v.value) === String(value));
|
|
20767
|
+
if (hit === void 0) {
|
|
20768
|
+
section.reasons[field] = PICKLIST_VALUE_NOT_SERVED_NOTE;
|
|
20769
|
+
return null;
|
|
20770
|
+
}
|
|
20771
|
+
return typeof hit.label === "string" ? hit.label : null;
|
|
20772
|
+
}
|
|
20773
|
+
};
|
|
20162
20774
|
}
|
|
20163
|
-
|
|
20164
|
-
|
|
20165
|
-
|
|
20166
|
-
|
|
20167
|
-
|
|
20168
|
-
|
|
20169
|
-
|
|
20170
|
-
|
|
20171
|
-
|
|
20172
|
-
|
|
20173
|
-
|
|
20174
|
-
|
|
20175
|
-
|
|
20176
|
-
|
|
20177
|
-
|
|
20775
|
+
var ROW_FK_TABLES = {
|
|
20776
|
+
companyID: "company",
|
|
20777
|
+
contractID: "contract",
|
|
20778
|
+
ticketID: "ticket",
|
|
20779
|
+
roleID: "role",
|
|
20780
|
+
billingCodeID: "billingCode"
|
|
20781
|
+
};
|
|
20782
|
+
var isResourceFkField = (field) => field === "resourceID" || field.endsWith("ResourceID");
|
|
20783
|
+
var LABEL_NAME_MAX_CHARS = 200;
|
|
20784
|
+
function untrustedName(name) {
|
|
20785
|
+
if (name === null) return null;
|
|
20786
|
+
const [[capped]] = truncateNoteChars([{ v: name }], LABEL_NAME_MAX_CHARS, ["v"]);
|
|
20787
|
+
return wrapUntrusted(capped.v) ?? capped.v;
|
|
20788
|
+
}
|
|
20789
|
+
function readNamesOpt(value, where) {
|
|
20790
|
+
if (value === void 0) return true;
|
|
20791
|
+
if (typeof value !== "boolean") {
|
|
20792
|
+
throw new AutotaskConfigError(`${where}: names must be a boolean (got ${typeof value}) \u2014 true (default) adds label siblings, false returns raw ids only`);
|
|
20178
20793
|
}
|
|
20179
|
-
return
|
|
20180
|
-
}
|
|
20181
|
-
var ENTITY_TYPE_TO_RESOURCE = buildEntityTypeMap();
|
|
20182
|
-
var ENUMERABLE_RESOURCES = new Set(
|
|
20183
|
-
CAPABILITIES.filter((rec) => rec.resource !== "operations" && (rec.operation === "query" || rec.operation === "get")).map(
|
|
20184
|
-
(rec) => rec.resource
|
|
20185
|
-
)
|
|
20186
|
-
);
|
|
20187
|
-
function resourceForEntityType(entityType) {
|
|
20188
|
-
if (typeof entityType !== "string" || entityType === "") return null;
|
|
20189
|
-
return ENTITY_TYPE_TO_RESOURCE[entityType] ?? null;
|
|
20190
|
-
}
|
|
20191
|
-
function isEnumerableResource(resource) {
|
|
20192
|
-
if (typeof resource !== "string" || resource === "") return false;
|
|
20193
|
-
return ENUMERABLE_RESOURCES.has(resource);
|
|
20794
|
+
return value;
|
|
20194
20795
|
}
|
|
20195
|
-
|
|
20196
|
-
|
|
20197
|
-
|
|
20198
|
-
|
|
20199
|
-
|
|
20200
|
-
|
|
20201
|
-
"
|
|
20202
|
-
|
|
20203
|
-
];
|
|
20204
|
-
|
|
20205
|
-
|
|
20206
|
-
|
|
20207
|
-
|
|
20208
|
-
|
|
20209
|
-
|
|
20210
|
-
const
|
|
20211
|
-
|
|
20212
|
-
|
|
20213
|
-
|
|
20214
|
-
|
|
20215
|
-
|
|
20796
|
+
async function labelRows(client, resourceKey, rows) {
|
|
20797
|
+
const present = /* @__PURE__ */ new Set();
|
|
20798
|
+
for (const row of rows) for (const [k, v] of Object.entries(row)) if (v !== null && v !== void 0) present.add(k);
|
|
20799
|
+
if (present.size === 0) return { rows: [...rows] };
|
|
20800
|
+
let picklistFields = [];
|
|
20801
|
+
const modelFields = MODEL_FIELD_SETS[resourceKey] ?? [];
|
|
20802
|
+
const candidates = [...present].filter((f) => f !== "id" && modelFields.includes(f) && ROW_FK_TABLES[f] === void 0 && !isResourceFkField(f));
|
|
20803
|
+
if (candidates.length > 0) try {
|
|
20804
|
+
const catalog = await fieldCatalog(client, { resources: [resourceKey], labels: true });
|
|
20805
|
+
const fields = catalog.rows.find((r) => r.resource === resourceKey)?.fields ?? [];
|
|
20806
|
+
picklistFields = fields.filter((f) => f.isPickList && candidates.includes(f.name)).map((f) => f.name);
|
|
20807
|
+
} catch (err) {
|
|
20808
|
+
if (!isAutotaskError(err) || err.code === "ABORTED") throw err;
|
|
20809
|
+
}
|
|
20810
|
+
const builder = createNamePlan(picklistFields, resourceKey);
|
|
20811
|
+
for (const row of rows) {
|
|
20812
|
+
for (const [field, value] of Object.entries(row)) {
|
|
20813
|
+
if (value === 0 || value === "0") continue;
|
|
20814
|
+
const table = ROW_FK_TABLES[field];
|
|
20815
|
+
if (table !== void 0) builder.addFk(table, value);
|
|
20816
|
+
else if (isResourceFkField(field)) builder.addResource(value);
|
|
20817
|
+
}
|
|
20818
|
+
}
|
|
20819
|
+
const plan = builder.plan();
|
|
20820
|
+
if (plan.resourceIds.length === 0 && plan.recordFks.length === 0 && picklistFields.length === 0) return { rows: [...rows] };
|
|
20821
|
+
const names = await resolveCompositeNames(client.http, plan);
|
|
20822
|
+
const out = rows.map((row) => {
|
|
20823
|
+
const r = { ...row };
|
|
20824
|
+
for (const [field, value] of Object.entries(row)) {
|
|
20825
|
+
if (value === null || value === void 0 || value === 0 || value === "0") continue;
|
|
20826
|
+
if (picklistFields.includes(field)) r[`${field}Label`] = names.labelFor(field, value);
|
|
20827
|
+
const table = ROW_FK_TABLES[field];
|
|
20828
|
+
if (table !== void 0 && isVendorId(value)) r[`${field}Name`] = untrustedName(names.recordName(table, value));
|
|
20829
|
+
else if (isResourceFkField(field) && isVendorId(value)) r[`${field}Name`] = untrustedName(names.resourceName(value));
|
|
20830
|
+
}
|
|
20831
|
+
if (r["assignedResourceIDName"] !== void 0) r["assignedResourceName"] = r["assignedResourceIDName"];
|
|
20832
|
+
return r;
|
|
20833
|
+
});
|
|
20834
|
+
const capped = Object.values(names.section.rows).filter((row) => row.capped).length;
|
|
20835
|
+
const reasons = names.section.reasons;
|
|
20836
|
+
if (capped === 0 && Object.keys(reasons).length === 0) return { rows: out };
|
|
20837
|
+
const parts = [];
|
|
20838
|
+
if (capped > 0) parts.push(`${capped} id(s) left unlabelled (their *Name is null): the per-call cap of ${DEFAULT_MAX_NAME_READS} uncached name reads was hit \u2014 names already read are cached, so repeating or narrowing the call labels more`);
|
|
20839
|
+
if (Object.keys(reasons).length > 0) parts.push(`picklist label(s) unavailable for ${Object.keys(reasons).join(", ")} (see reasons)`);
|
|
20216
20840
|
return {
|
|
20217
|
-
|
|
20218
|
-
|
|
20219
|
-
type,
|
|
20220
|
-
isRequired: r.isRequired === true,
|
|
20221
|
-
isReadOnly: r.isReadOnly === true,
|
|
20222
|
-
// Issue #219 N7: a UDF row never serves the vendor's isQueryable (it is
|
|
20223
|
-
// true for UDFs while a plain filter on a UDF name is refused); it serves
|
|
20224
|
-
// the marker the filter actually needs.
|
|
20225
|
-
...udf ? { filterable: "udf-marker" } : { isQueryable: r.isQueryable === true },
|
|
20226
|
-
isPickList: r.isPickList === true,
|
|
20227
|
-
...isObjectOrArrayType(type) ? { writeShape: "replace-all" } : {},
|
|
20228
|
-
maxLength: typeof r.length === "number" ? r.length : null,
|
|
20229
|
-
referenceEntityType,
|
|
20230
|
-
targetResource,
|
|
20231
|
-
targetEnumerable: isEnumerableResource(targetResource),
|
|
20232
|
-
...values !== void 0 ? { picklistValues: values } : {}
|
|
20841
|
+
rows: out,
|
|
20842
|
+
names: { truncated: capped > 0, capped, note: parts.join("; "), ...Object.keys(reasons).length > 0 ? { reasons } : {} }
|
|
20233
20843
|
};
|
|
20234
20844
|
}
|
|
20235
|
-
function
|
|
20236
|
-
const
|
|
20237
|
-
|
|
20238
|
-
|
|
20239
|
-
|
|
20240
|
-
const
|
|
20241
|
-
|
|
20845
|
+
async function labelFetchResults(client, result) {
|
|
20846
|
+
const byResource = /* @__PURE__ */ new Map();
|
|
20847
|
+
result.results.forEach((r, i) => {
|
|
20848
|
+
if (r.found && r.value !== null) byResource.set(r.resource, [...byResource.get(r.resource) ?? [], i]);
|
|
20849
|
+
});
|
|
20850
|
+
const results = [...result.results];
|
|
20851
|
+
const summaries = [];
|
|
20852
|
+
for (const [resource, indices] of byResource) {
|
|
20853
|
+
const labelled = await labelRows(client, resource, indices.map((i) => results[i].value));
|
|
20854
|
+
indices.forEach((idx, j) => {
|
|
20855
|
+
results[idx] = { ...results[idx], value: labelled.rows[j] };
|
|
20856
|
+
});
|
|
20857
|
+
if (labelled.names !== void 0) summaries.push(labelled.names);
|
|
20858
|
+
}
|
|
20859
|
+
if (summaries.length === 0) return { ...result, results };
|
|
20860
|
+
const reasons = Object.assign({}, ...summaries.map((s) => s.reasons ?? {}));
|
|
20861
|
+
const names = {
|
|
20862
|
+
truncated: summaries.some((s) => s.truncated),
|
|
20863
|
+
capped: summaries.reduce((n, s) => n + s.capped, 0),
|
|
20864
|
+
note: summaries.map((s) => s.note).join("; "),
|
|
20865
|
+
...Object.keys(reasons).length > 0 ? { reasons } : {}
|
|
20866
|
+
};
|
|
20867
|
+
return { ...result, results, names };
|
|
20242
20868
|
}
|
|
20243
|
-
|
|
20244
|
-
|
|
20245
|
-
|
|
20246
|
-
|
|
20247
|
-
|
|
20248
|
-
|
|
20869
|
+
|
|
20870
|
+
// src/resources/billing_codes.ts
|
|
20871
|
+
var BillingCodesResource = class extends BaseResource {
|
|
20872
|
+
constructor(http, pageSize) {
|
|
20873
|
+
super(http, {
|
|
20874
|
+
key: "billing_codes",
|
|
20875
|
+
resourcePath: "/V1.0/BillingCodes",
|
|
20876
|
+
deletable: false,
|
|
20877
|
+
udfCapable: false,
|
|
20878
|
+
searchFields: ["name"]
|
|
20879
|
+
}, pageSize);
|
|
20249
20880
|
}
|
|
20250
|
-
|
|
20251
|
-
|
|
20252
|
-
|
|
20253
|
-
row.queryable = kept.filter((f) => f.isQueryable).map((f) => f.name);
|
|
20254
|
-
row.picklistFields = kept.filter((f) => f.isPickList).map((f) => f.name);
|
|
20255
|
-
}
|
|
20256
|
-
function capabilitySets() {
|
|
20257
|
-
const fieldOps = /* @__PURE__ */ new Map();
|
|
20258
|
-
const udfCapable = /* @__PURE__ */ new Set();
|
|
20259
|
-
const resources = /* @__PURE__ */ new Set();
|
|
20260
|
-
for (const rec of CAPABILITIES) {
|
|
20261
|
-
if (rec.resource === "operations") continue;
|
|
20262
|
-
resources.add(rec.resource);
|
|
20263
|
-
if (rec.operation === "getFieldInfo") fieldOps.set(rec.resource, rec);
|
|
20264
|
-
if (rec.udfCapable) udfCapable.add(rec.resource);
|
|
20881
|
+
/** Query BillingCode records with the G3 filter surface (server-side JSON filter). */
|
|
20882
|
+
query(params) {
|
|
20883
|
+
return this.queryItems(params);
|
|
20265
20884
|
}
|
|
20266
|
-
|
|
20267
|
-
|
|
20268
|
-
|
|
20269
|
-
function derivedFieldsFor(resource) {
|
|
20270
|
-
if (derivedIndex === null) {
|
|
20271
|
-
derivedIndex = /* @__PURE__ */ new Map();
|
|
20272
|
-
for (const rec of CAPABILITIES) {
|
|
20273
|
-
const declared = rec.metadata?.derivedFields;
|
|
20274
|
-
if (!Array.isArray(declared) || derivedIndex.has(rec.resource)) continue;
|
|
20275
|
-
const rows = [];
|
|
20276
|
-
for (const row of declared) {
|
|
20277
|
-
if (typeof row?.field !== "string" || typeof row.rule !== "string") continue;
|
|
20278
|
-
rows.push({
|
|
20279
|
-
field: row.field,
|
|
20280
|
-
rule: row.rule,
|
|
20281
|
-
...typeof row.writableRoute === "string" ? { writableRoute: row.writableRoute } : {}
|
|
20282
|
-
});
|
|
20283
|
-
}
|
|
20284
|
-
derivedIndex.set(rec.resource, rows);
|
|
20285
|
-
}
|
|
20885
|
+
/** Get every BillingCode record (the MCP-preferred read; 1000-page guarded). */
|
|
20886
|
+
async queryAll(params) {
|
|
20887
|
+
return this.queryAllItems(params);
|
|
20286
20888
|
}
|
|
20287
|
-
|
|
20288
|
-
|
|
20289
|
-
|
|
20290
|
-
const m = /^GET \/V1\.0\/([^/]+)\/entityInformation\/fields$/.exec(rec.endpoint ?? "");
|
|
20291
|
-
if (m === null) {
|
|
20292
|
-
throw new AutotaskConfigError(
|
|
20293
|
-
`fieldCatalog: the plan's getFieldInfo endpoint for "${rec.resource}" has no /V1.0/<Entity>/entityInformation/fields shape (${rec.endpoint}) \u2014 plan defect`,
|
|
20294
|
-
{ operation: "operations.fieldCatalog" }
|
|
20295
|
-
);
|
|
20889
|
+
/** Count matching records (queryCount envelope unwrapped to a plain number). */
|
|
20890
|
+
async count(params) {
|
|
20891
|
+
return this.countGlobal(params);
|
|
20296
20892
|
}
|
|
20297
|
-
|
|
20298
|
-
}
|
|
20299
|
-
|
|
20300
|
-
if (opts !== void 0 && (typeof opts !== "object" || opts === null || Array.isArray(opts))) {
|
|
20301
|
-
throw new AutotaskConfigError(
|
|
20302
|
-
"fieldCatalog: opts must be an object { resources?: string[], include?: (info|fields|udf)[], fresh?: boolean, fields?: string[], labels?: boolean }"
|
|
20303
|
-
);
|
|
20893
|
+
/** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
|
|
20894
|
+
async get(id, opts = {}) {
|
|
20895
|
+
return this.getGlobal(id, opts?.fresh === true);
|
|
20304
20896
|
}
|
|
20305
|
-
|
|
20306
|
-
|
|
20307
|
-
|
|
20308
|
-
|
|
20309
|
-
|
|
20310
|
-
|
|
20311
|
-
|
|
20897
|
+
/** Resolve a BillingCode from a numeric id (or, where the vendor indexes a primary text field,
|
|
20898
|
+
* from an exact match on name). A numeric id miss throws NOT_FOUND; a complete
|
|
20899
|
+
* non-match returns null; ambiguity throws RESOLUTION_AMBIGUOUS (candidate ids) and a
|
|
20900
|
+
* cap-tripped bounded client scan throws RESOLUTION_TRUNCATED (never a silent null).
|
|
20901
|
+
* The compact shape is the structural alias of the full record; `expand: true` is the
|
|
20902
|
+
* type-level escape hatch. */
|
|
20903
|
+
async resolve(identifier, opts = {}) {
|
|
20904
|
+
void opts;
|
|
20905
|
+
return this.resolveBase(identifier);
|
|
20312
20906
|
}
|
|
20313
|
-
|
|
20314
|
-
|
|
20315
|
-
|
|
20316
|
-
|
|
20317
|
-
throw new AutotaskConfigError("fieldCatalog: opts.include must be a non-empty array of (info|fields|udf)");
|
|
20907
|
+
/** Search BillingCode records: bounded contains on name (no vendor free-text search exists);
|
|
20908
|
+
* limit defaults to 25, max 500. */
|
|
20909
|
+
async search(query, opts) {
|
|
20910
|
+
return this.searchBase(query, { limit: opts?.limit });
|
|
20318
20911
|
}
|
|
20319
|
-
|
|
20320
|
-
|
|
20321
|
-
|
|
20322
|
-
|
|
20323
|
-
|
|
20324
|
-
|
|
20325
|
-
|
|
20326
|
-
|
|
20327
|
-
|
|
20912
|
+
};
|
|
20913
|
+
|
|
20914
|
+
// src/resources/special/dateWindow.ts
|
|
20915
|
+
var DAY_MS = 864e5;
|
|
20916
|
+
var DEFAULT_LIMIT = 500;
|
|
20917
|
+
var MAX_LIMIT = 500;
|
|
20918
|
+
function clampWindowLimit(limit) {
|
|
20919
|
+
const n = limit === void 0 ? DEFAULT_LIMIT : Math.trunc(limit);
|
|
20920
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
20921
|
+
throw new AutotaskConfigError(`limit must be an integer >= 1 (got ${String(limit)})`);
|
|
20328
20922
|
}
|
|
20329
|
-
return
|
|
20923
|
+
return Math.min(n, MAX_LIMIT);
|
|
20330
20924
|
}
|
|
20331
|
-
|
|
20332
|
-
|
|
20333
|
-
|
|
20334
|
-
|
|
20335
|
-
|
|
20336
|
-
|
|
20337
|
-
|
|
20338
|
-
|
|
20339
|
-
|
|
20340
|
-
|
|
20341
|
-
|
|
20342
|
-
|
|
20343
|
-
|
|
20344
|
-
|
|
20345
|
-
|
|
20346
|
-
|
|
20347
|
-
|
|
20348
|
-
|
|
20349
|
-
|
|
20350
|
-
);
|
|
20351
|
-
|
|
20352
|
-
|
|
20353
|
-
|
|
20354
|
-
|
|
20355
|
-
|
|
20356
|
-
|
|
20357
|
-
|
|
20358
|
-
|
|
20359
|
-
|
|
20925
|
+
var iso = (ms) => new Date(ms).toISOString().slice(0, 19) + "Z";
|
|
20926
|
+
var WEEKDAYS = { Sun: 0, Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6 };
|
|
20927
|
+
function wallParts(tz, ms) {
|
|
20928
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
20929
|
+
timeZone: tz,
|
|
20930
|
+
year: "numeric",
|
|
20931
|
+
month: "2-digit",
|
|
20932
|
+
day: "2-digit",
|
|
20933
|
+
hour: "2-digit",
|
|
20934
|
+
minute: "2-digit",
|
|
20935
|
+
second: "2-digit",
|
|
20936
|
+
hour12: false,
|
|
20937
|
+
weekday: "short"
|
|
20938
|
+
}).formatToParts(new Date(ms));
|
|
20939
|
+
const w = {};
|
|
20940
|
+
let weekday = -1;
|
|
20941
|
+
for (const p of parts) {
|
|
20942
|
+
switch (p.type) {
|
|
20943
|
+
case "year":
|
|
20944
|
+
w.y = Number(p.value);
|
|
20945
|
+
break;
|
|
20946
|
+
case "month":
|
|
20947
|
+
w.m = Number(p.value);
|
|
20948
|
+
break;
|
|
20949
|
+
case "day":
|
|
20950
|
+
w.d = Number(p.value);
|
|
20951
|
+
break;
|
|
20952
|
+
case "hour":
|
|
20953
|
+
w.h = Number(p.value) % 24;
|
|
20954
|
+
break;
|
|
20955
|
+
case "minute":
|
|
20956
|
+
w.mi = Number(p.value);
|
|
20957
|
+
break;
|
|
20958
|
+
case "second":
|
|
20959
|
+
w.s = Number(p.value);
|
|
20960
|
+
break;
|
|
20961
|
+
case "weekday":
|
|
20962
|
+
weekday = WEEKDAYS[p.value] ?? -1;
|
|
20963
|
+
break;
|
|
20360
20964
|
}
|
|
20361
|
-
if (!targets.includes(key)) targets.push(key);
|
|
20362
20965
|
}
|
|
20363
|
-
|
|
20364
|
-
}
|
|
20365
|
-
function resolveFieldsFilter(opts) {
|
|
20366
|
-
const fieldsRaw = opts?.fields;
|
|
20367
|
-
if (fieldsRaw === void 0) return void 0;
|
|
20368
|
-
if (!Array.isArray(fieldsRaw) || fieldsRaw.length === 0) {
|
|
20369
|
-
throw new AutotaskConfigError("fieldCatalog: opts.fields must be a non-empty array of vendor field name strings");
|
|
20966
|
+
if (weekday < 0 || [w.y, w.m, w.d, w.h, w.mi, w.s].some((v) => v === void 0 || Number.isNaN(v))) {
|
|
20967
|
+
throw new AutotaskConfigError(`dateWindow: could not read the wall-clock parts for timezone "${tz}" (the Intl format returned an incomplete or unmapped part)`);
|
|
20370
20968
|
}
|
|
20371
|
-
|
|
20372
|
-
|
|
20373
|
-
|
|
20374
|
-
|
|
20375
|
-
|
|
20376
|
-
|
|
20377
|
-
|
|
20378
|
-
|
|
20379
|
-
|
|
20969
|
+
return { y: w.y, m: w.m, d: w.d, h: w.h, mi: w.mi, s: w.s, weekday };
|
|
20970
|
+
}
|
|
20971
|
+
function tzOffsetMs(tz, ms) {
|
|
20972
|
+
const p = wallParts(tz, ms);
|
|
20973
|
+
return Date.UTC(p.y, p.m - 1, p.d, p.h, p.mi, p.s) - ms;
|
|
20974
|
+
}
|
|
20975
|
+
function localDayStartMs(y, m, d, tz) {
|
|
20976
|
+
if (tz === void 0) return Date.UTC(y, m - 1, d);
|
|
20977
|
+
const candidate2 = Date.UTC(y, m - 1, d);
|
|
20978
|
+
const offset = tzOffsetMs(tz, candidate2);
|
|
20979
|
+
let start = candidate2 - offset;
|
|
20980
|
+
if (tzOffsetMs(tz, start) !== offset) {
|
|
20981
|
+
start = candidate2 - tzOffsetMs(tz, start);
|
|
20380
20982
|
}
|
|
20381
|
-
|
|
20983
|
+
const p = wallParts(tz, start);
|
|
20984
|
+
if (p.d !== d) start += 864e5 - ((p.h * 60 + p.mi) * 60 + p.s) * 1e3;
|
|
20985
|
+
return start;
|
|
20382
20986
|
}
|
|
20383
|
-
function
|
|
20384
|
-
if (
|
|
20987
|
+
function localDayEndMs(y, m, d, tz) {
|
|
20988
|
+
if (tz === void 0) return Date.UTC(y, m - 1, d) + DAY_MS - 1;
|
|
20989
|
+
const next = new Date(Date.UTC(y, m - 1, d + 1));
|
|
20990
|
+
return localDayStartMs(next.getUTCFullYear(), next.getUTCMonth() + 1, next.getUTCDate(), tz) - 1;
|
|
20991
|
+
}
|
|
20992
|
+
function validateZone(tz, opName = "dateWindow") {
|
|
20993
|
+
if (typeof tz !== "string" || tz.length === 0) {
|
|
20385
20994
|
throw new AutotaskConfigError(
|
|
20386
|
-
|
|
20995
|
+
`${opName}: tz must be a non-empty IANA timezone string (got ${JSON.stringify(tz)}; e.g. 'Australia/Sydney' \u2014 omitted tz = UTC)`,
|
|
20996
|
+
{ operation: opName }
|
|
20387
20997
|
);
|
|
20388
20998
|
}
|
|
20389
|
-
|
|
20390
|
-
}
|
|
20391
|
-
|
|
20392
|
-
const cost = targetCount * kindCount;
|
|
20393
|
-
if (cost > MAX_FIELD_CATALOG_WIRE) {
|
|
20999
|
+
try {
|
|
21000
|
+
new Intl.DateTimeFormat("en-US", { timeZone: tz });
|
|
21001
|
+
} catch {
|
|
20394
21002
|
throw new AutotaskConfigError(
|
|
20395
|
-
|
|
20396
|
-
{
|
|
21003
|
+
`${opName}: tz is not a valid IANA timezone (${JSON.stringify(tz)}; e.g. 'Australia/Sydney' \u2014 omitted tz = UTC)`,
|
|
21004
|
+
{ operation: opName }
|
|
20397
21005
|
);
|
|
20398
21006
|
}
|
|
21007
|
+
return tz;
|
|
20399
21008
|
}
|
|
20400
|
-
|
|
20401
|
-
|
|
20402
|
-
|
|
20403
|
-
|
|
20404
|
-
|
|
20405
|
-
|
|
21009
|
+
var utcDayStart = (ms) => {
|
|
21010
|
+
const d = new Date(ms);
|
|
21011
|
+
return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
|
|
21012
|
+
};
|
|
21013
|
+
var utcDayEnd = (ms) => utcDayStart(ms) + DAY_MS - 1;
|
|
21014
|
+
function utcDateWindowBounds(kind, now) {
|
|
21015
|
+
const t = now.getTime();
|
|
21016
|
+
let from;
|
|
21017
|
+
let to;
|
|
21018
|
+
switch (kind) {
|
|
21019
|
+
case "today":
|
|
21020
|
+
from = utcDayStart(t);
|
|
21021
|
+
to = utcDayEnd(t);
|
|
21022
|
+
break;
|
|
21023
|
+
case "yesterday":
|
|
21024
|
+
from = utcDayStart(t - DAY_MS);
|
|
21025
|
+
to = utcDayEnd(t - DAY_MS);
|
|
21026
|
+
break;
|
|
21027
|
+
case "last24h":
|
|
21028
|
+
from = t - DAY_MS;
|
|
21029
|
+
to = t;
|
|
21030
|
+
break;
|
|
21031
|
+
case "last7":
|
|
21032
|
+
case "last14":
|
|
21033
|
+
case "last30":
|
|
21034
|
+
case "last90": {
|
|
21035
|
+
const days = Number(kind.slice(4));
|
|
21036
|
+
from = utcDayStart(t - days * DAY_MS);
|
|
21037
|
+
to = utcDayEnd(t);
|
|
21038
|
+
break;
|
|
21039
|
+
}
|
|
21040
|
+
case "lastFullWeek":
|
|
21041
|
+
case "lastFullFortnight": {
|
|
21042
|
+
const d = new Date(t);
|
|
21043
|
+
const daysSinceMonday = (d.getUTCDay() + 6) % 7;
|
|
21044
|
+
const thisMonday = utcDayStart(t) - daysSinceMonday * DAY_MS;
|
|
21045
|
+
const daysBack = kind === "lastFullWeek" ? 7 : 14;
|
|
21046
|
+
from = thisMonday - daysBack * DAY_MS;
|
|
21047
|
+
to = thisMonday - 1;
|
|
21048
|
+
break;
|
|
21049
|
+
}
|
|
21050
|
+
case "lastFullMonth": {
|
|
21051
|
+
const d = new Date(t);
|
|
21052
|
+
const y = d.getUTCFullYear();
|
|
21053
|
+
const m = d.getUTCMonth() - 1;
|
|
21054
|
+
from = Date.UTC(y, m, 1);
|
|
21055
|
+
to = Date.UTC(y, m + 1, 1) - 1;
|
|
21056
|
+
break;
|
|
21057
|
+
}
|
|
21058
|
+
case "lastFullQuarter": {
|
|
21059
|
+
const d = new Date(t);
|
|
21060
|
+
let q = Math.floor(d.getUTCMonth() / 3) - 1;
|
|
21061
|
+
let y = d.getUTCFullYear();
|
|
21062
|
+
if (q < 0) {
|
|
21063
|
+
q = 3;
|
|
21064
|
+
y -= 1;
|
|
21065
|
+
}
|
|
21066
|
+
const first = q * 3;
|
|
21067
|
+
from = Date.UTC(y, first, 1);
|
|
21068
|
+
to = Date.UTC(y, first + 3, 1) - 1;
|
|
21069
|
+
break;
|
|
21070
|
+
}
|
|
21071
|
+
}
|
|
21072
|
+
return { from: iso(from), to: iso(to) };
|
|
20406
21073
|
}
|
|
20407
|
-
|
|
20408
|
-
|
|
20409
|
-
const
|
|
20410
|
-
const
|
|
20411
|
-
if (
|
|
20412
|
-
|
|
20413
|
-
|
|
20414
|
-
const
|
|
20415
|
-
|
|
20416
|
-
|
|
20417
|
-
|
|
20418
|
-
|
|
20419
|
-
...ctx.udfCapable.has(resource) ? { udfFilter: "udf-marker", udfWriteShape: "merge" } : {},
|
|
20420
|
-
unmatchedFields: [],
|
|
20421
|
-
required: [],
|
|
20422
|
-
readOnly: [],
|
|
20423
|
-
queryable: [],
|
|
20424
|
-
picklistFields: [],
|
|
20425
|
-
cache: { hit: false, age: null },
|
|
20426
|
-
wire: 0,
|
|
20427
|
-
error: null
|
|
21074
|
+
function dateWindowBounds(kind, now, tz) {
|
|
21075
|
+
if (tz === void 0) return utcDateWindowBounds(kind, now);
|
|
21076
|
+
const zone = validateZone(tz);
|
|
21077
|
+
const t = now.getTime();
|
|
21078
|
+
if (kind === "last24h") {
|
|
21079
|
+
return { from: iso(t - DAY_MS), to: iso(t) };
|
|
21080
|
+
}
|
|
21081
|
+
const { y, m, d, weekday } = wallParts(zone, t);
|
|
21082
|
+
const shifted = (dy) => new Date(Date.UTC(y, m - 1, d + dy));
|
|
21083
|
+
const dayStart = (dy) => {
|
|
21084
|
+
const dt = shifted(dy);
|
|
21085
|
+
return localDayStartMs(dt.getUTCFullYear(), dt.getUTCMonth() + 1, dt.getUTCDate(), zone);
|
|
20428
21086
|
};
|
|
20429
|
-
const
|
|
20430
|
-
|
|
20431
|
-
|
|
20432
|
-
const projected = list.map((f) => projectFieldRow(f, labels, kind === "udf"));
|
|
20433
|
-
if (kind === "fields") applyFieldsFilter(projected, fieldsFilter, row);
|
|
20434
|
-
else row.udf = projected;
|
|
20435
|
-
for (const f of list) if (exposesPicklistValues(f)) tally.anyPicklistValues = true;
|
|
21087
|
+
const dayStartAt = (instant) => {
|
|
21088
|
+
const p = wallParts(zone, instant);
|
|
21089
|
+
return localDayStartMs(p.y, p.m, p.d, zone);
|
|
20436
21090
|
};
|
|
20437
|
-
|
|
20438
|
-
|
|
20439
|
-
|
|
20440
|
-
|
|
20441
|
-
|
|
20442
|
-
|
|
20443
|
-
|
|
20444
|
-
|
|
20445
|
-
|
|
20446
|
-
|
|
20447
|
-
|
|
20448
|
-
|
|
20449
|
-
|
|
20450
|
-
|
|
20451
|
-
|
|
20452
|
-
|
|
20453
|
-
|
|
20454
|
-
|
|
20455
|
-
|
|
20456
|
-
|
|
20457
|
-
|
|
21091
|
+
const dayEndAt = (instant) => {
|
|
21092
|
+
const p = wallParts(zone, instant);
|
|
21093
|
+
return localDayEndMs(p.y, p.m, p.d, zone);
|
|
21094
|
+
};
|
|
21095
|
+
let from;
|
|
21096
|
+
let to;
|
|
21097
|
+
switch (kind) {
|
|
21098
|
+
case "today":
|
|
21099
|
+
from = dayStartAt(t);
|
|
21100
|
+
to = dayEndAt(t);
|
|
21101
|
+
break;
|
|
21102
|
+
case "yesterday":
|
|
21103
|
+
from = dayStart(-1);
|
|
21104
|
+
to = dayStart(0) - 1;
|
|
21105
|
+
break;
|
|
21106
|
+
case "last7":
|
|
21107
|
+
case "last14":
|
|
21108
|
+
case "last30":
|
|
21109
|
+
case "last90": {
|
|
21110
|
+
const days = Number(kind.slice(4));
|
|
21111
|
+
from = dayStart(-days);
|
|
21112
|
+
to = dayEndAt(t);
|
|
21113
|
+
break;
|
|
20458
21114
|
}
|
|
20459
|
-
|
|
20460
|
-
|
|
20461
|
-
|
|
20462
|
-
|
|
20463
|
-
|
|
20464
|
-
|
|
20465
|
-
|
|
20466
|
-
continue;
|
|
20467
|
-
}
|
|
20468
|
-
projectList(kind, value);
|
|
21115
|
+
case "lastFullWeek":
|
|
21116
|
+
case "lastFullFortnight": {
|
|
21117
|
+
const daysSinceMonday = (weekday + 6) % 7;
|
|
21118
|
+
const daysBack = kind === "lastFullWeek" ? 7 : 14;
|
|
21119
|
+
from = dayStart(-(daysSinceMonday + daysBack));
|
|
21120
|
+
to = dayStart(-daysSinceMonday) - 1;
|
|
21121
|
+
break;
|
|
20469
21122
|
}
|
|
20470
|
-
|
|
20471
|
-
|
|
20472
|
-
|
|
20473
|
-
|
|
20474
|
-
|
|
20475
|
-
row.derivedFields = declared;
|
|
20476
|
-
for (const field of row.fields ?? []) {
|
|
20477
|
-
const derivation = byField.get(field.name);
|
|
20478
|
-
if (derivation === void 0) continue;
|
|
20479
|
-
field.derived = {
|
|
20480
|
-
rule: derivation.rule,
|
|
20481
|
-
...derivation.writableRoute !== void 0 ? { writableRoute: derivation.writableRoute } : {}
|
|
20482
|
-
};
|
|
21123
|
+
case "lastFullMonth": {
|
|
21124
|
+
const prev = new Date(Date.UTC(y, m - 2, 1));
|
|
21125
|
+
from = localDayStartMs(prev.getUTCFullYear(), prev.getUTCMonth() + 1, 1, zone);
|
|
21126
|
+
to = localDayStartMs(y, m, 1, zone) - 1;
|
|
21127
|
+
break;
|
|
20483
21128
|
}
|
|
20484
|
-
|
|
20485
|
-
|
|
20486
|
-
|
|
20487
|
-
|
|
20488
|
-
|
|
20489
|
-
|
|
20490
|
-
|
|
20491
|
-
const targets = resolveTargets(opts, fieldOps, allResources);
|
|
20492
|
-
const fieldsFilter = resolveFieldsFilter(opts);
|
|
20493
|
-
const labels = resolveLabels(opts);
|
|
20494
|
-
assertWithinWireCap(targets.length, include.length);
|
|
20495
|
-
const cache = client.http.fieldCache;
|
|
20496
|
-
const zone = client.config.zone ?? "unknown";
|
|
20497
|
-
const rows = [];
|
|
20498
|
-
const tally = { wireUsed: 0, anyPicklistValues: false };
|
|
20499
|
-
const ctx = { client, include, fieldsFilter, labels, fresh: opts?.fresh === true, fieldOps, udfCapable };
|
|
20500
|
-
for (const resource of targets) {
|
|
20501
|
-
const row = await sweepResource(ctx, resource, tally);
|
|
20502
|
-
if (row !== void 0) rows.push(row);
|
|
20503
|
-
}
|
|
20504
|
-
return {
|
|
20505
|
-
zone,
|
|
20506
|
-
label: zone,
|
|
20507
|
-
cached: cache !== void 0,
|
|
20508
|
-
labels,
|
|
20509
|
-
ttlMs: cache?.ttlMs ?? null,
|
|
20510
|
-
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
20511
|
-
rows,
|
|
20512
|
-
wireUsed: tally.wireUsed,
|
|
20513
|
-
cap: MAX_FIELD_CATALOG_WIRE,
|
|
20514
|
-
truncated: false,
|
|
20515
|
-
counts: {
|
|
20516
|
-
resources: targets.length,
|
|
20517
|
-
fieldCapableResources: fieldOps.size,
|
|
20518
|
-
udfCapableResources: udfCapable.size,
|
|
20519
|
-
cached: rows.filter((r) => r.cache.hit === true).length
|
|
20520
|
-
},
|
|
20521
|
-
honesty: {
|
|
20522
|
-
picklistValues: tally.anyPicklistValues ? "partial" : "unexposed",
|
|
20523
|
-
note: FIELD_CATALOG_HONESTY_NOTE
|
|
21129
|
+
case "lastFullQuarter": {
|
|
21130
|
+
const q = Math.floor((m - 1) / 3);
|
|
21131
|
+
const prevQ = q - 1 < 0 ? 3 : q - 1;
|
|
21132
|
+
const prevY = q - 1 < 0 ? y - 1 : y;
|
|
21133
|
+
from = localDayStartMs(prevY, prevQ * 3 + 1, 1, zone);
|
|
21134
|
+
to = localDayStartMs(y, q * 3 + 1, 1, zone) - 1;
|
|
21135
|
+
break;
|
|
20524
21136
|
}
|
|
20525
|
-
}
|
|
21137
|
+
}
|
|
21138
|
+
return { from: iso(from), to: iso(to) };
|
|
20526
21139
|
}
|
|
20527
|
-
|
|
20528
|
-
|
|
20529
|
-
|
|
20530
|
-
|
|
20531
|
-
|
|
20532
|
-
|
|
20533
|
-
|
|
20534
|
-
if (typeof value === "string") return /^\d+$/.test(value);
|
|
20535
|
-
return false;
|
|
21140
|
+
function dateOnlyWindowBounds(kind, now, tz) {
|
|
21141
|
+
if (tz === void 0 || kind === "last24h") return dateWindowBounds(kind, now, tz);
|
|
21142
|
+
const zone = validateZone(tz);
|
|
21143
|
+
const { from, to } = dateWindowBounds(kind, now, zone);
|
|
21144
|
+
const f = wallParts(zone, Date.parse(from));
|
|
21145
|
+
const t = wallParts(zone, Date.parse(to));
|
|
21146
|
+
return { from: iso(Date.UTC(f.y, f.m - 1, f.d)), to: iso(Date.UTC(t.y, t.m - 1, t.d) + DAY_MS - 1) };
|
|
20536
21147
|
}
|
|
20537
|
-
function
|
|
20538
|
-
const
|
|
20539
|
-
const
|
|
20540
|
-
const
|
|
20541
|
-
|
|
20542
|
-
|
|
20543
|
-
|
|
20544
|
-
|
|
20545
|
-
|
|
20546
|
-
|
|
20547
|
-
|
|
20548
|
-
|
|
20549
|
-
|
|
20550
|
-
|
|
20551
|
-
|
|
20552
|
-
...lineOfBusinessContractId !== void 0 ? { lineOfBusinessContractId } : {}
|
|
20553
|
-
}),
|
|
20554
|
-
addResource: (value) => {
|
|
20555
|
-
if (!isVendorId(value)) return;
|
|
20556
|
-
const k = String(value);
|
|
20557
|
-
if (!resSeen.has(k)) {
|
|
20558
|
-
resSeen.add(k);
|
|
20559
|
-
resourceIds.push(value);
|
|
20560
|
-
}
|
|
20561
|
-
},
|
|
20562
|
-
addFk: (table, value) => {
|
|
20563
|
-
if (!isVendorId(value)) return;
|
|
20564
|
-
const k = `${table}:${String(value)}`;
|
|
20565
|
-
if (!fkSeen.has(k)) {
|
|
20566
|
-
fkSeen.add(k);
|
|
20567
|
-
recordFks.push({ table, id: value });
|
|
20568
|
-
}
|
|
20569
|
-
},
|
|
20570
|
-
addLineOfBusinessFromContract: (value) => {
|
|
20571
|
-
if (!isVendorId(value)) return;
|
|
20572
|
-
lineOfBusinessContractId = value;
|
|
20573
|
-
},
|
|
20574
|
-
addLineOfBusiness: (value, origin) => {
|
|
20575
|
-
if (!isVendorId(value)) return;
|
|
20576
|
-
lineOfBusinessId = value;
|
|
20577
|
-
if (origin !== void 0) {
|
|
20578
|
-
lineOfBusinessSource = origin.source;
|
|
20579
|
-
if (origin.contractId !== void 0) lineOfBusinessContractId = origin.contractId;
|
|
20580
|
-
}
|
|
20581
|
-
}
|
|
20582
|
-
};
|
|
21148
|
+
async function dateWindowQuery(http, path, field, kind, limit, operation, now, tz) {
|
|
21149
|
+
const zone = tz === void 0 ? void 0 : validateZone(tz, operation);
|
|
21150
|
+
const { from, to } = dateOnlyWindowBounds(kind, now, zone);
|
|
21151
|
+
const filter = [
|
|
21152
|
+
{ field, op: "gte", value: from },
|
|
21153
|
+
{ field, op: "lte", value: to }
|
|
21154
|
+
];
|
|
21155
|
+
const res = await http.request({
|
|
21156
|
+
method: "POST",
|
|
21157
|
+
idempotent: true,
|
|
21158
|
+
path,
|
|
21159
|
+
body: { MaxRecords: limit, filter },
|
|
21160
|
+
operation
|
|
21161
|
+
});
|
|
21162
|
+
return unwrapItems(res).items;
|
|
20583
21163
|
}
|
|
20584
|
-
|
|
20585
|
-
|
|
20586
|
-
|
|
20587
|
-
|
|
21164
|
+
var isoUtc = (ms) => new Date(ms).toISOString().slice(0, 19) + "Z";
|
|
21165
|
+
function customRangeDay(value, label, zone, opName) {
|
|
21166
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value.trim());
|
|
21167
|
+
if (!m) {
|
|
21168
|
+
throw new AutotaskConfigError(
|
|
21169
|
+
zone === void 0 ? `${opName}: ${label} must be a UTC 'YYYY-MM-DD' date (got ${JSON.stringify(value)})` : `${opName}: ${label} must be a 'YYYY-MM-DD' local calendar day in ${zone} (got ${JSON.stringify(value)})`,
|
|
21170
|
+
{ operation: opName }
|
|
21171
|
+
);
|
|
21172
|
+
}
|
|
21173
|
+
const y = Number(m[1]);
|
|
21174
|
+
const mo = Number(m[2]);
|
|
21175
|
+
const d = Number(m[3]);
|
|
21176
|
+
const probe = new Date(Date.UTC(y, mo - 1, d));
|
|
21177
|
+
const real = probe.getUTCFullYear() === y && probe.getUTCMonth() === mo - 1 && probe.getUTCDate() === d;
|
|
21178
|
+
const ms = !real ? NaN : zone === void 0 ? Date.UTC(y, mo - 1, d) : localDayStartMs(y, mo, d, zone);
|
|
21179
|
+
if (!Number.isFinite(ms)) {
|
|
21180
|
+
throw new AutotaskConfigError(
|
|
21181
|
+
zone === void 0 ? `${opName}: ${label} is not a valid UTC date (${JSON.stringify(value)})` : `${opName}: ${label} is not a valid calendar day in ${zone} (${JSON.stringify(value)})`,
|
|
21182
|
+
{ operation: opName }
|
|
21183
|
+
);
|
|
21184
|
+
}
|
|
21185
|
+
return { y, mo, d, ms };
|
|
20588
21186
|
}
|
|
20589
|
-
function
|
|
20590
|
-
|
|
20591
|
-
|
|
20592
|
-
|
|
21187
|
+
async function customRangeQuery(http, path, field, from, to, limit, operation, zone) {
|
|
21188
|
+
if (zone !== void 0) validateZone(zone, operation);
|
|
21189
|
+
const fromB = customRangeDay(from, "from", void 0, operation);
|
|
21190
|
+
const toB = customRangeDay(to, "to", void 0, operation);
|
|
21191
|
+
if (fromB.ms > toB.ms) {
|
|
21192
|
+
throw new AutotaskConfigError(
|
|
21193
|
+
`${operation}: from (${isoUtc(fromB.ms)}) must not be after to (${isoUtc(toB.ms)})`,
|
|
21194
|
+
{ operation }
|
|
21195
|
+
);
|
|
21196
|
+
}
|
|
21197
|
+
const toEndMs = toB.ms + DAY_MS - 1;
|
|
21198
|
+
const filter = [
|
|
21199
|
+
{ field, op: "gte", value: isoUtc(fromB.ms) },
|
|
21200
|
+
{ field, op: "lte", value: isoUtc(toEndMs) }
|
|
21201
|
+
];
|
|
21202
|
+
const res = await http.request({
|
|
21203
|
+
method: "POST",
|
|
21204
|
+
idempotent: true,
|
|
21205
|
+
path,
|
|
21206
|
+
body: { MaxRecords: limit, filter },
|
|
21207
|
+
operation
|
|
21208
|
+
});
|
|
21209
|
+
return unwrapItems(res).items;
|
|
20593
21210
|
}
|
|
20594
|
-
|
|
20595
|
-
|
|
20596
|
-
|
|
20597
|
-
|
|
20598
|
-
};
|
|
20599
|
-
|
|
20600
|
-
|
|
20601
|
-
|
|
20602
|
-
|
|
20603
|
-
|
|
20604
|
-
|
|
20605
|
-
|
|
20606
|
-
|
|
21211
|
+
|
|
21212
|
+
// src/resources/special/billing_items.ts
|
|
21213
|
+
async function windowQuery(_resource, kind, http, opts) {
|
|
21214
|
+
const now = opts?.now ?? /* @__PURE__ */ new Date();
|
|
21215
|
+
const tz = opts?.tz === void 0 ? void 0 : validateZone(opts.tz, `billing_items.${kind}`);
|
|
21216
|
+
return dateWindowQuery(
|
|
21217
|
+
http,
|
|
21218
|
+
"/V1.0/BillingItems/query",
|
|
21219
|
+
"itemDate",
|
|
21220
|
+
kind,
|
|
21221
|
+
clampWindowLimit(opts?.limit),
|
|
21222
|
+
`billing_items.${kind}`,
|
|
21223
|
+
now,
|
|
21224
|
+
tz
|
|
21225
|
+
);
|
|
20607
21226
|
}
|
|
20608
|
-
function
|
|
20609
|
-
const
|
|
20610
|
-
return
|
|
21227
|
+
async function customRange(_resource, from, to, http, opts) {
|
|
21228
|
+
const zone = opts?.tz === void 0 ? void 0 : validateZone(opts.tz, "billing_items.customRange");
|
|
21229
|
+
return customRangeQuery(
|
|
21230
|
+
http,
|
|
21231
|
+
"/V1.0/BillingItems/query",
|
|
21232
|
+
"itemDate",
|
|
21233
|
+
from,
|
|
21234
|
+
to,
|
|
21235
|
+
clampWindowLimit(opts?.limit),
|
|
21236
|
+
"billing_items.customRange",
|
|
21237
|
+
zone
|
|
21238
|
+
);
|
|
20611
21239
|
}
|
|
20612
|
-
|
|
20613
|
-
|
|
20614
|
-
|
|
20615
|
-
|
|
20616
|
-
|
|
21240
|
+
|
|
21241
|
+
// src/resources/billing_items.ts
|
|
21242
|
+
var BillingItemsResource = class extends BaseResource {
|
|
21243
|
+
constructor(http, pageSize) {
|
|
21244
|
+
super(http, {
|
|
21245
|
+
key: "billing_items",
|
|
21246
|
+
resourcePath: "/V1.0/BillingItems",
|
|
21247
|
+
deletable: false,
|
|
21248
|
+
udfCapable: false,
|
|
21249
|
+
collectionUpdate: true
|
|
21250
|
+
// D1: the spec's item path is GET-only — confirmed updates PUT the collection (id in the body)
|
|
21251
|
+
}, pageSize);
|
|
20617
21252
|
}
|
|
20618
|
-
|
|
20619
|
-
|
|
20620
|
-
|
|
20621
|
-
const operation = options.operation ?? "compositeNames";
|
|
20622
|
-
const degrade = options.onError === "degrade";
|
|
20623
|
-
const get = (path) => {
|
|
20624
|
-
const res = http.request({ method: "GET", path, operation }).then((body) => {
|
|
20625
|
-
const b = body;
|
|
20626
|
-
if (b === null || !("item" in b) || b.item === null || b.item === void 0) return null;
|
|
20627
|
-
return b.item;
|
|
20628
|
-
});
|
|
20629
|
-
if (!degrade) {
|
|
20630
|
-
return res.catch((e) => {
|
|
20631
|
-
if (e instanceof NotFoundError) return null;
|
|
20632
|
-
throw e;
|
|
20633
|
-
});
|
|
20634
|
-
}
|
|
20635
|
-
return res.catch((e) => {
|
|
20636
|
-
if (e instanceof NotFoundError || isAutotaskError(e) && e.code !== "ABORTED") return null;
|
|
20637
|
-
throw e;
|
|
20638
|
-
});
|
|
20639
|
-
};
|
|
20640
|
-
let remaining = options.budget;
|
|
20641
|
-
if (remaining < 1) {
|
|
20642
|
-
return {
|
|
20643
|
-
lineOfBusiness: { association: null, organizationalLevel2: null, organizationalLevel1: null, markers: { association: "capped", organizationalLevel2: "capped", organizationalLevel1: "capped" } },
|
|
20644
|
-
used: 0,
|
|
20645
|
-
truncated: true
|
|
20646
|
-
};
|
|
21253
|
+
/** Query BillingItems records with the G3 filter surface (server-side JSON filter). */
|
|
21254
|
+
query(params) {
|
|
21255
|
+
return this.queryItems(params);
|
|
20647
21256
|
}
|
|
20648
|
-
|
|
20649
|
-
|
|
20650
|
-
|
|
20651
|
-
if (association === null) {
|
|
20652
|
-
return {
|
|
20653
|
-
lineOfBusiness: { association: null, organizationalLevel2: null, organizationalLevel1: null, markers: { association: associationMarker, organizationalLevel2: "missing", organizationalLevel1: "missing" } },
|
|
20654
|
-
used: 1,
|
|
20655
|
-
truncated: false
|
|
20656
|
-
};
|
|
21257
|
+
/** Get every BillingItems record (the MCP-preferred read; 1000-page guarded). */
|
|
21258
|
+
async queryAll(params) {
|
|
21259
|
+
return this.queryAllItems(params);
|
|
20657
21260
|
}
|
|
20658
|
-
|
|
20659
|
-
|
|
20660
|
-
|
|
20661
|
-
let l1Marker = l1Id !== void 0 ? "unresolved" : "missing";
|
|
20662
|
-
const need = (l2Id !== void 0 ? 1 : 0) + (l1Id !== void 0 ? 1 : 0);
|
|
20663
|
-
let l2 = null;
|
|
20664
|
-
let l1 = null;
|
|
20665
|
-
let used = 1;
|
|
20666
|
-
let truncated = false;
|
|
20667
|
-
if (need > 0 && remaining < need) {
|
|
20668
|
-
truncated = true;
|
|
20669
|
-
if (l2Id !== void 0) l2Marker = "capped";
|
|
20670
|
-
if (l1Id !== void 0) l1Marker = "capped";
|
|
20671
|
-
} else {
|
|
20672
|
-
used += need;
|
|
20673
|
-
const [r2, r1] = await Promise.all([
|
|
20674
|
-
l2Id !== void 0 ? get(`/V1.0/OrganizationalLevel2s/${l2Id}`) : Promise.resolve(null),
|
|
20675
|
-
l1Id !== void 0 ? get(`/V1.0/OrganizationalLevel1s/${l1Id}`) : Promise.resolve(null)
|
|
20676
|
-
]);
|
|
20677
|
-
l2 = r2;
|
|
20678
|
-
l1 = r1;
|
|
20679
|
-
if (l2Id !== void 0) l2Marker = l2 === null ? "unresolved" : "ok";
|
|
20680
|
-
if (l1Id !== void 0) l1Marker = l1 === null ? "unresolved" : "ok";
|
|
21261
|
+
/** Count matching records (queryCount envelope unwrapped to a plain number). */
|
|
21262
|
+
async count(params) {
|
|
21263
|
+
return this.countGlobal(params);
|
|
20681
21264
|
}
|
|
20682
|
-
|
|
20683
|
-
|
|
20684
|
-
|
|
20685
|
-
truncated
|
|
20686
|
-
};
|
|
20687
|
-
}
|
|
20688
|
-
async function resolveCompositeNames(http, plan, opts = {}) {
|
|
20689
|
-
if (opts.maxNameReads !== void 0 && (!Number.isInteger(opts.maxNameReads) || opts.maxNameReads < 1)) {
|
|
20690
|
-
throw new AutotaskConfigError(`composite names: maxNameReads must be an integer >= 1 (got ${String(opts.maxNameReads)})`);
|
|
21265
|
+
/** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
|
|
21266
|
+
async get(id, opts = {}) {
|
|
21267
|
+
return this.getGlobal(id, opts?.fresh === true);
|
|
20691
21268
|
}
|
|
20692
|
-
|
|
20693
|
-
|
|
20694
|
-
|
|
20695
|
-
|
|
21269
|
+
/** Update a record (PUT on the COLLECTION — the vendored spec's item path is GET-only; the vendor
|
|
21270
|
+
* keys the record by the id in the body). The vendor validates the FULL model on this route (V2), so a
|
|
21271
|
+
* partial body is merged into the CURRENT record first: one fresh GET (which also serves the
|
|
21272
|
+
* stale-object guard's read) then the collection PUT of the merged model — an explicit null in the
|
|
21273
|
+
* caller body sends null; an omitted field keeps the current value. `raw: true` in the opts bag sends
|
|
21274
|
+
* the body as-is (full model or intentional partial — the compatibility escape hatch; no merge read).
|
|
21275
|
+
* `dryRun: true` returns the pinned envelope whose `data` is the merged body (the exact confirmed
|
|
21276
|
+
* wire) with the best-effort diff, from that same single read; a caller-supplied lastModifiedDate that
|
|
21277
|
+
* mismatches the vendor's current value throws STALE_OBJECT before the mutation. */
|
|
21278
|
+
async update(id, data, opts = {}) {
|
|
21279
|
+
if (opts.dryRun === true) return this.runUpdateDryRun(id, data, opts);
|
|
21280
|
+
return this.updateGlobal(id, data, opts);
|
|
20696
21281
|
}
|
|
20697
|
-
|
|
20698
|
-
|
|
20699
|
-
|
|
20700
|
-
|
|
20701
|
-
|
|
20702
|
-
|
|
20703
|
-
|
|
20704
|
-
|
|
20705
|
-
|
|
20706
|
-
|
|
20707
|
-
coldResource.push(id);
|
|
21282
|
+
/** Resolve a BillingItems from a numeric id (or, where the vendor indexes a primary text field,
|
|
21283
|
+
* from a numeric id — this resource has no primary text field). A numeric id miss throws NOT_FOUND;
|
|
21284
|
+
* a bare value rejects with a config error (ids only);
|
|
21285
|
+
* non-match returns null; ambiguity throws RESOLUTION_AMBIGUOUS (candidate ids) and a
|
|
21286
|
+
* cap-tripped bounded client scan throws RESOLUTION_TRUNCATED (never a silent null).
|
|
21287
|
+
* The compact shape is the structural alias of the full record; `expand: true` is the
|
|
21288
|
+
* type-level escape hatch. */
|
|
21289
|
+
async resolve(identifier, opts = {}) {
|
|
21290
|
+
void opts;
|
|
21291
|
+
return this.resolveBase(identifier);
|
|
20708
21292
|
}
|
|
20709
|
-
|
|
20710
|
-
|
|
20711
|
-
|
|
20712
|
-
for (const id of overBudgetResource) {
|
|
20713
|
-
section.rows[`resource:${id}`] = { name: null, blind: false, capped: true };
|
|
20714
|
-
section.truncated = true;
|
|
21293
|
+
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
21294
|
+
customRange(from, to, opts) {
|
|
21295
|
+
return customRange(this, from, to, this.http, opts);
|
|
20715
21296
|
}
|
|
20716
|
-
|
|
20717
|
-
|
|
20718
|
-
|
|
20719
|
-
let pass = opIds;
|
|
20720
|
-
if (pass.length > 500) {
|
|
20721
|
-
for (const id of pass.slice(500)) {
|
|
20722
|
-
section.rows[`resource:${id}`] = { name: null, blind: false, capped: true };
|
|
20723
|
-
section.truncated = true;
|
|
20724
|
-
}
|
|
20725
|
-
pass = pass.slice(0, 500);
|
|
20726
|
-
}
|
|
20727
|
-
const res = await expandResourceNames(client, pass);
|
|
20728
|
-
for (const r of res.results) {
|
|
20729
|
-
const row = { name: r.name, blind: r.blind, capped: false };
|
|
20730
|
-
if (r.error !== void 0) row.error = faultRow(r.error);
|
|
20731
|
-
section.rows[`resource:${r.id}`] = row;
|
|
20732
|
-
}
|
|
21297
|
+
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
21298
|
+
last14(opts) {
|
|
21299
|
+
return windowQuery(this, "last14", this.http, opts);
|
|
20733
21300
|
}
|
|
20734
|
-
|
|
20735
|
-
|
|
20736
|
-
|
|
20737
|
-
contract: client.contracts
|
|
20738
|
-
};
|
|
20739
|
-
for (const fk of plan.recordFks) {
|
|
20740
|
-
const key = `${fk.table}:${fk.id}`;
|
|
20741
|
-
const cold = readCache === void 0 || readCache.get(`${FK_TABLE_PATH[fk.table]}/${fk.id}`) === void 0;
|
|
20742
|
-
if (cold) {
|
|
20743
|
-
if (remaining < 1) {
|
|
20744
|
-
section.rows[key] = { name: null, blind: false, capped: true };
|
|
20745
|
-
section.truncated = true;
|
|
20746
|
-
if (fk.table === "contract" && plan.lineOfBusinessContractId !== void 0 && String(fk.id) === String(plan.lineOfBusinessContractId)) {
|
|
20747
|
-
contractLobUnread = "capped";
|
|
20748
|
-
}
|
|
20749
|
-
continue;
|
|
20750
|
-
}
|
|
20751
|
-
remaining -= 1;
|
|
20752
|
-
}
|
|
20753
|
-
try {
|
|
20754
|
-
const record = await holders[fk.table].get(fk.id);
|
|
20755
|
-
if (fk.table === "contract" && plan.lineOfBusinessId === void 0 && plan.lineOfBusinessContractId !== void 0 && String(fk.id) === String(plan.lineOfBusinessContractId)) {
|
|
20756
|
-
contractLobPointer = vendorId(record["organizationalLevelAssociationID"]);
|
|
20757
|
-
}
|
|
20758
|
-
section.rows[key] = { name: fkName(record, fk.table), blind: false, capped: false };
|
|
20759
|
-
} catch (err) {
|
|
20760
|
-
if (err instanceof NotFoundError) {
|
|
20761
|
-
section.rows[key] = { name: null, blind: true, capped: false };
|
|
20762
|
-
} else if (isAutotaskError(err) && err.code !== "ABORTED") {
|
|
20763
|
-
section.rows[key] = { name: null, blind: false, capped: false, error: faultRow(err) };
|
|
20764
|
-
if (fk.table === "contract" && plan.lineOfBusinessContractId !== void 0 && String(fk.id) === String(plan.lineOfBusinessContractId)) {
|
|
20765
|
-
contractLobUnread = "unresolved";
|
|
20766
|
-
}
|
|
20767
|
-
} else {
|
|
20768
|
-
throw err;
|
|
20769
|
-
}
|
|
20770
|
-
}
|
|
21301
|
+
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
21302
|
+
last24h(opts) {
|
|
21303
|
+
return windowQuery(this, "last24h", this.http, opts);
|
|
20771
21304
|
}
|
|
20772
|
-
|
|
20773
|
-
|
|
20774
|
-
|
|
20775
|
-
try {
|
|
20776
|
-
const catalog = await fieldCatalog(client, { resources: ["tickets"], labels: true, fields: [...plan.labelFields] });
|
|
20777
|
-
const row = catalog.rows.find((r) => r.resource === "tickets");
|
|
20778
|
-
if (row === void 0 || row.error !== null || row.fields === void 0) {
|
|
20779
|
-
usable = false;
|
|
20780
|
-
} else {
|
|
20781
|
-
for (const f of plan.labelFields) {
|
|
20782
|
-
const frow = row.fields.find((x) => x.name === f);
|
|
20783
|
-
picklists[f] = frow?.picklistValues;
|
|
20784
|
-
}
|
|
20785
|
-
}
|
|
20786
|
-
} catch (err) {
|
|
20787
|
-
if (isAutotaskError(err) && err.code !== "ABORTED") usable = false;
|
|
20788
|
-
else throw err;
|
|
20789
|
-
}
|
|
20790
|
-
if (!usable) for (const f of plan.labelFields) section.reasons[f] = FIELD_CATALOG_HONESTY_NOTE;
|
|
21305
|
+
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
21306
|
+
last30(opts) {
|
|
21307
|
+
return windowQuery(this, "last30", this.http, opts);
|
|
20791
21308
|
}
|
|
20792
|
-
|
|
20793
|
-
|
|
20794
|
-
|
|
20795
|
-
|
|
20796
|
-
|
|
20797
|
-
|
|
20798
|
-
|
|
20799
|
-
|
|
20800
|
-
|
|
20801
|
-
|
|
20802
|
-
|
|
20803
|
-
|
|
20804
|
-
|
|
20805
|
-
|
|
20806
|
-
|
|
20807
|
-
|
|
20808
|
-
|
|
20809
|
-
|
|
20810
|
-
|
|
20811
|
-
|
|
20812
|
-
|
|
21309
|
+
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
21310
|
+
last7(opts) {
|
|
21311
|
+
return windowQuery(this, "last7", this.http, opts);
|
|
21312
|
+
}
|
|
21313
|
+
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
21314
|
+
last90(opts) {
|
|
21315
|
+
return windowQuery(this, "last90", this.http, opts);
|
|
21316
|
+
}
|
|
21317
|
+
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
21318
|
+
lastFullFortnight(opts) {
|
|
21319
|
+
return windowQuery(this, "lastFullFortnight", this.http, opts);
|
|
21320
|
+
}
|
|
21321
|
+
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
21322
|
+
lastFullMonth(opts) {
|
|
21323
|
+
return windowQuery(this, "lastFullMonth", this.http, opts);
|
|
21324
|
+
}
|
|
21325
|
+
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
21326
|
+
lastFullQuarter(opts) {
|
|
21327
|
+
return windowQuery(this, "lastFullQuarter", this.http, opts);
|
|
21328
|
+
}
|
|
21329
|
+
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
21330
|
+
lastFullWeek(opts) {
|
|
21331
|
+
return windowQuery(this, "lastFullWeek", this.http, opts);
|
|
21332
|
+
}
|
|
21333
|
+
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
21334
|
+
today(opts) {
|
|
21335
|
+
return windowQuery(this, "today", this.http, opts);
|
|
20813
21336
|
}
|
|
20814
|
-
|
|
20815
|
-
|
|
20816
|
-
|
|
20817
|
-
|
|
20818
|
-
|
|
20819
|
-
labelFor: (field, value) => {
|
|
20820
|
-
if (value === null || value === void 0) return null;
|
|
20821
|
-
const values = picklists[field];
|
|
20822
|
-
if (values === void 0) {
|
|
20823
|
-
section.reasons[field] = FIELD_CATALOG_HONESTY_NOTE;
|
|
20824
|
-
return null;
|
|
20825
|
-
}
|
|
20826
|
-
const hit = values.find((v) => String(v.value) === String(value));
|
|
20827
|
-
if (hit === void 0) {
|
|
20828
|
-
section.reasons[field] = PICKLIST_VALUE_NOT_SERVED_NOTE;
|
|
20829
|
-
return null;
|
|
20830
|
-
}
|
|
20831
|
-
return typeof hit.label === "string" ? hit.label : null;
|
|
20832
|
-
}
|
|
20833
|
-
};
|
|
20834
|
-
}
|
|
21337
|
+
/** Node-parity special op (hand-written in src/resources/special/billing_items.ts). */
|
|
21338
|
+
yesterday(opts) {
|
|
21339
|
+
return windowQuery(this, "yesterday", this.http, opts);
|
|
21340
|
+
}
|
|
21341
|
+
};
|
|
20835
21342
|
|
|
20836
21343
|
// src/resources/special/_shared/dates.ts
|
|
20837
21344
|
var DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
@@ -20888,22 +21395,6 @@ function num(v) {
|
|
|
20888
21395
|
return typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
20889
21396
|
}
|
|
20890
21397
|
|
|
20891
|
-
// src/resources/special/_shared/bounded-part.ts
|
|
20892
|
-
async function boundedPart(http, path, conditions, limit, operation, includeFields) {
|
|
20893
|
-
const body = { MaxRecords: limit, filter: conditions };
|
|
20894
|
-
if (includeFields !== void 0) body.IncludeFields = includeFields;
|
|
20895
|
-
const res = await http.request({
|
|
20896
|
-
method: "POST",
|
|
20897
|
-
idempotent: true,
|
|
20898
|
-
path,
|
|
20899
|
-
body,
|
|
20900
|
-
operation
|
|
20901
|
-
});
|
|
20902
|
-
const { items, pageDetails } = unwrapItems(res, operation);
|
|
20903
|
-
const capped = items.length >= limit;
|
|
20904
|
-
return { items, capped, hasMore: capped && pageDetails.nextPageUrl != null };
|
|
20905
|
-
}
|
|
20906
|
-
|
|
20907
21398
|
// src/resources/special/companies/shared.ts
|
|
20908
21399
|
var DEFAULT_LIMIT2 = 25;
|
|
20909
21400
|
var MAX_LIMIT2 = 500;
|
|
@@ -21252,199 +21743,9 @@ async function upsertByName(resource, companyName, params, opts, http) {
|
|
|
21252
21743
|
return createWithDiscovery(resource, body, owner, caveat, opts);
|
|
21253
21744
|
}
|
|
21254
21745
|
|
|
21255
|
-
// src/field-sets.ts
|
|
21256
|
-
var MODEL_FIELD_SETS = {
|
|
21257
|
-
"appointments": ["id", "createDateTime", "creatorResourceID", "description", "endDateTime", "resourceID", "startDateTime", "title", "updateDateTime"],
|
|
21258
|
-
"billing_codes": ["id", "afterHoursWorkType", "billingCodeType", "department", "description", "externalNumber", "generalLedgerAccount", "isActive", "isExcludedFromNewContracts", "markupRate", "name", "taxCategoryID", "unitCost", "unitPrice", "useType"],
|
|
21259
|
-
"billing_item_approval_levels": ["id", "approvalDateTime", "approvalLevel", "approvalResourceID", "timeEntryID"],
|
|
21260
|
-
"billing_items": ["id", "accountManagerWhenApprovedID", "billingCodeID", "billingItemType", "companyID", "configurationItemID", "contractBlockID", "contractChargeID", "contractID", "contractServiceAdjustmentID", "contractServiceBundleAdjustmentID", "contractServiceBundleID", "contractServiceBundlePeriodID", "contractServiceID", "contractServicePeriodID", "description", "expenseItemID", "extendedPrice", "internalCurrencyExtendedPrice", "internalCurrencyRate", "internalCurrencyTaxDollars", "internalCurrencyTotalAmount", "invoiceID", "itemApproverID", "itemDate", "itemName", "lineItemFullDescription", "lineItemGroupDescription", "milestoneID", "nonBillable", "organizationalLevelAssociationID", "ourCost", "postedDate", "postedOnTime", "projectChargeID", "projectID", "purchaseOrderNumber", "quantity", "rate", "roleID", "serviceBundleID", "serviceID", "sortOrderID", "subType", "taskID", "taxDollars", "ticketChargeID", "ticketID", "timeEntryID", "totalAmount", "vendorID", "webServiceDate"],
|
|
21261
|
-
"change_request_links": ["id", "changeRequestTicketID", "problemOrIncidentTicketID"],
|
|
21262
|
-
"checklist_libraries": ["id", "description", "entityType", "isActive", "name"],
|
|
21263
|
-
"checklist_library_checklist_items": ["id", "checklistLibraryID", "isImportant", "itemName", "knowledgebaseArticleID", "position", "soapParentPropertyId"],
|
|
21264
|
-
"classification_icons": ["id", "description", "isActive", "isSystem", "name", "relativeUrl"],
|
|
21265
|
-
"companies": ["id", "additionalAddressInformation", "address1", "address2", "alternatePhone1", "alternatePhone2", "apiVendorID", "assetValue", "billToCompanyLocationID", "billToAdditionalAddressInformation", "billingAddress1", "billingAddress2", "billToAddressToUse", "billToAttention", "billToCity", "billToCountryID", "billToState", "billToZipCode", "city", "classification", "companyCategoryID", "companyName", "companyNumber", "companyType", "competitorID", "countryID", "createDate", "createdByResourceID", "currencyID", "fax", "impersonatorCreatorResourceID", "invoiceEmailMessageID", "invoiceMethod", "invoiceNonContractItemsToParentCompany", "invoiceTemplateID", "isActive", "isClientPortalActive", "isEnabledForComanaged", "isSample", "isTaskFireActive", "isTaxExempt", "lastActivityDate", "lastTrackedModifiedDateTime", "marketSegmentID", "ownerResourceID", "parentCompanyID", "phone", "postalCode", "purchaseOrderTemplateID", "quoteEmailMessageID", "quoteTemplateID", "sicCode", "state", "stockMarket", "stockSymbol", "surveyCompanyRating", "taxID", "taxRegionID", "territoryID", "webAddress", "userDefinedFields"],
|
|
21266
|
-
"company_alerts": ["id", "alertText", "alertTypeID", "companyID", "soapParentPropertyId"],
|
|
21267
|
-
"company_locations": ["id", "address1", "address2", "alternatePhone1", "alternatePhone2", "city", "companyID", "countryID", "description", "fax", "isActive", "isPrimary", "isTaxExempt", "overrideCompanyTaxSettings", "name", "phone", "postalCode", "roundtripDistance", "state", "taxRegionID", "soapParentPropertyId"],
|
|
21268
|
-
"company_notes": ["id", "actionType", "assignedResourceID", "companyID", "completedDateTime", "contactID", "createDateTime", "endDateTime", "impersonatorCreatorResourceID", "impersonatorUpdaterResourceID", "lastModifiedDate", "name", "note", "opportunityID", "startDateTime", "soapParentPropertyId"],
|
|
21269
|
-
"company_site_configurations": ["id", "companyID", "locationName", "soapParentPropertyId", "userDefinedFields"],
|
|
21270
|
-
"company_webhooks": ["id", "deactivationUrl", "isActive", "isReady", "isSubscribedToCreateEvents", "isSubscribedToDeleteEvents", "isSubscribedToUpdateEvents", "name", "notificationEmailAddress", "ownerResourceID", "secretKey", "sendThresholdExceededNotification", "webhookGUID", "webhookUrl"],
|
|
21271
|
-
"configuration_item_billing_product_associations": ["id", "billingProductID", "configurationItemID", "effectiveDate", "expirationDate", "soapParentPropertyId"],
|
|
21272
|
-
"configuration_item_categories": ["id", "displayColorRGB", "isActive", "isClientPortalDefault", "isGlobalDefault", "name", "nickname"],
|
|
21273
|
-
"configuration_item_category_udf_associations": ["id", "configurationItemCategoryID", "isRequired", "userDefinedFieldDefinitionID", "soapParentPropertyId"],
|
|
21274
|
-
"configuration_item_dns_records": ["id", "createDateTime", "data", "installedProductID", "timeToLiveSeconds", "dnsType", "soapParentPropertyId"],
|
|
21275
|
-
"configuration_item_notes": ["id", "configurationItemID", "createDateTime", "creatorResourceID", "description", "impersonatorCreatorResourceID", "impersonatorUpdaterResourceID", "lastActivityDate", "noteType", "title", "soapParentPropertyId"],
|
|
21276
|
-
"configuration_item_related_items": ["id", "configurationItemID", "relatedConfigurationItemID", "relationship", "soapParentPropertyId"],
|
|
21277
|
-
"configuration_item_ssl_subject_alternative_names": ["id", "configurationItemID", "name", "soapParentPropertyId"],
|
|
21278
|
-
"configuration_item_types": ["id", "isActive", "name"],
|
|
21279
|
-
"configuration_item_webhooks": ["id", "deactivationUrl", "isActive", "isReady", "isSubscribedToCreateEvents", "isSubscribedToDeleteEvents", "isSubscribedToUpdateEvents", "name", "notificationEmailAddress", "ownerResourceID", "secretKey", "sendThresholdExceededNotification", "webhookGUID", "webhookUrl"],
|
|
21280
|
-
"configuration_items": ["id", "apiVendorID", "configurationItemCategoryID", "companyID", "companyLocationID", "configurationItemType", "contactID", "contractID", "contractServiceBundleID", "contractServiceID", "createDate", "createdByPersonID", "dailyCost", "dattoAvailableKilobytes", "dattoDeviceMemoryMegabytes", "dattoDrivesErrors", "dattoHostname", "dattoInternalIP", "dattoKernelVersionID", "dattoLastCheckInDateTime", "dattoNICSpeedKilobitsPerSecond", "dattoNumberOfAgents", "dattoNumberOfDrives", "dattoNumberOfVolumes", "dattoOffsiteUsedBytes", "dattoOSVersionID", "dattoPercentageUsed", "dattoProtectedKilobytes", "dattoRemoteIP", "dattoSerialNumber", "dattoUptimeSeconds", "dattoUsedKilobytes", "dattoZFSVersionID", "deviceNetworkingID", "domain", "domainRegistrarID", "domainRegistrationDateTime", "domainLastUpdatedDateTime", "domainExpirationDateTime", "hourlyCost", "impersonatorCreatorResourceID", "installDate", "installedByContactID", "installedByID", "isActive", "lastActivityPersonID", "lastActivityPersonType", "lastModifiedTime", "location", "monthlyCost", "notes", "numberOfUsers", "parentConfigurationItemID", "perUseCost", "productID", "referenceNumber", "referenceTitle", "rmmDeviceAuditAntivirusStatusID", "rmmDeviceAuditArchitectureID", "rmmDeviceAuditBackupStatusID", "rmmDeviceAuditDescription", "rmmDeviceAuditDeviceTypeID", "rmmDeviceAuditDisplayAdaptorID", "rmmDeviceAuditDomainID", "rmmDeviceAuditExternalIPAddress", "rmmDeviceAuditHostname", "rmmDeviceAuditIPAddress", "rmmDeviceAuditLastUser", "rmmDeviceAuditMacAddress", "rmmDeviceAuditManufacturerID", "rmmDeviceAuditFirmwareID", "rmmDeviceAuditMemoryBytes", "rmmDeviceAuditMissingPatchCount", "rmmDeviceAuditMobileNetworkOperatorID", "rmmDeviceAuditMobileNumber", "rmmDeviceAuditModelID", "rmmDeviceAuditMotherboardID", "rmmDeviceAuditOperatingSystem", "rmmDeviceAuditPatchStatusID", "rmmDeviceAuditProcessorID", "rmmDeviceAuditServicePackID", "rmmDeviceAuditSNMPContact", "rmmDeviceAuditSNMPLocation", "rmmDeviceAuditSNMPName", "rmmDeviceAuditSoftwareStatusID", "rmmDeviceAuditStorageBytes", "rmmDeviceID", "rmmDeviceUID", "rmmVsaxDeviceID", "rmmOpenAlertCount", "serialNumber", "serviceBundleID", "serviceID", "serviceLevelAgreementID", "setupFee", "sourceChargeID", "sourceChargeType", "sslSource", "sslCommonName", "sslValidFromDateTime", "sslValidUntilDateTime", "sslIssuedBy", "sslOrganization", "sslOrganizationUnit", "sslLocation", "sslSerialNumber", "sslSignatureAlgorithm", "vendorID", "warrantyExpirationDate", "rmmIsInMaintenanceMode", "rmmIsMobileDeviceManagementEnrolled", "rmmDeviceUrl", "userDefinedFields"],
|
|
21281
|
-
"contact_group_contacts": ["id", "contactId", "contactGroupId", "soapParentPropertyId"],
|
|
21282
|
-
"contact_groups": ["id", "name", "isActive"],
|
|
21283
|
-
"contact_webhooks": ["id", "deactivationUrl", "isActive", "isReady", "isSubscribedToCreateEvents", "isSubscribedToDeleteEvents", "isSubscribedToUpdateEvents", "name", "notificationEmailAddress", "ownerResourceID", "secretKey", "sendThresholdExceededNotification", "webhookGUID", "webhookUrl"],
|
|
21284
|
-
"contacts": ["id", "additionalAddressInformation", "addressLine", "addressLine1", "alternatePhone", "apiVendorID", "bulkEmailOptOutTime", "city", "companyID", "companyLocationID", "countryID", "createDate", "emailAddress", "emailAddress2", "emailAddress3", "extension", "externalID", "facebookUrl", "faxNumber", "firstName", "impersonatorCreatorResourceID", "isActive", "isOptedOutFromBulkEmail", "lastActivityDate", "lastModifiedDate", "lastName", "linkedInUrl", "middleInitial", "mobilePhone", "namePrefix", "nameSuffix", "note", "receivesEmailNotifications", "phone", "primaryContact", "roomNumber", "solicitationOptOut", "solicitationOptOutTime", "state", "surveyOptOut", "title", "twitterUrl", "zipCode", "soapParentPropertyId", "userDefinedFields"],
|
|
21285
|
-
"contract_billing_rules": ["id", "contractID", "createChargesAsBillable", "dailyProratedCost", "dailyProratedPrice", "determineUnits", "endDate", "executionMethod", "includeItemsInChargeDescription", "invoiceDescription", "isActive", "isDailyProrationEnabled", "maximumUnits", "minimumUnits", "productID", "startDate", "soapParentPropertyId"],
|
|
21286
|
-
"contract_block_hour_factors": ["id", "blockHourMultiplier", "contractHourlyRate", "contractID", "internalCurrencyContractHourlyRate", "roleID", "soapParentPropertyId"],
|
|
21287
|
-
"contract_blocks": ["id", "contractID", "datePurchased", "endDate", "hourlyRate", "hours", "hoursApproved", "invoiceNumber", "isPaid", "paymentNumber", "paymentType", "startDate", "status", "soapParentPropertyId"],
|
|
21288
|
-
"contract_charges": ["id", "billableAmount", "billingCodeID", "chargeType", "contractID", "contractServiceBundleID", "contractServiceID", "createDate", "creatorResourceID", "datePurchased", "description", "extendedCost", "internalCurrencyBillableAmount", "internalCurrencyUnitPrice", "internalPurchaseOrderNumber", "isBillableToCompany", "isBilled", "name", "notes", "organizationalLevelAssociationID", "productID", "purchaseOrderNumber", "status", "statusLastModifiedBy", "statusLastModifiedDate", "unitCost", "unitPrice", "unitQuantity", "soapParentPropertyId"],
|
|
21289
|
-
"contract_exclusion_billing_codes": ["id", "billingCodeID", "contractID", "soapParentPropertyId"],
|
|
21290
|
-
"contract_exclusion_roles": ["id", "contractID", "roleID", "soapParentPropertyId"],
|
|
21291
|
-
"contract_exclusion_set_excluded_roles": ["id", "contractExclusionSetID", "excludedRoleID", "soapParentPropertyId"],
|
|
21292
|
-
"contract_exclusion_set_excluded_work_types": ["id", "contractExclusionSetID", "excludedWorkTypeID", "soapParentPropertyId"],
|
|
21293
|
-
"contract_exclusion_sets": ["id", "description", "isActive", "name"],
|
|
21294
|
-
"contract_milestones": ["id", "amount", "billingCodeID", "contractID", "createDate", "creatorResourceID", "dateDue", "description", "internalCurrencyAmount", "isInitialPayment", "organizationalLevelAssociationID", "status", "title", "soapParentPropertyId"],
|
|
21295
|
-
"contract_notes": ["id", "contractID", "createDateTime", "creatorResourceID", "description", "impersonatorCreatorResourceID", "impersonatorUpdaterResourceID", "lastActivityDate", "title", "soapParentPropertyId"],
|
|
21296
|
-
"contract_rates": ["id", "contractHourlyRate", "contractID", "internalCurrencyContractHourlyRate", "roleID", "soapParentPropertyId"],
|
|
21297
|
-
"contract_retainers": ["id", "amount", "amountApproved", "contractID", "datePurchased", "endDate", "internalCurrencyAmount", "internalCurrencyAmountApproved", "invoiceNumber", "isPaid", "paymentID", "paymentNumber", "startDate", "status", "soapParentPropertyId"],
|
|
21298
|
-
"contract_role_costs": ["id", "contractID", "rate", "resourceID", "roleID", "soapParentPropertyId"],
|
|
21299
|
-
"contract_service_bundle_units": ["id", "approveAndPostDate", "contractID", "contractServiceBundleID", "cost", "endDate", "internalCurrencyPrice", "organizationalLevelAssociationID", "price", "serviceBundleID", "startDate", "units", "soapParentPropertyId"],
|
|
21300
|
-
"contract_service_bundles": ["id", "adjustedPrice", "contractID", "internalCurrencyAdjustedPrice", "internalCurrencyUnitPrice", "internalDescription", "invoiceDescription", "quoteItemID", "serviceBundleID", "unitPrice", "soapParentPropertyId"],
|
|
21301
|
-
"contract_service_units": ["id", "approveAndPostDate", "contractID", "contractServiceID", "cost", "endDate", "internalCurrencyPrice", "organizationalLevelAssociationID", "price", "serviceID", "startDate", "units", "vendorCompanyID", "soapParentPropertyId"],
|
|
21302
|
-
"contract_services": ["id", "contractID", "internalCurrencyAdjustedPrice", "internalCurrencyUnitPrice", "internalDescription", "invoiceDescription", "quoteItemID", "serviceID", "unitCost", "unitPrice", "soapParentPropertyId"],
|
|
21303
|
-
"contract_ticket_purchases": ["id", "contractID", "datePurchased", "endDate", "invoiceNumber", "isPaid", "paymentNumber", "paymentType", "perTicketRate", "startDate", "status", "ticketsPurchased", "ticketsUsed", "soapParentPropertyId"],
|
|
21304
|
-
"contracts": ["id", "billingPreference", "billToCompanyContactID", "billToCompanyID", "companyID", "contactID", "contactName", "contractCategory", "contractExclusionSetID", "contractName", "contractNumber", "contractPeriodType", "contractType", "description", "endDate", "estimatedCost", "estimatedHours", "estimatedRevenue", "exclusionContractID", "internalCurrencyOverageBillingRate", "internalCurrencySetupFee", "isCompliant", "isDefaultContract", "lastModifiedDateTime", "opportunityID", "organizationalLevelAssociationID", "overageBillingRate", "purchaseOrderNumber", "renewedContractID", "serviceLevelAgreementID", "setupFee", "setupFeeBillingCodeID", "startDate", "status", "timeReportingRequiresStartAndStopTimes", "userDefinedFields"],
|
|
21305
|
-
"countries": ["id", "addressFormatID", "countryCode", "displayName", "invoiceTemplateID", "isActive", "isDefaultCountry", "name", "purchaseOrderTemplateID", "quoteTemplateID"],
|
|
21306
|
-
"currencies": ["id", "currencyNegativeFormat", "currencyPositiveFormat", "description", "displaySymbol", "exchangeRate", "isActive", "isInternalCurrency", "lastModifiedDateTime", "name", "updateResourceId"],
|
|
21307
|
-
"departments": ["id", "description", "name", "number", "primaryLocationID"],
|
|
21308
|
-
"domain_registrars": ["id", "name", "url"],
|
|
21309
|
-
"expense_item_attachments": ["id", "attachDate", "attachedByContactID", "attachedByResourceID", "attachmentType", "contentType", "creatorType", "expenseItemID", "expenseReportID", "fileSize", "fullPath", "impersonatorCreatorResourceID", "opportunityID", "parentID", "publish", "title", "data", "parentType", "soapParentPropertyId", "isTaskAttachment"],
|
|
21310
|
-
"expense_items": ["id", "companyID", "description", "destination", "entertainmentLocation", "expenseCategory", "expenseCurrencyExpenseAmount", "expenseCurrencyID", "expenseDate", "expenseReportID", "glCode", "haveReceipt", "internalCurrencyExpenseAmount", "internalCurrencyReimbursementAmount", "isBillableToCompany", "isReimbursable", "isRejected", "miles", "odometerEnd", "odometerStart", "origin", "paymentType", "projectID", "purchaseOrderNumber", "reimbursementCurrencyReimbursementAmount", "taskID", "ticketID", "workType", "soapParentPropertyId"],
|
|
21311
|
-
"expense_reports": ["id", "amountDue", "approvedDate", "approverID", "departmentNumber", "internalCurrencyCashAdvanceAmount", "internalCurrencyExpenseTotal", "name", "organizationalLevelAssociationID", "quickBooksReferenceNumber", "reimbursementCurrencyAmountDue", "reimbursementCurrencyCashAdvanceAmount", "reimbursementCurrencyID", "rejectionReason", "status", "submit", "submitDate", "submitterID", "weekEnding"],
|
|
21312
|
-
"holiday_sets": ["id", "holidaySetDescription", "holidaySetName"],
|
|
21313
|
-
"holidays": ["id", "holidayDate", "holidayName", "holidaySetID", "soapParentPropertyId"],
|
|
21314
|
-
"inventory_products": ["id", "availableUnits", "backOrderQuantity", "bin", "createDateTime", "createdByResourceID", "inventoryLocationID", "onHandUnits", "pickedUnits", "productID", "quantityMaximum", "quantityMinimum", "referenceNumber", "reservedUnits", "unitsOnOrder"],
|
|
21315
|
-
"invoice_templates": ["id", "coveredByBlockRetainerContractLabel", "coveredByRecurringServiceFixedPricePerTicketContractLabel", "currencyNegativeFormat", "currencyPositiveFormat", "dateFormat", "displayFixedPriceContractLabor", "displayRecurringServiceContractLabor", "displaySeparateLineItemForEachTax", "displayTaxCategory", "displayTaxCategorySuperscripts", "displayZeroAmountRecurringServicesAndBundles", "groupBy", "itemizeItemsInEachGroup", "itemizeServicesAndBundles", "name", "nonBillableLaborLabel", "numberFormat", "pageLayout", "pageNumberFormat", "paymentTerms", "rateCostExpression", "showGridHeader", "showVerticalGridLines", "sortBy", "timeFormat"],
|
|
21316
|
-
"invoices": ["id", "batchID", "comments", "companyID", "createDateTime", "creatorResourceID", "dueDate", "fromDate", "invoiceDateTime", "invoiceEditorTemplateID", "invoiceNumber", "invoiceTotal", "isVoided", "orderNumber", "paidDate", "paymentTerm", "taxGroup", "taxRegionName", "toDate", "totalTaxValue", "voidedByResourceID", "voidedDate", "webServiceDate"],
|
|
21317
|
-
"notification_history": ["id", "companyID", "entityNumber", "entityTitle", "initiatingContactID", "initiatingResourceID", "isActive", "isDeleted", "isTemplateJob", "notificationHistoryTypeID", "notificationSentTime", "opportunityID", "projectID", "quoteID", "recipientDisplayName", "recipientEmailAddress", "taskID", "templateName", "ticketID", "timeEntryID"],
|
|
21318
|
-
"opportunities": ["id", "advancedField1", "advancedField2", "advancedField3", "advancedField4", "advancedField5", "amount", "assessmentScore", "barriers", "closedDate", "companyID", "contactID", "cost", "createDate", "creatorResourceID", "description", "helpNeeded", "impersonatorCreatorResourceID", "lastActivity", "leadSource", "lossReason", "lossReasonDetail", "lostDate", "market", "monthlyCost", "monthlyRevenue", "nextStep", "onetimeCost", "onetimeRevenue", "opportunityCategoryID", "organizationalLevelAssociationID", "ownerResourceID", "primaryCompetitor", "probability", "productID", "projectedCloseDate", "promisedFulfillmentDate", "promotionName", "quarterlyCost", "quarterlyRevenue", "rating", "relationshipAssessmentScore", "revenueSpread", "revenueSpreadUnit", "salesOrderID", "salesProcessPercentComplete", "semiannualCost", "semiannualRevenue", "stage", "startDate", "status", "technicalAssessmentScore", "throughDate", "title", "totalAmountMonths", "useQuoteTotals", "winReason", "winReasonDetail", "yearlyCost", "yearlyRevenue", "userDefinedFields"],
|
|
21319
|
-
"opportunity_attachments": ["id", "attachDate", "attachedByContactID", "attachedByResourceID", "attachmentType", "contentType", "creatorType", "fileSize", "fullPath", "impersonatorCreatorResourceID", "opportunityID", "parentAttachmentID", "parentID", "publish", "title", "data", "parentType", "soapParentPropertyId", "isTaskAttachment"],
|
|
21320
|
-
"opportunity_categories": ["id", "displayColorRGB", "isActive", "isGlobalDefault", "name", "nickname"],
|
|
21321
|
-
"organizational_level1s": ["id", "description", "isActive", "name"],
|
|
21322
|
-
"organizational_level2s": ["id", "description", "isActive", "name"],
|
|
21323
|
-
"organizational_level_associations": ["id", "isActive", "organizationalLevel1ID", "organizationalLevel2ID"],
|
|
21324
|
-
"phases": ["id", "createDate", "creatorResourceID", "description", "dueDate", "estimatedHours", "externalID", "isScheduled", "lastActivityDateTime", "parentPhaseID", "phaseNumber", "projectID", "startDate", "title", "soapParentPropertyId"],
|
|
21325
|
-
"product_vendors": ["id", "isActive", "isDefault", "productID", "vendorCost", "vendorID", "vendorPartNumber", "soapParentPropertyId"],
|
|
21326
|
-
"products": ["id", "billingType", "chargeBillingCodeID", "createdByResourceID", "createdTime", "defaultVendorID", "description", "doesNotRequireProcurement", "externalProductID", "impersonatorCreatorResourceID", "internalProductID", "isActive", "isEligibleForRma", "isSerialized", "link", "manufacturerName", "manufacturerProductName", "markupRate", "msrp", "name", "periodType", "priceCostMethod", "productBillingCodeID", "productCategory", "sku", "unitCost", "unitPrice", "vendorProductNumber", "defaultInstalledProductCategoryID", "userDefinedFields"],
|
|
21327
|
-
"project_charges": ["id", "billableAmount", "billingCodeID", "chargeType", "contractServiceBundleID", "contractServiceID", "createDate", "creatorResourceID", "datePurchased", "description", "estimatedCost", "extendedCost", "internalCurrencyBillableAmount", "internalCurrencyUnitPrice", "internalPurchaseOrderNumber", "isBillableToCompany", "isBilled", "name", "notes", "organizationalLevelAssociationID", "productID", "projectID", "purchaseOrderNumber", "status", "statusLastModifiedBy", "statusLastModifiedDate", "unitCost", "unitPrice", "unitQuantity", "soapParentPropertyId"],
|
|
21328
|
-
"project_notes": ["id", "createDateTime", "createdByContactID", "creatorResourceID", "description", "impersonatorCreatorResourceID", "impersonatorUpdaterResourceID", "isAnnouncement", "lastActivityDate", "noteType", "projectID", "publish", "title", "soapParentPropertyId"],
|
|
21329
|
-
"projects": ["id", "actualBilledHours", "actualHours", "changeOrdersBudget", "changeOrdersRevenue", "companyID", "companyOwnerResourceID", "completedDateTime", "completedPercentage", "contractID", "createDateTime", "creatorResourceID", "department", "description", "duration", "endDateTime", "estimatedSalesCost", "estimatedTime", "extProjectNumber", "extProjectType", "impersonatorCreatorResourceID", "laborEstimatedCosts", "laborEstimatedMarginPercentage", "laborEstimatedRevenue", "lastActivityDateTime", "lastActivityPersonType", "lastActivityResourceID", "opportunityID", "organizationalLevelAssociationID", "originalEstimatedRevenue", "projectCostEstimatedMarginPercentage", "projectCostsBudget", "projectCostsRevenue", "projectLeadResourceID", "projectName", "projectNumber", "projectType", "purchaseOrderNumber", "sgda", "startDateTime", "status", "statusDateTime", "statusDetail", "userDefinedFields"],
|
|
21330
|
-
"purchase_approvals": ["id", "costType", "isApproved", "rejectNote"],
|
|
21331
|
-
"purchase_order_item_receiving": ["id", "purchaseOrderItemID", "quantityBackOrdered", "quantityNowReceiving", "quantityPreviouslyReceived", "receiveDate", "receivedByResourceID", "serialNumber", "vendorInvoiceNumber", "soapParentPropertyId"],
|
|
21332
|
-
"purchase_order_items": ["id", "chargeID", "contractID", "estimatedArrivalDate", "internalCurrencyUnitCost", "inventoryLocationID", "memo", "orderID", "productID", "projectID", "quantity", "salesOrderID", "ticketID", "unitCost", "soapParentPropertyId"],
|
|
21333
|
-
"purchase_orders": ["id", "cancelDateTime", "createDateTime", "creatorResourceID", "externalPONumber", "fax", "freight", "generalMemo", "impersonatorCreatorResourceID", "internalCurrencyFreight", "latestEstimatedArrivalDate", "paymentTerm", "phone", "purchaseForCompanyID", "purchaseOrderNumber", "purchaseOrderTemplateID", "shippingDate", "shippingType", "shipToAddress1", "shipToAddress2", "shipToCity", "shipToName", "shipToPostalCode", "shipToState", "showEachTaxInGroup", "showTaxCategory", "status", "submitDateTime", "taxRegionID", "useItemDescriptionsFrom", "vendorID", "vendorInvoiceNumber", "additionalVendorInvoiceNumbers"],
|
|
21334
|
-
"quote_items": ["id", "averageCost", "chargeID", "description", "expenseID", "highestCost", "internalCurrencyLineDiscount", "internalCurrencyUnitDiscount", "internalCurrencyUnitPrice", "isOptional", "isTaxable", "laborID", "lineDiscount", "markupRate", "name", "percentageDiscount", "periodType", "productID", "quantity", "quoteID", "quoteItemType", "serviceBundleID", "serviceID", "shippingID", "sortOrderID", "taxCategoryID", "totalEffectiveTax", "unitCost", "unitDiscount", "unitPrice", "soapParentPropertyId"],
|
|
21335
|
-
"quote_locations": ["id", "address1", "address2", "city", "postalCode", "state"],
|
|
21336
|
-
"quote_templates": ["id", "calculateTaxSeparately", "createDate", "createdBy", "currencyNegativeFormat", "currencyPositiveFormat", "dateFormat", "description", "displayTaxCategorySuperscripts", "isActive", "lastActivityBy", "lastActivityDate", "name", "numberFormat", "pageLayout", "pageNumberFormat", "showEachTaxInGroup", "showGridHeader", "showTaxCategory", "showVerticalGridLines"],
|
|
21337
|
-
"quotes": ["id", "approvalStatus", "approvalStatusChangedByResourceID", "approvalStatusChangedDate", "billToLocationID", "calculateTaxSeparately", "comment", "companyID", "contactID", "createDate", "creatorResourceID", "description", "effectiveDate", "expirationDate", "extApprovalContactResponse", "extApprovalResponseDate", "extApprovalResponseSignature", "externalQuoteNumber", "groupByID", "impersonatorCreatorResourceID", "isActive", "lastActivityDate", "lastModifiedBy", "lastPublishedByResourceID", "lastPublishedDateTime", "name", "opportunityID", "paymentTerm", "paymentType", "primaryQuote", "proposalProjectID", "purchaseOrderNumber", "quoteNumber", "quoteTemplateID", "shippingType", "shipToLocationID", "showEachTaxInGroup", "showTaxCategory", "soldToLocationID", "taxRegionID"],
|
|
21338
|
-
"resource_role_queues": ["id", "queueID", "resourceID", "soapParentPropertyId"],
|
|
21339
|
-
"resource_roles": ["id", "departmentID", "isActive", "queueID", "resourceID", "roleID", "soapParentPropertyId"],
|
|
21340
|
-
"resource_skills": ["id", "resourceID", "skillDescription", "skillID", "skillLevel", "soapParentPropertyId"],
|
|
21341
|
-
"resource_time_off_approvers": ["id", "resourceID", "approverResourceID", "approvalLevel", "soapParentPropertyId"],
|
|
21342
|
-
"resource_time_off_balances": ["id", "resourceID", "floatingHolidayAnnualAllowance", "floatingHolidayBalance", "floatingHolidayPlanned", "floatingHolidayUsed", "floatingHolidayWaitingApproval", "personalTimeAnnualAllowance", "personalTimeBalance", "personalTimePlanned", "personalTimeUsed", "personalTimeWaitingApproval", "sickTimeAnnualAllowance", "sickTimeBalance", "sickTimePlanned", "sickTimeUsed", "sickTimeWaitingApproval", "vacationTimeAnnualAllowance", "vacationTimeBalance", "vacationTimePlanned", "vacationTimeUsed", "vacationTimeWaitingApproval", "year", "soapParentPropertyId"],
|
|
21343
|
-
"resources": ["id", "accountingReferenceID", "dateFormat", "defaultServiceDeskRoleID", "email", "email2", "email3", "emailTypeCode", "emailTypeCode2", "emailTypeCode3", "firstName", "gender", "greeting", "hireDate", "homePhone", "initials", "internalCost", "isActive", "lastName", "licenseType", "locationID", "middleName", "mobilePhone", "numberFormat", "officeExtension", "officePhone", "payrollIdentifier", "payrollType", "resourceType", "suffix", "surveyResourceRating", "timeFormat", "title", "travelAvailabilityPct", "userName", "userType"],
|
|
21344
|
-
"roles": ["id", "description", "hourlyFactor", "hourlyRate", "isActive", "isExcludedFromNewContracts", "isSystemRole", "name", "quoteItemDefaultTaxCategoryId", "roleType"],
|
|
21345
|
-
"sales_orders": ["id", "additionalBillToAddressInformation", "additionalShipToAddressInformation", "billingAddress1", "billingAddress2", "billToCity", "billToCountryID", "billToPostalCode", "billToState", "companyID", "contactID", "impersonatorCreatorResourceID", "opportunityID", "organizationalLevelAssociationID", "ownerResourceID", "promisedFulfillmentDate", "salesOrderDate", "shipToAddress1", "shipToAddress2", "shipToCity", "shipToCountryID", "shipToPostalCode", "shipToState", "status", "title", "soapParentPropertyId", "userDefinedFields"],
|
|
21346
|
-
"service_call_task_resources": ["id", "resourceID", "serviceCallTaskID", "soapParentPropertyId"],
|
|
21347
|
-
"service_call_tasks": ["id", "serviceCallID", "taskID", "soapParentPropertyId"],
|
|
21348
|
-
"service_call_ticket_resources": ["id", "resourceID", "serviceCallTicketID", "soapParentPropertyId"],
|
|
21349
|
-
"service_call_tickets": ["id", "serviceCallID", "ticketID", "soapParentPropertyId"],
|
|
21350
|
-
"service_calls": ["id", "cancelationNoticeHours", "canceledByResourceID", "canceledDateTime", "companyID", "companyLocationID", "createDateTime", "creatorResourceID", "description", "duration", "endDateTime", "impersonatorCreatorResourceID", "isComplete", "lastModifiedDateTime", "startDateTime", "status"],
|
|
21351
|
-
"service_level_agreement_results": ["id", "firstResponseElapsedHours", "firstResponseInitiatingResourceID", "firstResponseResourceID", "isFirstResponseMet", "isResolutionMet", "isResolutionPlanMet", "resolutionElapsedHours", "resolutionPlanElapsedHours", "resolutionPlanResourceID", "resolutionResourceID", "serviceLevelAgreementName", "ticketID", "soapParentPropertyId"],
|
|
21352
|
-
"services": ["id", "billingCodeID", "createDate", "creatorResourceID", "description", "invoiceDescription", "isActive", "lastModifiedDate", "markupRate", "name", "periodType", "serviceLevelAgreementID", "unitCost", "unitPrice", "updateResourceID", "vendorCompanyID", "manufacturerServiceProvider", "manufacturerServiceProviderProductNumber", "catalogNumberPartNumber", "sku", "internalID", "externalID", "url", "userDefinedFields"],
|
|
21353
|
-
"skills": ["id", "categoryID", "description", "isActive", "name"],
|
|
21354
|
-
"subscription_periods": ["id", "periodCost", "periodDate", "periodPrice", "postedDate", "purchaseOrderNumber", "subscriptionID", "soapParentPropertyId"],
|
|
21355
|
-
"subscriptions": ["id", "configurationItemID", "description", "effectiveDate", "expirationDate", "impersonatorCreatorResourceID", "materialCodeID", "organizationalLevelAssociationID", "periodCost", "periodPrice", "periodType", "purchaseOrderNumber", "status", "subscriptionName", "totalCost", "totalPrice", "vendorID", "userDefinedFields"],
|
|
21356
|
-
"survey_results": ["id", "companyID", "companyRating", "completeDate", "contactID", "contactRating", "resourceRating", "sendDate", "surveyID", "surveyRating", "ticketID"],
|
|
21357
|
-
"surveys": ["id", "description", "displayName", "name"],
|
|
21358
|
-
"tag_aliases": ["id", "alias", "tagID", "soapParentPropertyId"],
|
|
21359
|
-
"tag_groups": ["id", "displayColor", "isActive", "isSystem", "label"],
|
|
21360
|
-
"tags": ["id", "createDateTime", "isActive", "isExcludedFromAutomaticTagging", "isSystem", "label", "lastModifiedDateTime", "tagGroupID"],
|
|
21361
|
-
"task_secondary_resources": ["id", "resourceID", "roleID", "taskID", "soapParentPropertyId"],
|
|
21362
|
-
"tasks": ["id", "assignedResourceID", "assignedResourceRoleID", "billingCodeID", "canClientPortalUserCompleteTask", "companyLocationID", "completedByResourceID", "completedByType", "completedDateTime", "createDateTime", "creatorResourceID", "creatorType", "departmentID", "description", "endDateTime", "estimatedHours", "externalID", "hoursToBeScheduled", "isTaskBillable", "isVisibleInClientPortal", "lastActivityDateTime", "lastActivityPersonType", "lastActivityResourceID", "phaseID", "priority", "priorityLabel", "projectID", "purchaseOrderNumber", "remainingHours", "startDateTime", "status", "taskCategoryID", "taskNumber", "taskType", "title", "soapParentPropertyId", "userDefinedFields"],
|
|
21363
|
-
"tax_regions": ["id", "isActive", "name"],
|
|
21364
|
-
"ticket_additional_configuration_items": ["id", "configurationItemID", "ticketID", "soapParentPropertyId"],
|
|
21365
|
-
"ticket_additional_contacts": ["id", "contactID", "ticketID", "soapParentPropertyId"],
|
|
21366
|
-
"ticket_attachments": ["id", "attachDate", "attachedByContactID", "attachedByResourceID", "attachmentType", "contentType", "creatorType", "fileSize", "fullPath", "impersonatorCreatorResourceID", "opportunityID", "parentAttachmentID", "parentID", "publish", "ticketID", "ticketNoteID", "timeEntryID", "title", "data", "parentType", "soapParentPropertyId", "isTaskAttachment"],
|
|
21367
|
-
"ticket_categories": ["id", "displayColorRGB", "isActive", "isApiOnly", "isGlobalDefault", "name", "nickname"],
|
|
21368
|
-
"ticket_category_field_defaults": ["id", "description", "estimatedHours", "issueTypeID", "organizationalLevelAssociationID", "priority", "purchaseOrderNumber", "queueID", "resolution", "serviceLevelAgreementID", "sourceID", "status", "subIssueTypeID", "ticketCategoryID", "ticketTypeID", "title", "workTypeID", "soapParentPropertyId"],
|
|
21369
|
-
"ticket_change_request_approvals": ["id", "approveRejectDateTime", "approveRejectNote", "contactID", "isApproved", "resourceID", "ticketID", "soapParentPropertyId"],
|
|
21370
|
-
"ticket_charges": ["id", "billableAmount", "billingCodeID", "chargeType", "contractServiceBundleID", "contractServiceID", "createDate", "creatorResourceID", "datePurchased", "description", "extendedCost", "internalCurrencyBillableAmount", "internalCurrencyUnitPrice", "internalPurchaseOrderNumber", "isBillableToCompany", "isBilled", "name", "notes", "organizationalLevelAssociationID", "productID", "purchaseOrderNumber", "status", "statusLastModifiedBy", "statusLastModifiedDate", "ticketID", "unitCost", "unitPrice", "unitQuantity", "soapParentPropertyId"],
|
|
21371
|
-
"ticket_checklist_items": ["id", "completedByResourceID", "completedDateTime", "isCompleted", "isImportant", "itemName", "knowledgebaseArticleID", "position", "ticketID", "soapParentPropertyId"],
|
|
21372
|
-
"ticket_history": ["id", "action", "date", "detail", "resourceID", "ticketID", "soapParentPropertyId"],
|
|
21373
|
-
"ticket_note_attachments": ["id", "attachDate", "attachedByContactID", "attachedByResourceID", "attachmentType", "contentType", "creatorType", "fileSize", "fullPath", "impersonatorCreatorResourceID", "opportunityID", "parentID", "publish", "ticketID", "ticketNoteID", "title", "data", "parentType", "soapParentPropertyId", "isTaskAttachment"],
|
|
21374
|
-
"ticket_note_webhooks": ["id", "deactivationUrl", "isActive", "isReady", "isSubscribedToCreateEvents", "isSubscribedToDeleteEvents", "isSubscribedToUpdateEvents", "name", "notificationEmailAddress", "ownerResourceID", "secretKey", "sendThresholdExceededNotification", "webhookGUID", "webhookUrl"],
|
|
21375
|
-
"ticket_notes": ["id", "createDateTime", "createdByContactID", "creatorResourceID", "description", "impersonatorCreatorResourceID", "impersonatorUpdaterResourceID", "lastActivityDate", "noteType", "publish", "ticketID", "title", "soapParentPropertyId"],
|
|
21376
|
-
"ticket_secondary_resources": ["id", "resourceID", "roleID", "ticketID", "soapParentPropertyId"],
|
|
21377
|
-
"ticket_webhooks": ["id", "deactivationUrl", "isActive", "isReady", "isSubscribedToCreateEvents", "isSubscribedToDeleteEvents", "isSubscribedToUpdateEvents", "name", "notificationEmailAddress", "ownerResourceID", "secretKey", "sendThresholdExceededNotification", "webhookGUID", "webhookUrl"],
|
|
21378
|
-
"tickets": ["id", "apiVendorID", "assignedResourceID", "assignedResourceRoleID", "billingCodeID", "changeApprovalBoard", "changeApprovalStatus", "changeApprovalType", "changeInfoField1", "changeInfoField2", "changeInfoField3", "changeInfoField4", "changeInfoField5", "companyID", "companyLocationID", "completedByResourceID", "completedDate", "configurationItemID", "contactID", "contractID", "contractServiceBundleID", "contractServiceID", "createDate", "createdByContactID", "creatorResourceID", "creatorType", "currentServiceThermometerRating", "description", "dueDateTime", "estimatedHours", "externalID", "firstResponseAssignedResourceID", "firstResponseDateTime", "firstResponseDueDateTime", "firstResponseInitiatingResourceID", "hoursToBeScheduled", "impersonatorCreatorResourceID", "isAssignedToComanaged", "issueType", "isVisibleToComanaged", "lastActivityDate", "lastActivityPersonType", "lastActivityResourceID", "lastCustomerNotificationDateTime", "lastCustomerVisibleActivityDateTime", "lastTrackedModificationDateTime", "monitorID", "monitorTypeID", "opportunityID", "organizationalLevelAssociationID", "previousServiceThermometerRating", "priority", "problemTicketId", "projectID", "purchaseOrderNumber", "queueID", "resolution", "resolutionPlanDateTime", "resolutionPlanDueDateTime", "resolvedDateTime", "resolvedDueDateTime", "rmaStatus", "rmaType", "rmmAlertID", "serviceLevelAgreementHasBeenMet", "serviceLevelAgreementID", "serviceLevelAgreementPausedNextEventHours", "serviceThermometerTemperature", "source", "status", "subIssueType", "ticketCategory", "ticketNumber", "ticketType", "title", "userDefinedFields"],
|
|
21379
|
-
"time_entries": ["id", "billingApprovalDateTime", "billingApprovalLevelMostRecent", "billingApprovalResourceID", "billingCodeID", "contractID", "contractServiceBundleID", "contractServiceID", "createDateTime", "creatorUserID", "dateWorked", "endDateTime", "hoursToBill", "hoursWorked", "impersonatorCreatorResourceID", "impersonatorUpdaterResourceID", "internalBillingCodeID", "internalNotes", "isInternalNotesVisibleToComanaged", "isNonBillable", "lastModifiedDateTime", "lastModifiedUserID", "offsetHours", "resourceID", "roleID", "showOnInvoice", "startDateTime", "summaryNotes", "taskID", "ticketID", "timeEntryType"],
|
|
21380
|
-
"time_entry_attachments": ["id", "attachDate", "attachedByContactID", "attachedByResourceID", "attachmentType", "contentType", "creatorType", "fileSize", "fullPath", "impersonatorCreatorResourceID", "opportunityID", "parentID", "publish", "taskID", "ticketID", "timeEntryID", "title", "data", "parentType", "soapParentPropertyId", "isTaskAttachment"],
|
|
21381
|
-
"time_off_requests": ["id", "approvedDateTime", "approveRejectResourceID", "createdByResourceID", "createDateTime", "endTime", "hours", "impersonatorApproveRejectResourceID", "lastModifiedByResourceID", "lastModifiedDateTime", "lastApprovedLevel", "reason", "rejectReason", "requestDate", "resourceID", "startTime", "status", "timeOffRequestType", "soapParentPropertyId"]
|
|
21382
|
-
};
|
|
21383
|
-
var MODEL_DATE_FIELDS = {
|
|
21384
|
-
"appointments": ["createDateTime", "endDateTime", "startDateTime", "updateDateTime"],
|
|
21385
|
-
"billing_item_approval_levels": ["approvalDateTime"],
|
|
21386
|
-
"billing_items": ["itemDate", "postedDate", "postedOnTime", "webServiceDate"],
|
|
21387
|
-
"companies": ["createDate", "lastActivityDate", "lastTrackedModifiedDateTime"],
|
|
21388
|
-
"company_notes": ["completedDateTime", "createDateTime", "endDateTime", "lastModifiedDate", "startDateTime"],
|
|
21389
|
-
"configuration_item_billing_product_associations": ["effectiveDate", "expirationDate"],
|
|
21390
|
-
"configuration_item_dns_records": ["createDateTime"],
|
|
21391
|
-
"configuration_item_notes": ["createDateTime", "lastActivityDate"],
|
|
21392
|
-
"configuration_items": ["createDate", "dattoLastCheckInDateTime", "domainRegistrationDateTime", "domainLastUpdatedDateTime", "domainExpirationDateTime", "installDate", "lastModifiedTime", "sslValidFromDateTime", "sslValidUntilDateTime", "warrantyExpirationDate"],
|
|
21393
|
-
"contacts": ["bulkEmailOptOutTime", "createDate", "lastActivityDate", "lastModifiedDate", "solicitationOptOutTime"],
|
|
21394
|
-
"contract_billing_rules": ["endDate", "startDate"],
|
|
21395
|
-
"contract_blocks": ["datePurchased", "endDate", "startDate"],
|
|
21396
|
-
"contract_charges": ["createDate", "datePurchased", "statusLastModifiedDate"],
|
|
21397
|
-
"contract_milestones": ["createDate", "dateDue"],
|
|
21398
|
-
"contract_notes": ["createDateTime", "lastActivityDate"],
|
|
21399
|
-
"contract_retainers": ["datePurchased", "endDate", "startDate"],
|
|
21400
|
-
"contract_service_bundle_units": ["approveAndPostDate", "endDate", "startDate"],
|
|
21401
|
-
"contract_service_units": ["approveAndPostDate", "endDate", "startDate"],
|
|
21402
|
-
"contract_ticket_purchases": ["datePurchased", "endDate", "startDate"],
|
|
21403
|
-
"contracts": ["endDate", "lastModifiedDateTime", "startDate"],
|
|
21404
|
-
"currencies": ["lastModifiedDateTime"],
|
|
21405
|
-
"expense_item_attachments": ["attachDate"],
|
|
21406
|
-
"expense_items": ["expenseDate"],
|
|
21407
|
-
"expense_reports": ["approvedDate", "submitDate", "weekEnding"],
|
|
21408
|
-
"holidays": ["holidayDate"],
|
|
21409
|
-
"inventory_products": ["createDateTime"],
|
|
21410
|
-
"invoices": ["createDateTime", "dueDate", "fromDate", "invoiceDateTime", "paidDate", "toDate", "voidedDate", "webServiceDate"],
|
|
21411
|
-
"notification_history": ["notificationSentTime"],
|
|
21412
|
-
"opportunities": ["closedDate", "createDate", "lastActivity", "lostDate", "projectedCloseDate", "promisedFulfillmentDate", "startDate", "throughDate"],
|
|
21413
|
-
"opportunity_attachments": ["attachDate"],
|
|
21414
|
-
"phases": ["createDate", "dueDate", "lastActivityDateTime", "startDate"],
|
|
21415
|
-
"products": ["createdTime"],
|
|
21416
|
-
"project_charges": ["createDate", "datePurchased", "statusLastModifiedDate"],
|
|
21417
|
-
"project_notes": ["createDateTime", "lastActivityDate"],
|
|
21418
|
-
"projects": ["completedDateTime", "createDateTime", "endDateTime", "lastActivityDateTime", "startDateTime", "statusDateTime"],
|
|
21419
|
-
"purchase_order_item_receiving": ["receiveDate"],
|
|
21420
|
-
"purchase_order_items": ["estimatedArrivalDate"],
|
|
21421
|
-
"purchase_orders": ["cancelDateTime", "createDateTime", "latestEstimatedArrivalDate", "shippingDate", "submitDateTime"],
|
|
21422
|
-
"quote_templates": ["createDate", "lastActivityDate"],
|
|
21423
|
-
"quotes": ["approvalStatusChangedDate", "createDate", "effectiveDate", "expirationDate", "extApprovalResponseDate", "lastActivityDate", "lastPublishedDateTime"],
|
|
21424
|
-
"resources": ["hireDate"],
|
|
21425
|
-
"sales_orders": ["promisedFulfillmentDate", "salesOrderDate"],
|
|
21426
|
-
"service_calls": ["canceledDateTime", "createDateTime", "endDateTime", "lastModifiedDateTime", "startDateTime"],
|
|
21427
|
-
"services": ["createDate", "lastModifiedDate"],
|
|
21428
|
-
"subscription_periods": ["periodDate", "postedDate"],
|
|
21429
|
-
"subscriptions": ["effectiveDate", "expirationDate"],
|
|
21430
|
-
"survey_results": ["completeDate", "sendDate"],
|
|
21431
|
-
"tags": ["createDateTime", "lastModifiedDateTime"],
|
|
21432
|
-
"tasks": ["completedDateTime", "createDateTime", "endDateTime", "lastActivityDateTime", "startDateTime"],
|
|
21433
|
-
"ticket_attachments": ["attachDate"],
|
|
21434
|
-
"ticket_change_request_approvals": ["approveRejectDateTime"],
|
|
21435
|
-
"ticket_charges": ["createDate", "datePurchased", "statusLastModifiedDate"],
|
|
21436
|
-
"ticket_checklist_items": ["completedDateTime"],
|
|
21437
|
-
"ticket_history": ["date"],
|
|
21438
|
-
"ticket_note_attachments": ["attachDate"],
|
|
21439
|
-
"ticket_notes": ["createDateTime", "lastActivityDate"],
|
|
21440
|
-
"tickets": ["completedDate", "createDate", "dueDateTime", "firstResponseDateTime", "firstResponseDueDateTime", "lastActivityDate", "lastCustomerNotificationDateTime", "lastCustomerVisibleActivityDateTime", "lastTrackedModificationDateTime", "resolutionPlanDateTime", "resolutionPlanDueDateTime", "resolvedDateTime", "resolvedDueDateTime"],
|
|
21441
|
-
"time_entries": ["billingApprovalDateTime", "createDateTime", "dateWorked", "endDateTime", "lastModifiedDateTime", "startDateTime"],
|
|
21442
|
-
"time_entry_attachments": ["attachDate"],
|
|
21443
|
-
"time_off_requests": ["approvedDateTime", "createDateTime", "endTime", "lastModifiedDateTime", "requestDate", "startTime"]
|
|
21444
|
-
};
|
|
21445
|
-
|
|
21446
21746
|
// src/operations/invoke/fieldNames.ts
|
|
21447
21747
|
async function liveFieldNames(holder) {
|
|
21748
|
+
if (holder === void 0) return null;
|
|
21448
21749
|
const reader = holder.getFieldInfo;
|
|
21449
21750
|
if (typeof reader !== "function") return null;
|
|
21450
21751
|
try {
|
|
@@ -21478,7 +21779,7 @@ function collectFilterFieldNames(conditions) {
|
|
|
21478
21779
|
continue;
|
|
21479
21780
|
}
|
|
21480
21781
|
if (cond.udf === true) continue;
|
|
21481
|
-
if (typeof cond.field === "string" && cond.field.length > 0) out.push(cond.field);
|
|
21782
|
+
if (typeof cond.field === "string" && cond.field.length > 0 && !cond.field.includes(".")) out.push(cond.field);
|
|
21482
21783
|
}
|
|
21483
21784
|
};
|
|
21484
21785
|
walk(conditions);
|
|
@@ -21556,16 +21857,19 @@ async function validateQueryFieldNames(record, holder, args) {
|
|
|
21556
21857
|
filterNames = collectFilterFieldNames(conditions ?? []);
|
|
21557
21858
|
}
|
|
21558
21859
|
if (fieldCheck === false) return;
|
|
21559
|
-
const
|
|
21560
|
-
|
|
21561
|
-
|
|
21860
|
+
const found = await findUnknownFieldNames(record, holder, { includeFields: includeNames ?? [], filter: filterNames ?? [] });
|
|
21861
|
+
if (found === void 0) return;
|
|
21862
|
+
const { known, authority, unknown } = found;
|
|
21863
|
+
if (unknown["includeFields"].length === 0 && unknown["filter"].length === 0) return;
|
|
21864
|
+
throw unknownNamesRefusal(record, authority, known, unknown["includeFields"], unknown["filter"]);
|
|
21865
|
+
}
|
|
21866
|
+
async function findUnknownFieldNames(record, holder, lists) {
|
|
21867
|
+
if (Object.values(lists).every((names) => names.length === 0)) return void 0;
|
|
21562
21868
|
const resolved = resolveAuthority(record, await liveFieldNames(holder));
|
|
21563
|
-
if (resolved === void 0) return;
|
|
21564
|
-
const
|
|
21565
|
-
const
|
|
21566
|
-
|
|
21567
|
-
if (unknownInclude.length === 0 && unknownFilter.length === 0) return;
|
|
21568
|
-
throw unknownNamesRefusal(record, authority, known, unknownInclude, unknownFilter);
|
|
21869
|
+
if (resolved === void 0) return void 0;
|
|
21870
|
+
const unknown = {};
|
|
21871
|
+
for (const [key, names] of Object.entries(lists)) unknown[key] = [...new Set(names.filter((name) => !resolved.known.includes(name)))];
|
|
21872
|
+
return { ...resolved, unknown };
|
|
21569
21873
|
}
|
|
21570
21874
|
|
|
21571
21875
|
// src/resources/special/_shared/ticketStatus.ts
|
|
@@ -21995,7 +22299,7 @@ async function prepareCrmWrite(resource, input, http) {
|
|
|
21995
22299
|
|
|
21996
22300
|
// src/resources/special/companies/unbilledWorkTypes.ts
|
|
21997
22301
|
var UNBILLED_WORK_NOTE_CHARS_DEFAULT = 400;
|
|
21998
|
-
var UNBILLED_WORK_JOIN_SET_CAP =
|
|
22302
|
+
var UNBILLED_WORK_JOIN_SET_CAP = JOIN_SET_CAP;
|
|
21999
22303
|
|
|
22000
22304
|
// src/resources/special/companies/unbilledWorkValidate.ts
|
|
22001
22305
|
var UNBILLED_WORK_OPT_KEYS = [
|
|
@@ -22104,11 +22408,6 @@ function validateUnbilledWorkOptions(companyId, opts) {
|
|
|
22104
22408
|
|
|
22105
22409
|
// src/resources/special/companies/unbilledWork.ts
|
|
22106
22410
|
var OP3 = "companies.unbilledWork";
|
|
22107
|
-
function capJoinSet(ids2, cap) {
|
|
22108
|
-
const sorted = [...new Set(ids2)].sort((a, b) => a - b);
|
|
22109
|
-
if (sorted.length <= cap) return { ids: sorted, truncated: false };
|
|
22110
|
-
return { ids: sorted.slice(0, cap), truncated: true };
|
|
22111
|
-
}
|
|
22112
22411
|
function unbilledWorkPartStatus(key, conditions, items) {
|
|
22113
22412
|
if (items.length !== 0) return { status: "ok" };
|
|
22114
22413
|
const stamp = blindStampFor(BLIND_SURFACE_STAMPS[key]?.query, conditions);
|
|
@@ -22554,6 +22853,7 @@ async function fetchMany(client, items, opts = {}) {
|
|
|
22554
22853
|
if (opts.liveFields !== void 0 && typeof opts.liveFields !== "boolean") {
|
|
22555
22854
|
throw new AutotaskConfigError(`fetchMany: liveFields must be a boolean (got ${typeof opts.liveFields})`);
|
|
22556
22855
|
}
|
|
22856
|
+
const labels = readNamesOpt(opts.names, "fetchMany");
|
|
22557
22857
|
const cap = Math.min(FETCH_MANY_MAX_ITEMS, opts.maxItems ?? FETCH_MANY_MAX_ITEMS);
|
|
22558
22858
|
if (opts.liveFields === true) {
|
|
22559
22859
|
validateFetchManyItems(items, cap, void 0, void 0, { fieldCheck: false });
|
|
@@ -22609,7 +22909,8 @@ async function fetchMany(client, items, opts = {}) {
|
|
|
22609
22909
|
}
|
|
22610
22910
|
}
|
|
22611
22911
|
const ok = results.filter((r) => r.found).length;
|
|
22612
|
-
|
|
22912
|
+
const out = { results, ok, failed: results.length - ok };
|
|
22913
|
+
return labels ? labelFetchResults(client, out) : out;
|
|
22613
22914
|
}
|
|
22614
22915
|
|
|
22615
22916
|
// src/operations/fetchByIds.ts
|
|
@@ -22637,6 +22938,7 @@ async function fetchByIds(client, items, opts = {}) {
|
|
|
22637
22938
|
throw new AutotaskConfigError(`fetchByIds: maxItems must be an integer >= 1 (got ${String(opts.maxItems)})`);
|
|
22638
22939
|
}
|
|
22639
22940
|
}
|
|
22941
|
+
const labels = readNamesOpt(opts.names, "fetchByIds");
|
|
22640
22942
|
const cap = Math.min(FETCH_BY_IDS_MAX_ITEMS, opts.maxItems ?? FETCH_BY_IDS_MAX_ITEMS);
|
|
22641
22943
|
validateFetchManyItems(items, cap, "fetchByIds", `FETCH_BY_IDS_MAX_ITEMS = ${FETCH_BY_IDS_MAX_ITEMS}`);
|
|
22642
22944
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -22682,8 +22984,8 @@ async function fetchByIds(client, items, opts = {}) {
|
|
|
22682
22984
|
maxRecords: FETCH_BY_IDS_CHUNK_SIZE,
|
|
22683
22985
|
includeFields
|
|
22684
22986
|
});
|
|
22685
|
-
const
|
|
22686
|
-
const { items: rows } = unwrapItems(
|
|
22987
|
+
const res2 = await client.http.request({ ...req, operation: `${key}.fetchByIds` });
|
|
22988
|
+
const { items: rows } = unwrapItems(res2, `${key}.fetchByIds`);
|
|
22687
22989
|
for (const row of rows) {
|
|
22688
22990
|
const k = String(row.id);
|
|
22689
22991
|
if (!chunkKeys.has(k)) continue;
|
|
@@ -22733,7 +23035,8 @@ async function fetchByIds(client, items, opts = {}) {
|
|
|
22733
23035
|
);
|
|
22734
23036
|
}
|
|
22735
23037
|
const ok = results.filter((r) => r.found).length;
|
|
22736
|
-
|
|
23038
|
+
const res = { results, ok, failed: results.length - ok };
|
|
23039
|
+
return labels ? labelFetchResults(client, res) : res;
|
|
22737
23040
|
}
|
|
22738
23041
|
|
|
22739
23042
|
// src/resources/special/contracts/lineOfBusiness.ts
|
|
@@ -26048,7 +26351,8 @@ var TimeEntriesResource = class extends BaseResource {
|
|
|
26048
26351
|
postedWithin(from, to, opts) {
|
|
26049
26352
|
return postedWithin(this, from, to, this.http, opts);
|
|
26050
26353
|
}
|
|
26051
|
-
/** Node-parity special op (hand-written in src/resources/special/time_entries.ts).
|
|
26354
|
+
/** Node-parity special op (hand-written in src/resources/special/time_entries.ts).
|
|
26355
|
+
* @deprecated Since 0.38.0, removed in the next minor. Use operations.aggregate(client, 'time_entries.query', [{ field: 'resourceID', op: 'eq', value: R }, { field: 'dateWorked', op: 'gte', value: FROM }, { field: 'dateWorked', op: 'lte', value: TO }], { groupBy: ['ticketID'], sum: ['hoursWorked'] }) — group keys carry ticketIDName; add { field: 'ticket.companyID', op: 'eq', value: C } to scope by company. */
|
|
26052
26356
|
recentTickets(from, to, opts) {
|
|
26053
26357
|
return recentTickets(this, from, to, this.http, opts);
|
|
26054
26358
|
}
|
|
@@ -26181,6 +26485,8 @@ export {
|
|
|
26181
26485
|
validateFilter,
|
|
26182
26486
|
buildFilter,
|
|
26183
26487
|
normalizedConditions,
|
|
26488
|
+
normalizeInValues,
|
|
26489
|
+
assertNoRelationLeaf,
|
|
26184
26490
|
buildUrl,
|
|
26185
26491
|
cursorToPath,
|
|
26186
26492
|
buildQueryBody,
|
|
@@ -26213,16 +26519,7 @@ export {
|
|
|
26213
26519
|
FIELD_PRESETS,
|
|
26214
26520
|
FIELD_PRESET_NAMES,
|
|
26215
26521
|
resolveFieldsPreset,
|
|
26216
|
-
BaseResource,
|
|
26217
|
-
BillingCodesResource,
|
|
26218
|
-
BillingItemsResource,
|
|
26219
|
-
isDateYMD,
|
|
26220
|
-
monthsInWindow,
|
|
26221
|
-
dayAfter,
|
|
26222
|
-
num,
|
|
26223
|
-
MODEL_FIELD_SETS,
|
|
26224
26522
|
MODEL_DATE_FIELDS,
|
|
26225
|
-
validateQueryFieldNames,
|
|
26226
26523
|
EXPAND_RESOURCE_NAMES_MAX_IDS,
|
|
26227
26524
|
expandResourceNames,
|
|
26228
26525
|
ENTITY_TYPE_TO_RESOURCE,
|
|
@@ -26240,6 +26537,18 @@ export {
|
|
|
26240
26537
|
vendorId,
|
|
26241
26538
|
resolveLineOfBusinessChain,
|
|
26242
26539
|
resolveCompositeNames,
|
|
26540
|
+
readNamesOpt,
|
|
26541
|
+
labelRows,
|
|
26542
|
+
BaseResource,
|
|
26543
|
+
BillingCodesResource,
|
|
26544
|
+
BillingItemsResource,
|
|
26545
|
+
isDateYMD,
|
|
26546
|
+
monthsInWindow,
|
|
26547
|
+
dayAfter,
|
|
26548
|
+
num,
|
|
26549
|
+
collectFilterFieldNames,
|
|
26550
|
+
validateQueryFieldNames,
|
|
26551
|
+
findUnknownFieldNames,
|
|
26243
26552
|
resolveTicketStatusSemantics,
|
|
26244
26553
|
openStatusFilter,
|
|
26245
26554
|
isClosedStatus,
|