@vertexvis/api-client-node 0.20.10 → 0.21.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/api.d.ts +6 -6
- package/dist/cjs/api.js +176 -88
- package/dist/cjs/client/helpers/scene-items.d.ts +21 -0
- package/dist/cjs/client/helpers/scene-items.js +40 -1
- package/dist/cjs/client/helpers/scenes.d.ts +21 -38
- package/dist/cjs/client/helpers/scenes.js +231 -192
- package/dist/cjs/client/utils.d.ts +8 -1
- package/dist/cjs/client/utils.js +21 -2
- package/dist/cjs/client/version.d.ts +1 -1
- package/dist/cjs/client/version.js +1 -1
- package/dist/esm/api.d.ts +6 -6
- package/dist/esm/api.js +176 -88
- package/dist/esm/client/helpers/scene-items.d.ts +21 -0
- package/dist/esm/client/helpers/scene-items.js +38 -1
- package/dist/esm/client/helpers/scenes.d.ts +21 -38
- package/dist/esm/client/helpers/scenes.js +229 -188
- package/dist/esm/client/utils.d.ts +8 -1
- package/dist/esm/client/utils.js +18 -1
- package/dist/esm/client/version.d.ts +1 -1
- package/dist/esm/client/version.js +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
import { CreateSceneItemRequest, SceneItem } from '../../index';
|
|
2
2
|
import { BaseReq, Polling } from '../index';
|
|
3
|
+
export declare enum SceneItemErrorStatus {
|
|
4
|
+
NotFound = "404",
|
|
5
|
+
ServerError = "500"
|
|
6
|
+
}
|
|
7
|
+
export declare enum SceneItemErrorCode {
|
|
8
|
+
NotFound = "NotFound",
|
|
9
|
+
ServerError = "ServerError"
|
|
10
|
+
}
|
|
11
|
+
export declare enum SceneItemErrorSourcePointer {
|
|
12
|
+
Parent = "/body/data/attributes/parent",
|
|
13
|
+
SourcePart = "/body/data/relationships/source/data"
|
|
14
|
+
}
|
|
15
|
+
export declare enum SceneItemSystemMetadata {
|
|
16
|
+
IsMissingGeometry = "VERTEX_IS_MISSING_GEOMETRY",
|
|
17
|
+
MissingGeometrySetId = "VERTEX_MISSING_GEOMETRY_SET_ID",
|
|
18
|
+
MissingPartRevisionId = "VERTEX_MISSING_PART_REVISION_ID",
|
|
19
|
+
MissingSuppliedPartId = "VERTEX_MISSING_SUPPLIED_PART_ID",
|
|
20
|
+
MissingSuppliedPartRevisionId = "VERTEX_MISSING_SUPPLIED_PART_REVISION_ID"
|
|
21
|
+
}
|
|
3
22
|
/**
|
|
4
23
|
* Create scene item arguments.
|
|
5
24
|
*/
|
|
@@ -17,3 +36,5 @@ export interface CreateSceneItemReq extends BaseReq {
|
|
|
17
36
|
* @param args - The {@link CreateSceneItemReq}.
|
|
18
37
|
*/
|
|
19
38
|
export declare function createSceneItem({ client, createSceneItemReq, onMsg, polling, sceneId, verbose, }: CreateSceneItemReq): Promise<SceneItem>;
|
|
39
|
+
export declare function isPartNotFoundError(e: unknown): boolean;
|
|
40
|
+
export declare function isParentNotFoundError(e: unknown): boolean;
|
|
@@ -7,7 +7,30 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import { isPollError, MaxAttempts, PollIntervalMs, pollQueuedJob, throwOnError, } from '../index';
|
|
10
|
+
import { defined, isApiError, isPollError, MaxAttempts, PollIntervalMs, pollQueuedJob, throwOnError, } from '../index';
|
|
11
|
+
export var SceneItemErrorStatus;
|
|
12
|
+
(function (SceneItemErrorStatus) {
|
|
13
|
+
SceneItemErrorStatus["NotFound"] = "404";
|
|
14
|
+
SceneItemErrorStatus["ServerError"] = "500";
|
|
15
|
+
})(SceneItemErrorStatus || (SceneItemErrorStatus = {}));
|
|
16
|
+
export var SceneItemErrorCode;
|
|
17
|
+
(function (SceneItemErrorCode) {
|
|
18
|
+
SceneItemErrorCode["NotFound"] = "NotFound";
|
|
19
|
+
SceneItemErrorCode["ServerError"] = "ServerError";
|
|
20
|
+
})(SceneItemErrorCode || (SceneItemErrorCode = {}));
|
|
21
|
+
export var SceneItemErrorSourcePointer;
|
|
22
|
+
(function (SceneItemErrorSourcePointer) {
|
|
23
|
+
SceneItemErrorSourcePointer["Parent"] = "/body/data/attributes/parent";
|
|
24
|
+
SceneItemErrorSourcePointer["SourcePart"] = "/body/data/relationships/source/data";
|
|
25
|
+
})(SceneItemErrorSourcePointer || (SceneItemErrorSourcePointer = {}));
|
|
26
|
+
export var SceneItemSystemMetadata;
|
|
27
|
+
(function (SceneItemSystemMetadata) {
|
|
28
|
+
SceneItemSystemMetadata["IsMissingGeometry"] = "VERTEX_IS_MISSING_GEOMETRY";
|
|
29
|
+
SceneItemSystemMetadata["MissingGeometrySetId"] = "VERTEX_MISSING_GEOMETRY_SET_ID";
|
|
30
|
+
SceneItemSystemMetadata["MissingPartRevisionId"] = "VERTEX_MISSING_PART_REVISION_ID";
|
|
31
|
+
SceneItemSystemMetadata["MissingSuppliedPartId"] = "VERTEX_MISSING_SUPPLIED_PART_ID";
|
|
32
|
+
SceneItemSystemMetadata["MissingSuppliedPartRevisionId"] = "VERTEX_MISSING_SUPPLIED_PART_REVISION_ID";
|
|
33
|
+
})(SceneItemSystemMetadata || (SceneItemSystemMetadata = {}));
|
|
11
34
|
/**
|
|
12
35
|
* Create a scene item.
|
|
13
36
|
*
|
|
@@ -34,3 +57,17 @@ export function createSceneItem({ client, createSceneItemReq, onMsg = console.lo
|
|
|
34
57
|
return pollRes.res;
|
|
35
58
|
});
|
|
36
59
|
}
|
|
60
|
+
export function isPartNotFoundError(e) {
|
|
61
|
+
return (defined(e) &&
|
|
62
|
+
isApiError(e) &&
|
|
63
|
+
e.code === SceneItemErrorCode.NotFound &&
|
|
64
|
+
e.source !== undefined &&
|
|
65
|
+
e.source.pointer === SceneItemErrorSourcePointer.SourcePart);
|
|
66
|
+
}
|
|
67
|
+
export function isParentNotFoundError(e) {
|
|
68
|
+
return (defined(e) &&
|
|
69
|
+
isApiError(e) &&
|
|
70
|
+
e.code === SceneItemErrorCode.NotFound &&
|
|
71
|
+
e.source !== undefined &&
|
|
72
|
+
e.source.pointer === SceneItemErrorSourcePointer.Parent);
|
|
73
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { AxiosResponse } from 'axios';
|
|
2
|
+
import { Limit } from 'p-limit';
|
|
3
|
+
import { RelationshipData } from '../../index';
|
|
2
4
|
import { ApiError, BatchOperation, CreateSceneItemRequest, CreateSceneItemRequestData, CreateSceneRequest, Failure, QueuedJob, Scene, SceneData, UpdateSceneRequestDataAttributes } from '../../index';
|
|
3
5
|
import { BaseReq, DeleteReq, Polling, RenderImageReq, VertexClient } from '../index';
|
|
4
6
|
export interface CreateSceneAndSceneItemsReq extends BaseReq {
|
|
@@ -17,35 +19,23 @@ export interface CreateSceneAndSceneItemsReq extends BaseReq {
|
|
|
17
19
|
/** Whether or not to return queued scene items. */
|
|
18
20
|
readonly returnQueued?: boolean;
|
|
19
21
|
}
|
|
20
|
-
export interface CreateSceneAndSceneItemsReqEXPERIMENTAL extends BaseReq {
|
|
21
|
-
/** A list of {@link CreateSceneItemRequest}. */
|
|
22
|
-
readonly createSceneItemReqs: Array<Array<CreateSceneItemRequest>>;
|
|
23
|
-
/** Function returning a {@link CreateSceneRequest}. */
|
|
24
|
-
readonly createSceneReq: () => CreateSceneRequest;
|
|
25
|
-
/** Whether or not to fail if any scene item fails initial validation. */
|
|
26
|
-
readonly failFast?: boolean;
|
|
27
|
-
/** How many requests to run in parallel. */
|
|
28
|
-
readonly parallelism: number;
|
|
29
|
-
/** {@link Polling} */
|
|
30
|
-
readonly polling?: Polling;
|
|
31
|
-
/** Callback with total number of requests and number complete. */
|
|
32
|
-
onProgress?: (complete: number, total: number) => void;
|
|
33
|
-
/** Whether or not to return queued scene items. */
|
|
34
|
-
readonly returnQueued?: boolean;
|
|
35
|
-
}
|
|
36
22
|
export interface CreateSceneAndSceneItemsRes {
|
|
37
|
-
readonly errors: QueuedSceneItem[];
|
|
38
|
-
readonly scene: Scene;
|
|
39
|
-
/** Only populated if `returnQueued` is true in request. */
|
|
40
|
-
readonly queued: QueuedSceneItem[];
|
|
41
|
-
}
|
|
42
|
-
export interface CreateSceneAndSceneItemsResEXPERIMENTAL {
|
|
43
23
|
readonly errors: QueuedBatchOps[];
|
|
44
|
-
readonly scene
|
|
24
|
+
readonly scene?: Scene;
|
|
45
25
|
readonly sceneItemErrors: SceneItemError[];
|
|
46
26
|
/** Only populated if `returnQueued` is true in request. */
|
|
47
27
|
readonly queued: QueuedBatchOps[];
|
|
48
28
|
}
|
|
29
|
+
export interface CreateSceneItemBatchReq extends CreateSceneItemsReq {
|
|
30
|
+
/** {@link Polling} */
|
|
31
|
+
readonly polling?: Polling;
|
|
32
|
+
}
|
|
33
|
+
export interface CreateSceneItemBatchRes {
|
|
34
|
+
batchOps: QueuedBatchOps[];
|
|
35
|
+
batchErrors: QueuedBatchOps[];
|
|
36
|
+
itemErrors: SceneItemError[];
|
|
37
|
+
itemResults: SceneItemResult[];
|
|
38
|
+
}
|
|
49
39
|
export interface CreateSceneItemsReq extends Base {
|
|
50
40
|
/** A list of {@link CreateSceneItemRequest}. */
|
|
51
41
|
readonly createSceneItemReqs: CreateSceneItemRequest[];
|
|
@@ -53,14 +43,10 @@ export interface CreateSceneItemsReq extends Base {
|
|
|
53
43
|
readonly failFast: boolean;
|
|
54
44
|
/** Callback with total number of requests and number complete. */
|
|
55
45
|
readonly onProgress?: (complete: number, total: number) => void;
|
|
56
|
-
/**
|
|
57
|
-
readonly
|
|
46
|
+
/** Limit for requests to run in parallel. */
|
|
47
|
+
readonly limit: Limit;
|
|
58
48
|
}
|
|
59
49
|
export interface CreateSceneItemsRes {
|
|
60
|
-
readonly leaves: number;
|
|
61
|
-
readonly queuedSceneItems: QueuedSceneItem[];
|
|
62
|
-
}
|
|
63
|
-
export interface CreateSceneItemsResEXPERIMENTAL {
|
|
64
50
|
readonly chunks: number;
|
|
65
51
|
readonly queuedBatchOps: QueuedBatchOps[];
|
|
66
52
|
}
|
|
@@ -71,6 +57,11 @@ export interface QueuedBatchOps {
|
|
|
71
57
|
export interface SceneItemError {
|
|
72
58
|
readonly req: CreateSceneItemRequestData;
|
|
73
59
|
readonly res?: ApiError;
|
|
60
|
+
placeholderItem?: RelationshipData;
|
|
61
|
+
}
|
|
62
|
+
export interface SceneItemResult {
|
|
63
|
+
readonly req: CreateSceneItemRequestData;
|
|
64
|
+
readonly res: RelationshipData | ApiError;
|
|
74
65
|
}
|
|
75
66
|
/**
|
|
76
67
|
* Poll scene ready arguments.
|
|
@@ -104,15 +95,7 @@ export declare function createSceneAndSceneItems({ client, createSceneItemReqs,
|
|
|
104
95
|
/**
|
|
105
96
|
* Create scene items within a scene.
|
|
106
97
|
*/
|
|
107
|
-
export declare function createSceneItems({ client, createSceneItemReqs, failFast,
|
|
108
|
-
/**
|
|
109
|
-
* Create a scene with scene items using experimental strategy.
|
|
110
|
-
*/
|
|
111
|
-
export declare function createSceneAndSceneItemsEXPERIMENTAL({ client, createSceneItemReqs, createSceneReq, failFast, onMsg, onProgress, parallelism, polling, returnQueued, verbose, }: CreateSceneAndSceneItemsReqEXPERIMENTAL): Promise<CreateSceneAndSceneItemsResEXPERIMENTAL>;
|
|
112
|
-
/**
|
|
113
|
-
* Create scene items within a scene.
|
|
114
|
-
*/
|
|
115
|
-
export declare function createSceneItemsEXPERIMENTAL({ client, createSceneItemReqs, failFast, parallelism, sceneId, }: CreateSceneItemsReq): Promise<CreateSceneItemsResEXPERIMENTAL>;
|
|
98
|
+
export declare function createSceneItems({ client, createSceneItemReqs, failFast, limit, sceneId, }: CreateSceneItemsReq): Promise<CreateSceneItemsRes>;
|
|
116
99
|
/**
|
|
117
100
|
* Delete all scenes.
|
|
118
101
|
*
|
|
@@ -10,239 +10,280 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
import pLimit from 'p-limit';
|
|
11
11
|
import { hrtime } from 'process';
|
|
12
12
|
import { BatchOperationOpEnum, BatchOperationRefTypeEnum, CameraFitTypeEnum, SceneRelationshipDataTypeEnum, UpdateSceneRequestDataAttributesStateEnum, } from '../../index';
|
|
13
|
-
import { defined, getPage, hasVertexError, isQueuedJob, MaxAttempts, partition, PollIntervalMs, tryStream, } from '../index';
|
|
14
|
-
import { arrayChunked, delay, isApiError, toAccept } from '../utils';
|
|
13
|
+
import { defined, getPage, hasVertexError, isPartNotFoundError, isQueuedJob, MaxAttempts, partition, PollIntervalMs, SceneItemSystemMetadata, tryStream, } from '../index';
|
|
14
|
+
import { arrayChunked, delay, formatTime, isApiError, isSceneItemRelationship, toAccept, } from '../utils';
|
|
15
15
|
import { isBatch, isPollError, pollQueuedJob, throwOnError, } from './queued-jobs';
|
|
16
|
-
const
|
|
16
|
+
const defaultPolling = { intervalMs: 200, maxAttempts: 4500 }; // 15 minute timeout for batch completions
|
|
17
|
+
const sceneReadyPolling = { intervalMs: 1000, maxAttempts: 3600 }; // one hour timeout for scene state ready
|
|
17
18
|
/**
|
|
18
19
|
* Create a scene with scene items.
|
|
19
20
|
*/
|
|
20
|
-
export function createSceneAndSceneItems({ client, createSceneItemReqs, createSceneReq, failFast = false, onMsg = console.log, onProgress, parallelism, polling =
|
|
21
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
22
|
-
const scene = (yield client.scenes.createScene({ createSceneRequest: createSceneReq() })).data;
|
|
23
|
-
const sceneId = scene.data.id;
|
|
24
|
-
const createRes = yield createSceneItems({
|
|
25
|
-
client,
|
|
26
|
-
createSceneItemReqs,
|
|
27
|
-
failFast,
|
|
28
|
-
onProgress,
|
|
29
|
-
parallelism,
|
|
30
|
-
sceneId,
|
|
31
|
-
});
|
|
32
|
-
const { a: queuedItems, b: errors } = partition(createRes.queuedSceneItems, (i) => isQueuedJob(i.res));
|
|
33
|
-
const queued = returnQueued ? createRes.queuedSceneItems : [];
|
|
34
|
-
if (queuedItems.length === 0 || errors.length === createRes.leaves) {
|
|
35
|
-
return { errors, queued, scene };
|
|
36
|
-
}
|
|
37
|
-
if (verbose)
|
|
38
|
-
onMsg(`Polling for completed scene-items...`);
|
|
39
|
-
const limit = pLimit(Math.min(parallelism, 20));
|
|
40
|
-
const cnt = queuedItems.length;
|
|
41
|
-
const step = Math.floor(cnt / ((PollPercentage / 100) * cnt));
|
|
42
|
-
const qis = [];
|
|
43
|
-
// Poll for percentage of items starting at end of `queuedItems` array
|
|
44
|
-
for (let i = 0; i < cnt; i += step)
|
|
45
|
-
qis.push(queuedItems[cnt - i - 1]);
|
|
46
|
-
function poll({ req, res }) {
|
|
47
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
48
|
-
const r = yield pollQueuedJob({
|
|
49
|
-
id: res.data.id,
|
|
50
|
-
getQueuedJob: (id, cancelToken) => client.sceneItems.getQueuedSceneItem({ id }, { cancelToken }),
|
|
51
|
-
allow404: true,
|
|
52
|
-
limit,
|
|
53
|
-
polling,
|
|
54
|
-
});
|
|
55
|
-
if (isPollError(r.res)) {
|
|
56
|
-
failFast ? throwOnError(r) : errors.push({ req, res: r.res });
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
yield Promise.all(qis.map((is) => limit(poll, is)));
|
|
61
|
-
if (verbose)
|
|
62
|
-
onMsg(`Committing scene and polling until ready...`);
|
|
63
|
-
yield updateScene({
|
|
64
|
-
attributes: { state: UpdateSceneRequestDataAttributesStateEnum.Commit },
|
|
65
|
-
client,
|
|
66
|
-
sceneId,
|
|
67
|
-
});
|
|
68
|
-
yield pollSceneReady({ client, id: sceneId, onMsg, polling, verbose });
|
|
69
|
-
if (verbose)
|
|
70
|
-
onMsg(`Fitting scene's camera to scene-items...`);
|
|
71
|
-
const updated = (yield updateScene({
|
|
72
|
-
attributes: { camera: { type: CameraFitTypeEnum.FitVisibleSceneItems } },
|
|
73
|
-
client,
|
|
74
|
-
sceneId,
|
|
75
|
-
})).scene;
|
|
76
|
-
return { errors, queued, scene: updated };
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Create scene items within a scene.
|
|
81
|
-
*/
|
|
82
|
-
export function createSceneItems({ client, createSceneItemReqs, failFast, onProgress, parallelism, sceneId, }) {
|
|
83
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
84
|
-
const limit = pLimit(parallelism);
|
|
85
|
-
let complete = 0;
|
|
86
|
-
let leaves = 0;
|
|
87
|
-
const queuedSceneItems = yield Promise.all(createSceneItemReqs.map((r) => limit((req) => __awaiter(this, void 0, void 0, function* () {
|
|
88
|
-
var _a;
|
|
89
|
-
let res;
|
|
90
|
-
try {
|
|
91
|
-
if (defined(req.data.attributes.source) ||
|
|
92
|
-
defined(req.data.relationships.source)) {
|
|
93
|
-
leaves++;
|
|
94
|
-
}
|
|
95
|
-
res = (yield client.sceneItems.createSceneItem({
|
|
96
|
-
id: sceneId,
|
|
97
|
-
createSceneItemRequest: req,
|
|
98
|
-
})).data;
|
|
99
|
-
}
|
|
100
|
-
catch (error) {
|
|
101
|
-
if (!failFast && hasVertexError(error)) {
|
|
102
|
-
res = (_a = error.vertexError) === null || _a === void 0 ? void 0 : _a.res;
|
|
103
|
-
}
|
|
104
|
-
else
|
|
105
|
-
throw error;
|
|
106
|
-
}
|
|
107
|
-
if (onProgress != null) {
|
|
108
|
-
complete += 1;
|
|
109
|
-
onProgress(complete, createSceneItemReqs.length);
|
|
110
|
-
}
|
|
111
|
-
return { req, res };
|
|
112
|
-
}), r)));
|
|
113
|
-
return { leaves, queuedSceneItems };
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Create a scene with scene items using experimental strategy.
|
|
118
|
-
*/
|
|
119
|
-
export function createSceneAndSceneItemsEXPERIMENTAL({ client, createSceneItemReqs, createSceneReq, failFast = false, onMsg = console.log, onProgress, parallelism, polling = { intervalMs: PollIntervalMs, maxAttempts: MaxAttempts }, returnQueued = false, verbose, }) {
|
|
21
|
+
export function createSceneAndSceneItems({ client, createSceneItemReqs, createSceneReq, failFast = false, onMsg = console.log, onProgress, parallelism, polling = defaultPolling, returnQueued = false, verbose, }) {
|
|
120
22
|
return __awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
const limit = pLimit(Math.min(parallelism, 100));
|
|
121
24
|
const startTime = hrtime.bigint();
|
|
122
25
|
if (verbose)
|
|
123
26
|
onMsg(`Creating scene...`);
|
|
124
27
|
const scene = (yield client.scenes.createScene({ createSceneRequest: createSceneReq() })).data;
|
|
125
28
|
const sceneId = scene.data.id;
|
|
29
|
+
if (verbose)
|
|
30
|
+
onMsg(`Scene ID: ${sceneId}`);
|
|
126
31
|
if (verbose)
|
|
127
32
|
onMsg(`Creating scene items...`);
|
|
128
33
|
let itemCount = 0;
|
|
129
|
-
let
|
|
130
|
-
let
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
34
|
+
let createFailed = false;
|
|
35
|
+
let sceneResult;
|
|
36
|
+
const reqMap = new Map();
|
|
37
|
+
// create parent map and set ordinals based on request order
|
|
38
|
+
createSceneItemReqs.forEach((req) => {
|
|
39
|
+
var _a, _b, _c;
|
|
40
|
+
const reqParent = (_c = (_a = req.data.attributes.parent) !== null && _a !== void 0 ? _a : (_b = req.data.relationships.parent) === null || _b === void 0 ? void 0 : _b.data.id) !== null && _c !== void 0 ? _c : '';
|
|
41
|
+
if (!reqMap.has(reqParent)) {
|
|
42
|
+
reqMap.set(reqParent, []);
|
|
43
|
+
}
|
|
44
|
+
const siblings = reqMap.get(reqParent);
|
|
45
|
+
if (req.data.attributes.ordinal == null) {
|
|
46
|
+
req.data.attributes.ordinal = siblings === null || siblings === void 0 ? void 0 : siblings.length;
|
|
47
|
+
}
|
|
48
|
+
siblings === null || siblings === void 0 ? void 0 : siblings.push(req);
|
|
49
|
+
});
|
|
50
|
+
// sort all scene item requests into depth sorted array of arrays
|
|
51
|
+
const depthSortedItems = [];
|
|
52
|
+
// fetch list of scene items with no parent (root items)
|
|
53
|
+
let nextChildren = reqMap.get('') || [];
|
|
54
|
+
reqMap.delete('');
|
|
55
|
+
while (nextChildren.length > 0) {
|
|
56
|
+
depthSortedItems.push(nextChildren);
|
|
57
|
+
nextChildren = nextChildren.flatMap((req) => {
|
|
58
|
+
if (req.data.attributes.suppliedId &&
|
|
59
|
+
reqMap.has(req.data.attributes.suppliedId)) {
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
61
|
+
const children = reqMap.get(req.data.attributes.suppliedId);
|
|
62
|
+
reqMap.delete(req.data.attributes.suppliedId);
|
|
63
|
+
return children;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
let resultQueuedOps = [];
|
|
71
|
+
let resultBatchErrors = [];
|
|
72
|
+
let resultItemErrors = [];
|
|
73
|
+
// if we had any scene item requests with invalid parents,
|
|
74
|
+
// add error entries indicating so.
|
|
75
|
+
reqMap.forEach((children) => {
|
|
76
|
+
children.forEach((childItem) => {
|
|
77
|
+
resultItemErrors.push({
|
|
78
|
+
req: childItem.data,
|
|
79
|
+
res: {
|
|
80
|
+
status: '404',
|
|
81
|
+
code: 'NotFound',
|
|
82
|
+
title: 'The requested resource was not found.',
|
|
83
|
+
source: { pointer: '/body/data/attributes/parent' },
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
let depth = 0;
|
|
89
|
+
for (depth; depth < depthSortedItems.length; depth++) {
|
|
90
|
+
const createItemReqs = depthSortedItems[depth];
|
|
134
91
|
itemCount += createItemReqs.length;
|
|
135
92
|
if (verbose)
|
|
136
93
|
onMsg(`Creating ${createItemReqs.length} scene items at depth ${depth}...`);
|
|
137
94
|
// Await is used intentionally to defer loop iteration
|
|
138
95
|
// until all scene items have been created at each depth.
|
|
96
|
+
const { batchOps: queuedBatchOps, batchErrors: queuedBatchErrors, itemErrors: batchItemErrors, } =
|
|
139
97
|
// eslint-disable-next-line no-await-in-loop
|
|
140
|
-
|
|
98
|
+
yield createSceneItemBatch({
|
|
141
99
|
client,
|
|
142
100
|
createSceneItemReqs: createItemReqs,
|
|
143
101
|
failFast,
|
|
144
102
|
onProgress,
|
|
145
|
-
|
|
103
|
+
limit,
|
|
146
104
|
sceneId,
|
|
105
|
+
polling,
|
|
147
106
|
});
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (errors.length) {
|
|
151
|
-
batchErrors = batchErrors.concat(errors);
|
|
152
|
-
if (verbose)
|
|
153
|
-
onMsg(`WARNING: ${errors.length} scene item batch errors at depth ${depth}.`);
|
|
154
|
-
}
|
|
155
|
-
// Nothing succeeded, return early as something is likely wrong
|
|
156
|
-
if (queuedOps.length === 0 || errors.length === createRes.chunks) {
|
|
157
|
-
return {
|
|
158
|
-
errors,
|
|
159
|
-
queued: returnQueued ? createRes.queuedBatchOps : [],
|
|
160
|
-
scene,
|
|
161
|
-
sceneItemErrors: [],
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
const limit = pLimit(Math.min(parallelism, 20));
|
|
165
|
-
function poll({ ops, res, }) {
|
|
166
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
167
|
-
const r = yield pollQueuedJob({
|
|
168
|
-
id: res.data.id,
|
|
169
|
-
getQueuedJob: (id, cancelToken) => client.batches.getQueuedBatch({ id }, { cancelToken }),
|
|
170
|
-
allow404: true,
|
|
171
|
-
limit,
|
|
172
|
-
polling,
|
|
173
|
-
});
|
|
174
|
-
if (isPollError(r.res)) {
|
|
175
|
-
failFast ? throwOnError(r) : errors.push({ ops, res: r.res });
|
|
176
|
-
}
|
|
177
|
-
return r;
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
// eslint-disable-next-line no-await-in-loop
|
|
181
|
-
const batchRes = yield Promise.all(queuedOps.map((is) => limit(poll, is)));
|
|
182
|
-
const batchItemErrors = batchRes
|
|
183
|
-
.flatMap((b, i) => isBatch(b.res)
|
|
184
|
-
? b.res['vertexvis/batch:results'].map((r, j) => isApiError(r)
|
|
185
|
-
? { req: queuedOps[i].ops[j].data, res: r }
|
|
186
|
-
: undefined)
|
|
187
|
-
: [])
|
|
188
|
-
.filter(defined);
|
|
107
|
+
resultQueuedOps = resultQueuedOps.concat(queuedBatchOps);
|
|
108
|
+
resultBatchErrors = resultBatchErrors.concat(queuedBatchErrors);
|
|
189
109
|
if (batchItemErrors.length) {
|
|
190
|
-
sceneItemErrors = sceneItemErrors.concat(batchItemErrors);
|
|
191
110
|
if (verbose)
|
|
192
111
|
onMsg(`WARNING: ${batchItemErrors.length} scene item creation errors at depth ${depth}.`);
|
|
112
|
+
resultItemErrors = resultItemErrors.concat(batchItemErrors);
|
|
113
|
+
if (failFast) {
|
|
114
|
+
createFailed = true;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
// evaluate item errors and generate retry list
|
|
119
|
+
const retryErrors = batchItemErrors.filter((v) => isPartNotFoundError(v.res));
|
|
120
|
+
const retries = retryErrors.map((itemError) => {
|
|
121
|
+
var _a, _b, _c, _d, _e, _f;
|
|
122
|
+
const item = itemError.req;
|
|
123
|
+
return {
|
|
124
|
+
data: {
|
|
125
|
+
type: 'scene-item',
|
|
126
|
+
attributes: Object.assign(Object.assign({}, item.attributes), { metadata: Object.assign(Object.assign({}, item.attributes.metadata), { [SceneItemSystemMetadata.IsMissingGeometry]: toMetadataOrUndefined('1'), [SceneItemSystemMetadata.MissingGeometrySetId]: toMetadataOrUndefined((_a = item.relationships.source) === null || _a === void 0 ? void 0 : _a.data.id, ((_b = item.relationships.source) === null || _b === void 0 ? void 0 : _b.data.type) === 'geometry-set'), [SceneItemSystemMetadata.MissingPartRevisionId]: toMetadataOrUndefined((_c = item.relationships.source) === null || _c === void 0 ? void 0 : _c.data.id, ((_d = item.relationships.source) === null || _d === void 0 ? void 0 : _d.data.type) === 'part-revision'), [SceneItemSystemMetadata.MissingSuppliedPartId]: toMetadataOrUndefined((_e = item.attributes.source) === null || _e === void 0 ? void 0 : _e.suppliedPartId), [SceneItemSystemMetadata.MissingSuppliedPartRevisionId]: toMetadataOrUndefined((_f = item.attributes.source) === null || _f === void 0 ? void 0 : _f.suppliedRevisionId) }), source: undefined }),
|
|
127
|
+
relationships: Object.assign(Object.assign({}, item.relationships), { source: undefined }),
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
if (retries.length > 0) {
|
|
132
|
+
onMsg(`Creating ${retries.length} placeholder scene items at depth ${depth}.`);
|
|
133
|
+
// wait for placeholders to be created
|
|
134
|
+
const { itemResults: placeholderItemResults } =
|
|
135
|
+
// eslint-disable-next-line no-await-in-loop
|
|
136
|
+
yield createSceneItemBatch({
|
|
137
|
+
client,
|
|
138
|
+
createSceneItemReqs: retries,
|
|
139
|
+
failFast,
|
|
140
|
+
onProgress,
|
|
141
|
+
limit,
|
|
142
|
+
sceneId,
|
|
143
|
+
polling,
|
|
144
|
+
});
|
|
145
|
+
// attach placeholder references to item errors
|
|
146
|
+
placeholderItemResults.forEach((resultItem, i) => {
|
|
147
|
+
if (isSceneItemRelationship(resultItem.res)) {
|
|
148
|
+
retryErrors[i].placeholderItem = resultItem.res;
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
193
153
|
}
|
|
194
154
|
}
|
|
195
|
-
if (
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
onMsg(` Batch errors: ${batchErrors.length}`);
|
|
155
|
+
if (createFailed) {
|
|
156
|
+
if (verbose) {
|
|
157
|
+
onMsg(`Scene item creation failed in ${formatTime(Number(hrtime.bigint() - startTime) / 1000000000)} at depth ${depth}.`);
|
|
199
158
|
}
|
|
200
|
-
|
|
201
|
-
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
if (verbose) {
|
|
162
|
+
onMsg(`Scene item creation completed in ${formatTime(Number(hrtime.bigint() - startTime) / 1000000000)} for ${itemCount} scene items with max depth of ${depthSortedItems.length - 1}.`);
|
|
163
|
+
if (resultBatchErrors.length) {
|
|
164
|
+
onMsg(`Batch errors: ${resultBatchErrors.length}`);
|
|
165
|
+
}
|
|
166
|
+
if (resultItemErrors.length) {
|
|
167
|
+
onMsg(`Scene item errors: ${resultItemErrors.length}`);
|
|
168
|
+
}
|
|
202
169
|
}
|
|
170
|
+
if (verbose)
|
|
171
|
+
onMsg(`Committing scene and polling until ready...`);
|
|
172
|
+
yield updateScene({
|
|
173
|
+
attributes: { state: UpdateSceneRequestDataAttributesStateEnum.Commit },
|
|
174
|
+
client,
|
|
175
|
+
sceneId,
|
|
176
|
+
});
|
|
177
|
+
yield pollSceneReady({
|
|
178
|
+
client,
|
|
179
|
+
id: sceneId,
|
|
180
|
+
onMsg,
|
|
181
|
+
polling: sceneReadyPolling,
|
|
182
|
+
verbose,
|
|
183
|
+
});
|
|
184
|
+
if (verbose)
|
|
185
|
+
onMsg(`Fitting scene's camera to scene items...`);
|
|
186
|
+
sceneResult = (yield updateScene({
|
|
187
|
+
attributes: {
|
|
188
|
+
camera: { type: CameraFitTypeEnum.FitVisibleSceneItems },
|
|
189
|
+
},
|
|
190
|
+
client,
|
|
191
|
+
sceneId,
|
|
192
|
+
})).scene;
|
|
203
193
|
}
|
|
204
|
-
if (verbose)
|
|
205
|
-
onMsg(`Committing scene and polling until ready...`);
|
|
206
|
-
yield updateScene({
|
|
207
|
-
attributes: { state: UpdateSceneRequestDataAttributesStateEnum.Commit },
|
|
208
|
-
client,
|
|
209
|
-
sceneId,
|
|
210
|
-
});
|
|
211
|
-
yield pollSceneReady({ client, id: sceneId, onMsg, polling, verbose });
|
|
212
|
-
if (verbose)
|
|
213
|
-
onMsg(`Fitting scene's camera to scene items...`);
|
|
214
|
-
const sceneResult = (yield updateScene({
|
|
215
|
-
attributes: {
|
|
216
|
-
camera: { type: CameraFitTypeEnum.FitVisibleSceneItems },
|
|
217
|
-
},
|
|
218
|
-
client,
|
|
219
|
-
sceneId,
|
|
220
|
-
})).scene;
|
|
221
194
|
if (verbose) {
|
|
222
|
-
const formatTime = (seconds) => {
|
|
223
|
-
const h = Math.floor(seconds / 3600);
|
|
224
|
-
const m = Math.floor((seconds % 3600) / 60);
|
|
225
|
-
const s = Math.round(seconds % 60);
|
|
226
|
-
return [h, m > 9 ? m : h ? '0' + m : m || '0', s > 9 ? s : '0' + s]
|
|
227
|
-
.filter(Boolean)
|
|
228
|
-
.join(':');
|
|
229
|
-
};
|
|
230
195
|
onMsg(`Scene creation completed in ${formatTime(Number(hrtime.bigint() - startTime) / 1000000000)}.`);
|
|
231
196
|
}
|
|
232
197
|
return {
|
|
233
|
-
errors:
|
|
234
|
-
queued:
|
|
198
|
+
errors: resultBatchErrors,
|
|
199
|
+
queued: returnQueued ? resultQueuedOps : [],
|
|
235
200
|
scene: sceneResult,
|
|
236
|
-
sceneItemErrors,
|
|
201
|
+
sceneItemErrors: resultItemErrors,
|
|
237
202
|
};
|
|
238
203
|
});
|
|
239
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* Helper function for building a metadata string object.
|
|
207
|
+
*
|
|
208
|
+
* @param value Value to convert to metadata object
|
|
209
|
+
* @param condition Setting to `false` will cause result to be `undefined`
|
|
210
|
+
* @returns Instance of a `MetadataValue` object
|
|
211
|
+
*/
|
|
212
|
+
function toMetadataOrUndefined(value, condition = true) {
|
|
213
|
+
return condition && defined(value)
|
|
214
|
+
? {
|
|
215
|
+
type: 'string',
|
|
216
|
+
value,
|
|
217
|
+
}
|
|
218
|
+
: // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
219
|
+
undefined;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* This async function takes a long list of create scene item data and handles
|
|
223
|
+
* batch based scene item creation. Batch operation results and errors are
|
|
224
|
+
* returned to the caller.
|
|
225
|
+
*/
|
|
226
|
+
const createSceneItemBatch = ({ client, createSceneItemReqs: createItemReqs, failFast, onProgress, limit, sceneId, polling = { intervalMs: PollIntervalMs, maxAttempts: MaxAttempts }, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
227
|
+
let batchErrors = [];
|
|
228
|
+
let itemErrors = [];
|
|
229
|
+
let itemResults = [];
|
|
230
|
+
const createRes = yield createSceneItems({
|
|
231
|
+
client,
|
|
232
|
+
createSceneItemReqs: createItemReqs,
|
|
233
|
+
failFast,
|
|
234
|
+
onProgress,
|
|
235
|
+
limit,
|
|
236
|
+
sceneId,
|
|
237
|
+
});
|
|
238
|
+
const { a: batchOps, b: errors } = partition(createRes.queuedBatchOps, (i) => isQueuedJob(i.res));
|
|
239
|
+
if (errors.length) {
|
|
240
|
+
batchErrors = batchErrors.concat(errors);
|
|
241
|
+
}
|
|
242
|
+
// Nothing succeeded, return early as something is likely wrong
|
|
243
|
+
if (batchOps.length === 0 || errors.length === createRes.chunks) {
|
|
244
|
+
return { batchOps, batchErrors, itemErrors, itemResults };
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
function poll({ ops, res, }) {
|
|
248
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
249
|
+
const r = yield pollQueuedJob({
|
|
250
|
+
id: res.data.id,
|
|
251
|
+
getQueuedJob: (id, cancelToken) => client.batches.getQueuedBatch({ id }, { cancelToken }),
|
|
252
|
+
allow404: true,
|
|
253
|
+
limit,
|
|
254
|
+
polling,
|
|
255
|
+
});
|
|
256
|
+
if (isPollError(r.res)) {
|
|
257
|
+
failFast ? throwOnError(r) : errors.push({ ops, res: r.res });
|
|
258
|
+
}
|
|
259
|
+
return r;
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
// eslint-disable-next-line no-await-in-loop
|
|
263
|
+
const batchRes = yield Promise.all(batchOps.map((is) => limit(poll, is)));
|
|
264
|
+
itemResults = batchRes.flatMap((b, i) => isBatch(b.res)
|
|
265
|
+
? b.res['vertexvis/batch:results'].map((r, j) => {
|
|
266
|
+
return { req: batchOps[i].ops[j].data, res: r };
|
|
267
|
+
})
|
|
268
|
+
: []);
|
|
269
|
+
itemErrors = itemErrors.concat(itemResults.filter((resultItem) => isApiError(resultItem.res)));
|
|
270
|
+
}
|
|
271
|
+
// if the full batch failed add batch item error for each item
|
|
272
|
+
errors.forEach((error) => {
|
|
273
|
+
console.log(error);
|
|
274
|
+
error.ops.forEach((op) => {
|
|
275
|
+
// `error.res` guaranteed to be non-null due to `isApiError()` condition above
|
|
276
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
277
|
+
itemErrors.push({ req: op.data, res: error.res });
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
return { batchOps, batchErrors, itemErrors, itemResults };
|
|
281
|
+
});
|
|
240
282
|
/**
|
|
241
283
|
* Create scene items within a scene.
|
|
242
284
|
*/
|
|
243
|
-
export function
|
|
285
|
+
export function createSceneItems({ client, createSceneItemReqs, failFast, limit, sceneId, }) {
|
|
244
286
|
return __awaiter(this, void 0, void 0, function* () {
|
|
245
|
-
const limit = pLimit(parallelism);
|
|
246
287
|
const batchSize = 500;
|
|
247
288
|
const opChunks = arrayChunked(createSceneItemReqs.map((req) => ({
|
|
248
289
|
data: req.data,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { AxiosError, AxiosResponse, Method } from 'axios';
|
|
3
3
|
import { ParsedUrlQuery } from 'querystring';
|
|
4
|
-
import { ApiError, Failure, ImageType, Matrix4, Oauth2Api, OAuth2Token, QueuedJob } from '../index';
|
|
4
|
+
import { ApiError, Failure, ImageType, Matrix4, Oauth2Api, OAuth2Token, QueuedJob, RelationshipData } from '../index';
|
|
5
5
|
export interface Cursors {
|
|
6
6
|
readonly next?: string;
|
|
7
7
|
readonly self?: string;
|
|
@@ -136,6 +136,7 @@ export declare function is4x4Identity(transform: number[][]): boolean;
|
|
|
136
136
|
*/
|
|
137
137
|
export declare function isEncoded(s: string): boolean;
|
|
138
138
|
export declare function isApiError(error: unknown): error is ApiError;
|
|
139
|
+
export declare function isSceneItemRelationship(data: unknown): data is RelationshipData;
|
|
139
140
|
export declare function isFailure(obj: unknown): obj is Failure;
|
|
140
141
|
export declare function isQueuedJob(obj: unknown): obj is QueuedJob;
|
|
141
142
|
export declare function hasVertexError(error: unknown): error is VertexError;
|
|
@@ -225,3 +226,9 @@ export declare function tryStream<T>(fn: () => Promise<T>): Promise<T>;
|
|
|
225
226
|
* @returns `true` if webhook signature is valid and body is safe to parse.
|
|
226
227
|
*/
|
|
227
228
|
export declare function isWebhookValid(body: string, secret: string, signature: string): boolean;
|
|
229
|
+
/**
|
|
230
|
+
* Formats a number in seconds as a time formatted string.
|
|
231
|
+
* @param seconds number of seconds to be formatted
|
|
232
|
+
* @returns `string` in the form of HH:MM:SS based on input
|
|
233
|
+
*/
|
|
234
|
+
export declare function formatTime(seconds: number): string;
|