@openforis/arena-core 2.16.4 → 2.16.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/job/JobBase.js +19 -2
- package/dist/esm/job/JobBase.js.map +1 -1
- package/dist/esm/record/recordUpdater.js +46 -4
- package/dist/esm/record/recordUpdater.js.map +1 -1
- package/dist/esm/record/recordValidator/attributeKeyValidator.js +2 -1
- package/dist/esm/record/recordValidator/attributeKeyValidator.js.map +1 -1
- package/dist/job/JobBase.js +19 -2
- package/dist/job/JobBase.js.map +1 -1
- package/dist/record/recordUpdater.js +46 -4
- package/dist/record/recordUpdater.js.map +1 -1
- package/dist/record/recordValidator/attributeKeyValidator.js +2 -1
- package/dist/record/recordValidator/attributeKeyValidator.js.map +1 -1
- package/package.json +1 -1
package/dist/esm/job/JobBase.js
CHANGED
|
@@ -148,7 +148,8 @@ export class JobBase {
|
|
|
148
148
|
return;
|
|
149
149
|
this.cancelRequested = true;
|
|
150
150
|
const currentInnerJob = this.getCurrentInnerJob();
|
|
151
|
-
if (currentInnerJob
|
|
151
|
+
if (currentInnerJob && !currentInnerJob.isEnded()) {
|
|
152
|
+
// inner job running or about to start (pending):
|
|
152
153
|
// the parent job status will be set to canceled when handling the canceled event of the inner job
|
|
153
154
|
yield currentInnerJob.cancel({ canceledByAdmin });
|
|
154
155
|
return;
|
|
@@ -181,6 +182,9 @@ export class JobBase {
|
|
|
181
182
|
start() {
|
|
182
183
|
return __awaiter(this, arguments, void 0, function* (client = null) {
|
|
183
184
|
var _a;
|
|
185
|
+
// canceled before being started: nothing to execute
|
|
186
|
+
if (this.cancelRequested || this.isEnded())
|
|
187
|
+
return;
|
|
184
188
|
this.logDebug('start');
|
|
185
189
|
try {
|
|
186
190
|
if (client) {
|
|
@@ -213,6 +217,9 @@ export class JobBase {
|
|
|
213
217
|
executeInTransaction() {
|
|
214
218
|
return __awaiter(this, void 0, void 0, function* () {
|
|
215
219
|
try {
|
|
220
|
+
// canceled while the transaction was being opened: it must not be set as running again
|
|
221
|
+
if (this.cancelRequested)
|
|
222
|
+
return;
|
|
216
223
|
yield this.onStart();
|
|
217
224
|
const shouldExecute = yield this.shouldExecute();
|
|
218
225
|
if (shouldExecute) {
|
|
@@ -268,6 +275,9 @@ export class JobBase {
|
|
|
268
275
|
return __awaiter(this, void 0, void 0, function* () {
|
|
269
276
|
this.logDebug(`- ${this.total} inner jobs found`);
|
|
270
277
|
for (let i = 0; i < this.innerJobs.length; i++) {
|
|
278
|
+
// canceled between two inner jobs: the next ones must not be started
|
|
279
|
+
if (this.cancelRequested)
|
|
280
|
+
break;
|
|
271
281
|
this.logDebug(`- executing inner job ${i + 1}`);
|
|
272
282
|
this.currentInnerJobIndex = i;
|
|
273
283
|
const currentInnerJob = this.innerJobs[i];
|
|
@@ -301,6 +311,12 @@ export class JobBase {
|
|
|
301
311
|
break;
|
|
302
312
|
}
|
|
303
313
|
}
|
|
314
|
+
if (!this.stopOnInnerJobFailure &&
|
|
315
|
+
!this.cancelRequested &&
|
|
316
|
+
this.innerJobs.some((innerJob) => innerJob.isFailed())) {
|
|
317
|
+
// set only after all inner jobs have run: setting it at the first failure would end this job (onEnd) too early
|
|
318
|
+
yield this.setStatus(JobStatus.failed);
|
|
319
|
+
}
|
|
304
320
|
this.logDebug(`- ${this.processed} inner jobs processed successfully`);
|
|
305
321
|
});
|
|
306
322
|
}
|
|
@@ -359,7 +375,8 @@ export class JobBase {
|
|
|
359
375
|
if (status === JobStatus.failed) {
|
|
360
376
|
// report the errors of the failed inner job in the failed event (and in the errors) of this job
|
|
361
377
|
Object.assign(this.errors, (_d = (_b = this.getCurrentInnerJob()) === null || _b === void 0 ? void 0 : _b.errors) !== null && _d !== void 0 ? _d : {});
|
|
362
|
-
|
|
378
|
+
// when not stopping on inner job failures, this job is set as failed after all the inner jobs have run
|
|
379
|
+
return this.stopOnInnerJobFailure ? this.setStatus(status) : undefined;
|
|
363
380
|
}
|
|
364
381
|
if (status === JobStatus.running) {
|
|
365
382
|
this.notifyEvent({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JobBase.js","sourceRoot":"","sources":["../../../src/job/JobBase.ts"],"names":[],"mappings":"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","sourcesContent":["import { User } from '../auth'\nimport { Dictionary } from '../common'\nimport { SystemError, SystemErrorKeys } from '../error'\nimport { Logger } from '../logger'\nimport { Survey } from '../survey'\nimport { UUIDs } from '../utils'\n\nimport { Job } from './job'\nimport { JobContext } from './jobContext'\nimport { JobEvent, JobEventType } from './jobEvent'\nimport { JobSerialized } from './jobSerialized'\nimport { JobStatus } from './status'\n\nexport interface JobConstructor {\n new <C extends JobContext, R>(context: C, innerJobs?: JobBase<any>[]): JobBase<C, R>\n readonly prototype: JobBase<any, any>\n}\n\n/**\n * Client able to open a transaction (e.g. a pg-promise database): the job runs inside `client.tx(callback)`.\n */\nexport interface JobTxClient {\n tx(callback: (tx: any) => Promise<void>): Promise<unknown>\n}\n\nconst PROGRESS_NOTIFICATION_THROTTLE_MILLIS = 1000\n\n/**\n * Asynchronous task handler.\n *\n * Status workflow:\n * - pending\n * - running\n * - (end)\n * -- succeeded\n * -- failed\n * -- canceled\n *\n * Methods that can be overwritten by subclasses:\n * - shouldExecute (in tx)\n * - onStart (in tx)\n * - execute (in tx)\n * - beforeSuccess (in tx)\n * - generateResult (in tx)\n * - beforeEnd (in tx)\n * - onEnd (out of tx)\n * - getErrorInfo\n */\nexport abstract class JobBase<C extends JobContext, R = undefined> implements Job<R> {\n static readonly keysContext = {\n surveyId: 'surveyId',\n survey: 'survey',\n user: 'user',\n }\n\n readonly uuid: string\n readonly type: string\n status: JobStatus = JobStatus.pending\n startTime?: Date\n endTime?: Date\n total: number\n result: R | undefined = undefined\n errors: Record<string, any> = {}\n canceledByAdmin?: boolean\n\n protected logger: Logger\n protected context: C\n protected innerJobs: JobBase<C, any>[]\n protected currentInnerJobIndex = -1\n\n private _processed = 0\n private eventListener: ((event: JobEvent) => unknown) | undefined = undefined\n private progressThrottleTimeoutId: ReturnType<typeof setTimeout> | undefined = undefined\n private progressThrottleLastRunTime = 0\n private _stopOnInnerJobFailure = true\n // set as soon as cancel() is called (the canceled status is set only after beforeEnd has run)\n private cancelRequested = false\n private beforeEndCalled = false\n // inner job events are handled in sequence; executeJobs waits for them before going on\n private innerJobEventsQueue: Promise<void> = Promise.resolve()\n\n public constructor(context: C, innerJobs: JobBase<C, any>[] = []) {\n this.context = { ...context }\n this.innerJobs = innerJobs\n\n this.uuid = UUIDs.v4()\n this.type = this.context.type ?? this.constructor.name\n this.total = innerJobs.length > 0 ? innerJobs.length : 1\n\n // Created last, so that createLogger() overrides can safely build the logger name out of\n // the job identity (e.g. arena's `Job <ClassName> (<uuid>)` log correlation id).\n this.logger = this.createLogger()\n }\n\n get processed(): number {\n return this._processed\n }\n\n set processed(value: number) {\n this._processed = value\n this.notifyProgress()\n }\n\n get stopOnInnerJobFailure(): boolean {\n return this._stopOnInnerJobFailure\n }\n\n set stopOnInnerJobFailure(value: boolean) {\n this._stopOnInnerJobFailure = value\n }\n\n protected get surveyId(): number | null {\n return this.context.surveyId ?? null\n }\n\n protected get user(): User {\n return this.context.user\n }\n\n protected get userUuid(): string | undefined {\n return this.user?.uuid\n }\n\n protected get contextSurvey(): Survey | undefined {\n return this.context.survey\n }\n\n /**\n * Returns the value of the specified context prop, or the default value (null if not specified) when not set.\n */\n protected getContextProp<K extends keyof C & string>(prop: K, defaultValue?: C[K] | null): C[K] | null\n /** Context prop not declared in the context type: the value is typed by the caller. */\n protected getContextProp<T = any, P extends string = string>(\n prop: P extends keyof C ? never : P,\n defaultValue?: T | null\n ): T | null\n protected getContextProp(prop: string, defaultValue: unknown = null): unknown {\n const value = (this.context as Record<string, unknown>)[prop]\n return value ?? defaultValue\n }\n\n protected setContext(context: Partial<C>): void {\n Object.assign(this.context, context)\n }\n\n protected deleteContextProps(...propNames: string[]): void {\n propNames.forEach((propName) => {\n delete (this.context as any)[propName]\n })\n }\n\n /**\n * Registers the listener notified of job events (status changes and progress).\n * Only one listener can be registered at a time; registering a new one replaces the previous one.\n * Errors thrown (or rejections returned) by the listener are logged and do not affect the job execution.\n */\n onEvent(listener: (event: JobEvent) => unknown): this {\n this.eventListener = listener\n return this\n }\n\n isPending(): boolean {\n return this.status === JobStatus.pending\n }\n\n isRunning(): boolean {\n return this.status === JobStatus.running\n }\n\n isSucceeded(): boolean {\n return this.status === JobStatus.succeeded\n }\n\n isFailed(): boolean {\n return this.status === JobStatus.failed\n }\n\n isCanceled(): boolean {\n return this.status === JobStatus.canceled\n }\n\n isEnded(): boolean {\n return [JobStatus.succeeded, JobStatus.failed, JobStatus.canceled].includes(this.status)\n }\n\n hasErrors(): boolean {\n return Object.keys(this.errors).length > 0\n }\n\n protected getCurrentInnerJob(): JobBase<C, any> | undefined {\n return this.innerJobs[this.currentInnerJobIndex]\n }\n\n protected combineInnerJobsResults(): Record<string, any> {\n const results: Record<string, any> = {}\n this.innerJobs.forEach((innerJob) => Object.assign(results, innerJob.result ?? {}))\n return results\n }\n\n protected combineInnerJobsErrors(): Record<string, any> {\n const errors: Record<string, any> = {}\n this.innerJobs.forEach((innerJob) => Object.assign(errors, innerJob.errors ?? {}))\n return errors\n }\n\n async cancel(options: { canceledByAdmin?: boolean } = {}): Promise<void> {\n const { canceledByAdmin = false } = options\n if (this.isEnded()) return\n\n this.cancelRequested = true\n\n const currentInnerJob = this.getCurrentInnerJob()\n if (currentInnerJob?.isRunning()) {\n // the parent job status will be set to canceled when handling the canceled event of the inner job\n await currentInnerJob.cancel({ canceledByAdmin })\n return\n }\n // no inner job running (job without inner jobs, or between two inner jobs)\n this.canceledByAdmin = canceledByAdmin\n // release the resources (temp files/directories, streams) allocated before the cancellation\n await this.runBeforeEnd()\n await this.setStatus(JobStatus.canceled)\n }\n\n /**\n * Returns true when the job has been canceled (or is no longer running).\n * Long-running subclasses can check it to stop processing items (e.g. in a loop) as soon as the job is canceled.\n */\n protected isCancelRequestedOrNotRunning(): boolean {\n return this.cancelRequested || !this.isRunning()\n }\n\n /**\n * Runs beforeEnd only once (it can be called by cancel() and when the job execution ends).\n */\n private async runBeforeEnd(): Promise<void> {\n if (this.beforeEndCalled) return\n this.beforeEndCalled = true\n await this.beforeEnd()\n }\n\n async start(client: JobTxClient | null = null): Promise<void> {\n this.logDebug('start')\n\n try {\n if (client) {\n await client.tx(async (tx: any) => {\n this.context.tx = tx\n await this.executeInTransaction()\n })\n } else {\n await this.executeInTransaction()\n }\n if (!this.isCancelRequestedOrNotRunning()) {\n await this.setStatus(JobStatus.succeeded)\n }\n } catch (error: any) {\n // a job can fail while still pending (e.g. the transaction cannot be opened, or onStart fails)\n if (!this.cancelRequested && !this.isFailed() && !this.isCanceled()) {\n this.logError(error.stack ?? error)\n const { key, params } = this.getErrorInfo(error)\n this.addError({ error: { valid: false, errors: [{ key, params }] } })\n await this.setStatus(JobStatus.failed)\n }\n } finally {\n this.context.tx = undefined\n }\n }\n\n private async executeInTransaction(): Promise<void> {\n try {\n await this.onStart()\n\n const shouldExecute = await this.shouldExecute()\n if (shouldExecute) {\n if (this.innerJobs.length > 0) {\n await this.executeJobs()\n } else {\n await this.execute()\n }\n if (!this.isCancelRequestedOrNotRunning()) {\n await this.beforeSuccess()\n }\n }\n } finally {\n // runs only if not already run by cancel()\n await this.runBeforeEnd()\n this.context.tx = undefined\n }\n\n if (this.isCancelRequestedOrNotRunning()) {\n this.throwError(SystemErrorKeys.job.canceledOrErrorsFound)\n }\n }\n\n /**\n * Generates a plain JSON-serializable representation of the job, including its inner jobs.\n */\n toJSON(): JobSerialized<R> {\n const { errors, processed, result, status, total, type, userUuid, uuid } = this\n\n return {\n uuid,\n type,\n userUuid,\n surveyId: this.surveyId,\n innerJobs: this.innerJobs.map((job) => job.toJSON()),\n currentInnerJobIndex: this.currentInnerJobIndex,\n\n status,\n pending: this.isPending(),\n running: this.isRunning(),\n succeeded: this.isSucceeded(),\n canceled: this.isCanceled(),\n failed: this.isFailed(),\n ended: this.isEnded(),\n\n total,\n processed,\n progressPercent: this.calculateProgressPercent(),\n elapsedMillis: this.calculateElapsedMillis(),\n\n errors: this.isFailed() ? errors : undefined,\n result: this.isSucceeded() ? result : undefined,\n }\n }\n\n private async executeJobs(): Promise<void> {\n this.logDebug(`- ${this.total} inner jobs found`)\n\n for (let i = 0; i < this.innerJobs.length; i++) {\n this.logDebug(`- executing inner job ${i + 1}`)\n this.currentInnerJobIndex = i\n const currentInnerJob = this.innerJobs[i]\n if (currentInnerJob.context) {\n Object.assign(this.context, currentInnerJob.context)\n }\n currentInnerJob.context = this.context\n currentInnerJob.onEvent((event) => {\n // handle the inner job events in sequence; errors are logged (they would be unhandled rejections otherwise)\n this.innerJobEventsQueue = this.innerJobEventsQueue\n .then(() => this.onInnerJobEvent(event))\n .catch((error) => this.logError(error?.stack ?? error))\n })\n\n // The inner job shares this very context object, and its own start()/executeInTransaction()\n // clear `context.tx` when they terminate: without saving and restoring it here, the first\n // inner job would wipe the parent's transaction handle, leaving every following inner job\n // (and the parent's own beforeSuccess()/beforeEnd()) running outside of the transaction.\n // start() never throws (it swallows/records errors internally), so a plain restore is enough.\n const parentTx = this.context.tx\n await currentInnerJob.start(parentTx)\n this.context.tx = parentTx\n // the inner job events (e.g. canceled, failed) update the status of this job\n await this.innerJobEventsQueue\n\n if (this.cancelRequested || this.isCanceled() || currentInnerJob.isCanceled()) {\n break\n }\n if (currentInnerJob.isSucceeded()) {\n this.incrementProcessedItems()\n } else if (this.stopOnInnerJobFailure) {\n break\n }\n }\n\n this.logDebug(`- ${this.processed} inner jobs processed successfully`)\n }\n\n /**\n * Runs the job's own work when it has no inner jobs. Default implementation is a no-op, so\n * subclasses that only supply inner jobs (and never override this) don't need to. It also means\n * a subclass can safely call `super.execute()` defensively without knowing whether any ancestor\n * overrides it - if this were `abstract` instead, TypeScript would compile away any runtime\n * property for it entirely, so `super.execute()` would throw the moment no ancestor overrides it\n * (confirmed in production: a subclass whose immediate parent didn't override `execute()` crashed\n * with \"(intermediate value).execute is not a function\" the instant it called `super.execute()`).\n */\n protected async execute(): Promise<void> {\n // to be extended by subclasses\n }\n\n /**\n * Determines whether the job should actually run.\n * When it returns false, \"execute\" (or the inner jobs) will be skipped, but the job will still succeed.\n */\n protected shouldExecute(): Promise<boolean> {\n return Promise.resolve(true)\n }\n\n protected incrementProcessedItems(incrementBy = 1): void {\n this.processed += incrementBy\n }\n\n protected async setStatus(status: JobStatus): Promise<void> {\n this.logDebug(`set status: ${status}`)\n this.status = status\n\n const event: JobEvent = { type: JobEventType.statusChange, status, total: this.total, processed: this.processed }\n if (status === JobStatus.failed) {\n event.errors = this.errors\n }\n\n if (this.isEnded()) {\n this.logDebug('onEnd...')\n await this.onEnd()\n this.logDebug(`onEnd run. Job completed in ${this.elapsedTimePrettyFormat}`)\n }\n\n this.notifyEvent(event)\n }\n\n /**\n * Inner job event handler.\n */\n protected async onInnerJobEvent(event: JobEvent): Promise<void> {\n const { status } = event\n if (status === JobStatus.canceled) {\n this.canceledByAdmin = this.getCurrentInnerJob()?.canceledByAdmin ?? false\n return this.setStatus(status)\n }\n if (status === JobStatus.failed) {\n // report the errors of the failed inner job in the failed event (and in the errors) of this job\n Object.assign(this.errors, this.getCurrentInnerJob()?.errors ?? {})\n return this.setStatus(status)\n }\n if (status === JobStatus.running) {\n this.notifyEvent({\n type: JobEventType.progress,\n status: this.status,\n total: this.total,\n processed: this.processed,\n })\n return\n }\n this.logDebug(`Unknown inner job status: ${status}`)\n }\n\n /**\n * Called when the job just has been started.\n */\n protected async onStart(): Promise<void> {\n this.startTime = new Date()\n await this.setStatus(JobStatus.running)\n }\n\n /**\n * Called before beforeEnd only if the status will change to 'success'.\n * Default implementation stores the value returned by generateResult() via setResult().\n * It runs INSIDE the current db transaction.\n */\n protected async beforeSuccess(): Promise<void> {\n this.setResult(await this.generateResult())\n }\n\n /**\n * Computes the value beforeSuccess() will store as this job's result.\n * Default implementation returns whatever is already in `this.result`\n * (e.g. accumulated via earlier setResult() calls during execute()).\n */\n protected generateResult(): Promise<R | undefined> {\n return Promise.resolve(this.result)\n }\n\n /**\n * Updates this job's result. When both the current and incoming values are plain objects,\n * they are merged (Object.assign); otherwise the incoming value replaces the current one.\n * This keeps it safe for subclasses whose result is a primitive (e.g. a plain number) as\n * well as ones that build an object result incrementally across multiple setResult() calls.\n */\n protected setResult(result: R | undefined): void {\n if (result && typeof result === 'object' && this.result && typeof this.result === 'object') {\n Object.assign(this.result, result)\n } else {\n this.result = result\n }\n }\n\n /**\n * Called before onEnd. Useful for flushing resources used by the job before it terminates completely.\n * It runs INSIDE the current db transaction.\n */\n protected beforeEnd(): Promise<void> {\n this.logDebug('Cleanup')\n return Promise.resolve()\n }\n\n /**\n * Called when the job status changes to success, failed or canceled\n * (it runs OUTSIDE of the current db transaction)\n */\n protected onEnd(): Promise<void> {\n this.endTime = new Date()\n this.cancelNotifyProgress()\n return Promise.resolve()\n }\n\n protected addError(error: any, errorKey?: string): void {\n const key = errorKey ?? String(this.processed + 1)\n this.errors[key] = error\n }\n\n protected throwError(errorKey: string, params?: Dictionary<any>): void {\n // SystemError: getErrorInfo will report the translatable key instead of a generic error\n throw new SystemError(errorKey, params)\n }\n\n protected getErrorInfo(error: any): { key: string; params: Record<string, any> } {\n if (error instanceof SystemError) {\n return { key: `appErrors:${error.key}`, params: error.params }\n }\n return { key: 'appErrors:generic', params: { text: error.toString() } }\n }\n\n protected abstract createLogger(): Logger\n\n protected logDebug(...msgs: any[]): void {\n this.logger.debug(...msgs)\n }\n\n protected logInfo(...msgs: any[]): void {\n this.logger.info(...msgs)\n }\n\n protected logWarn(...msgs: any[]): void {\n this.logger.warn(...msgs)\n }\n\n protected logError(...msgs: any[]): void {\n this.logger.error(...msgs)\n }\n\n private get elapsedTimePrettyFormat(): string {\n const elapsedTime = this.calculateElapsedMillis()\n const elapsedMins = Math.floor(elapsedTime / 1000 / 60)\n const elapsedSeconds = String(Math.floor(elapsedTime / 1000) % 60).padStart(2, '0')\n const elapsedMillis = String(elapsedTime % 1000).padStart(3, '0')\n return `${elapsedMins}:${elapsedSeconds}.${elapsedMillis}`\n }\n\n private calculatePartialProgress(): number {\n if (this.isSucceeded()) return 100\n if (this.total > 0) return Math.floor((100 * this.processed) / this.total)\n return 0\n }\n\n private calculateProgressPercent(): number {\n const partialProgress = this.calculatePartialProgress()\n const currentInnerJob = this.getCurrentInnerJob()\n if (\n !currentInnerJob ||\n this.currentInnerJobIndex < 0 ||\n partialProgress === 100 ||\n this.processed > this.currentInnerJobIndex\n ) {\n return partialProgress\n }\n return partialProgress + Math.floor(currentInnerJob.calculateProgressPercent() / this.total)\n }\n\n private calculateElapsedMillis(): number {\n if (!this.startTime) return 0\n return (this.endTime ?? new Date()).getTime() - this.startTime.getTime()\n }\n\n /**\n * Notifies a progress event to the registered listener, throttled so that fast-changing\n * progress does not flood consumers (e.g. web socket clients).\n */\n private notifyProgress(): void {\n const run = (): void => {\n this.progressThrottleTimeoutId = undefined\n this.progressThrottleLastRunTime = Date.now()\n this.notifyEvent({\n type: JobEventType.progress,\n status: this.status,\n total: this.total,\n processed: this.processed,\n })\n }\n\n if (this.progressThrottleTimeoutId) return\n\n const elapsed = Date.now() - this.progressThrottleLastRunTime\n if (!this.progressThrottleLastRunTime || elapsed >= PROGRESS_NOTIFICATION_THROTTLE_MILLIS) {\n run()\n } else {\n this.progressThrottleTimeoutId = setTimeout(run, PROGRESS_NOTIFICATION_THROTTLE_MILLIS - elapsed)\n }\n }\n\n /**\n * Cancels any pending throttled progress notification and resets the throttle state.\n */\n private cancelNotifyProgress(): void {\n if (this.progressThrottleTimeoutId) {\n clearTimeout(this.progressThrottleTimeoutId)\n this.progressThrottleTimeoutId = undefined\n }\n this.progressThrottleLastRunTime = 0\n }\n\n /**\n * Notifies the event to the registered listener.\n * Listener errors are only logged: they would otherwise fail the job or, when thrown inside the progress\n * throttle timeout, crash the process.\n */\n private notifyEvent(event: JobEvent): void {\n const listener = this.eventListener\n if (!listener) return\n try {\n Promise.resolve(listener(event)).catch((error) => this.logListenerError(error))\n } catch (error) {\n this.logListenerError(error)\n }\n }\n\n private logListenerError(error: any): void {\n this.logError(`error in job event listener: ${error?.stack ?? error}`)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"JobBase.js","sourceRoot":"","sources":["../../../src/job/JobBase.ts"],"names":[],"mappings":"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","sourcesContent":["import { User } from '../auth'\nimport { Dictionary } from '../common'\nimport { SystemError, SystemErrorKeys } from '../error'\nimport { Logger } from '../logger'\nimport { Survey } from '../survey'\nimport { UUIDs } from '../utils'\n\nimport { Job } from './job'\nimport { JobContext } from './jobContext'\nimport { JobEvent, JobEventType } from './jobEvent'\nimport { JobSerialized } from './jobSerialized'\nimport { JobStatus } from './status'\n\nexport interface JobConstructor {\n new <C extends JobContext, R>(context: C, innerJobs?: JobBase<any>[]): JobBase<C, R>\n readonly prototype: JobBase<any, any>\n}\n\n/**\n * Client able to open a transaction (e.g. a pg-promise database): the job runs inside `client.tx(callback)`.\n */\nexport interface JobTxClient {\n tx(callback: (tx: any) => Promise<void>): Promise<unknown>\n}\n\nconst PROGRESS_NOTIFICATION_THROTTLE_MILLIS = 1000\n\n/**\n * Asynchronous task handler.\n *\n * Status workflow:\n * - pending\n * - running\n * - (end)\n * -- succeeded\n * -- failed\n * -- canceled\n *\n * Methods that can be overwritten by subclasses:\n * - shouldExecute (in tx)\n * - onStart (in tx)\n * - execute (in tx)\n * - beforeSuccess (in tx)\n * - generateResult (in tx)\n * - beforeEnd (in tx)\n * - onEnd (out of tx)\n * - getErrorInfo\n */\nexport abstract class JobBase<C extends JobContext, R = undefined> implements Job<R> {\n static readonly keysContext = {\n surveyId: 'surveyId',\n survey: 'survey',\n user: 'user',\n }\n\n readonly uuid: string\n readonly type: string\n status: JobStatus = JobStatus.pending\n startTime?: Date\n endTime?: Date\n total: number\n result: R | undefined = undefined\n errors: Record<string, any> = {}\n canceledByAdmin?: boolean\n\n protected logger: Logger\n protected context: C\n protected innerJobs: JobBase<C, any>[]\n protected currentInnerJobIndex = -1\n\n private _processed = 0\n private eventListener: ((event: JobEvent) => unknown) | undefined = undefined\n private progressThrottleTimeoutId: ReturnType<typeof setTimeout> | undefined = undefined\n private progressThrottleLastRunTime = 0\n private _stopOnInnerJobFailure = true\n // set as soon as cancel() is called (the canceled status is set only after beforeEnd has run)\n private cancelRequested = false\n private beforeEndCalled = false\n // inner job events are handled in sequence; executeJobs waits for them before going on\n private innerJobEventsQueue: Promise<void> = Promise.resolve()\n\n public constructor(context: C, innerJobs: JobBase<C, any>[] = []) {\n this.context = { ...context }\n this.innerJobs = innerJobs\n\n this.uuid = UUIDs.v4()\n this.type = this.context.type ?? this.constructor.name\n this.total = innerJobs.length > 0 ? innerJobs.length : 1\n\n // Created last, so that createLogger() overrides can safely build the logger name out of\n // the job identity (e.g. arena's `Job <ClassName> (<uuid>)` log correlation id).\n this.logger = this.createLogger()\n }\n\n get processed(): number {\n return this._processed\n }\n\n set processed(value: number) {\n this._processed = value\n this.notifyProgress()\n }\n\n get stopOnInnerJobFailure(): boolean {\n return this._stopOnInnerJobFailure\n }\n\n set stopOnInnerJobFailure(value: boolean) {\n this._stopOnInnerJobFailure = value\n }\n\n protected get surveyId(): number | null {\n return this.context.surveyId ?? null\n }\n\n protected get user(): User {\n return this.context.user\n }\n\n protected get userUuid(): string | undefined {\n return this.user?.uuid\n }\n\n protected get contextSurvey(): Survey | undefined {\n return this.context.survey\n }\n\n /**\n * Returns the value of the specified context prop, or the default value (null if not specified) when not set.\n */\n protected getContextProp<K extends keyof C & string>(prop: K, defaultValue?: C[K] | null): C[K] | null\n /** Context prop not declared in the context type: the value is typed by the caller. */\n protected getContextProp<T = any, P extends string = string>(\n prop: P extends keyof C ? never : P,\n defaultValue?: T | null\n ): T | null\n protected getContextProp(prop: string, defaultValue: unknown = null): unknown {\n const value = (this.context as Record<string, unknown>)[prop]\n return value ?? defaultValue\n }\n\n protected setContext(context: Partial<C>): void {\n Object.assign(this.context, context)\n }\n\n protected deleteContextProps(...propNames: string[]): void {\n propNames.forEach((propName) => {\n delete (this.context as any)[propName]\n })\n }\n\n /**\n * Registers the listener notified of job events (status changes and progress).\n * Only one listener can be registered at a time; registering a new one replaces the previous one.\n * Errors thrown (or rejections returned) by the listener are logged and do not affect the job execution.\n */\n onEvent(listener: (event: JobEvent) => unknown): this {\n this.eventListener = listener\n return this\n }\n\n isPending(): boolean {\n return this.status === JobStatus.pending\n }\n\n isRunning(): boolean {\n return this.status === JobStatus.running\n }\n\n isSucceeded(): boolean {\n return this.status === JobStatus.succeeded\n }\n\n isFailed(): boolean {\n return this.status === JobStatus.failed\n }\n\n isCanceled(): boolean {\n return this.status === JobStatus.canceled\n }\n\n isEnded(): boolean {\n return [JobStatus.succeeded, JobStatus.failed, JobStatus.canceled].includes(this.status)\n }\n\n hasErrors(): boolean {\n return Object.keys(this.errors).length > 0\n }\n\n protected getCurrentInnerJob(): JobBase<C, any> | undefined {\n return this.innerJobs[this.currentInnerJobIndex]\n }\n\n protected combineInnerJobsResults(): Record<string, any> {\n const results: Record<string, any> = {}\n this.innerJobs.forEach((innerJob) => Object.assign(results, innerJob.result ?? {}))\n return results\n }\n\n protected combineInnerJobsErrors(): Record<string, any> {\n const errors: Record<string, any> = {}\n this.innerJobs.forEach((innerJob) => Object.assign(errors, innerJob.errors ?? {}))\n return errors\n }\n\n async cancel(options: { canceledByAdmin?: boolean } = {}): Promise<void> {\n const { canceledByAdmin = false } = options\n if (this.isEnded()) return\n\n this.cancelRequested = true\n\n const currentInnerJob = this.getCurrentInnerJob()\n if (currentInnerJob && !currentInnerJob.isEnded()) {\n // inner job running or about to start (pending):\n // the parent job status will be set to canceled when handling the canceled event of the inner job\n await currentInnerJob.cancel({ canceledByAdmin })\n return\n }\n // no inner job running (job without inner jobs, or between two inner jobs)\n this.canceledByAdmin = canceledByAdmin\n // release the resources (temp files/directories, streams) allocated before the cancellation\n await this.runBeforeEnd()\n await this.setStatus(JobStatus.canceled)\n }\n\n /**\n * Returns true when the job has been canceled (or is no longer running).\n * Long-running subclasses can check it to stop processing items (e.g. in a loop) as soon as the job is canceled.\n */\n protected isCancelRequestedOrNotRunning(): boolean {\n return this.cancelRequested || !this.isRunning()\n }\n\n /**\n * Runs beforeEnd only once (it can be called by cancel() and when the job execution ends).\n */\n private async runBeforeEnd(): Promise<void> {\n if (this.beforeEndCalled) return\n this.beforeEndCalled = true\n await this.beforeEnd()\n }\n\n async start(client: JobTxClient | null = null): Promise<void> {\n // canceled before being started: nothing to execute\n if (this.cancelRequested || this.isEnded()) return\n\n this.logDebug('start')\n\n try {\n if (client) {\n await client.tx(async (tx: any) => {\n this.context.tx = tx\n await this.executeInTransaction()\n })\n } else {\n await this.executeInTransaction()\n }\n if (!this.isCancelRequestedOrNotRunning()) {\n await this.setStatus(JobStatus.succeeded)\n }\n } catch (error: any) {\n // a job can fail while still pending (e.g. the transaction cannot be opened, or onStart fails)\n if (!this.cancelRequested && !this.isFailed() && !this.isCanceled()) {\n this.logError(error.stack ?? error)\n const { key, params } = this.getErrorInfo(error)\n this.addError({ error: { valid: false, errors: [{ key, params }] } })\n await this.setStatus(JobStatus.failed)\n }\n } finally {\n this.context.tx = undefined\n }\n }\n\n private async executeInTransaction(): Promise<void> {\n try {\n // canceled while the transaction was being opened: it must not be set as running again\n if (this.cancelRequested) return\n\n await this.onStart()\n\n const shouldExecute = await this.shouldExecute()\n if (shouldExecute) {\n if (this.innerJobs.length > 0) {\n await this.executeJobs()\n } else {\n await this.execute()\n }\n if (!this.isCancelRequestedOrNotRunning()) {\n await this.beforeSuccess()\n }\n }\n } finally {\n // runs only if not already run by cancel()\n await this.runBeforeEnd()\n this.context.tx = undefined\n }\n\n if (this.isCancelRequestedOrNotRunning()) {\n this.throwError(SystemErrorKeys.job.canceledOrErrorsFound)\n }\n }\n\n /**\n * Generates a plain JSON-serializable representation of the job, including its inner jobs.\n */\n toJSON(): JobSerialized<R> {\n const { errors, processed, result, status, total, type, userUuid, uuid } = this\n\n return {\n uuid,\n type,\n userUuid,\n surveyId: this.surveyId,\n innerJobs: this.innerJobs.map((job) => job.toJSON()),\n currentInnerJobIndex: this.currentInnerJobIndex,\n\n status,\n pending: this.isPending(),\n running: this.isRunning(),\n succeeded: this.isSucceeded(),\n canceled: this.isCanceled(),\n failed: this.isFailed(),\n ended: this.isEnded(),\n\n total,\n processed,\n progressPercent: this.calculateProgressPercent(),\n elapsedMillis: this.calculateElapsedMillis(),\n\n errors: this.isFailed() ? errors : undefined,\n result: this.isSucceeded() ? result : undefined,\n }\n }\n\n private async executeJobs(): Promise<void> {\n this.logDebug(`- ${this.total} inner jobs found`)\n\n for (let i = 0; i < this.innerJobs.length; i++) {\n // canceled between two inner jobs: the next ones must not be started\n if (this.cancelRequested) break\n\n this.logDebug(`- executing inner job ${i + 1}`)\n this.currentInnerJobIndex = i\n const currentInnerJob = this.innerJobs[i]\n if (currentInnerJob.context) {\n Object.assign(this.context, currentInnerJob.context)\n }\n currentInnerJob.context = this.context\n currentInnerJob.onEvent((event) => {\n // handle the inner job events in sequence; errors are logged (they would be unhandled rejections otherwise)\n this.innerJobEventsQueue = this.innerJobEventsQueue\n .then(() => this.onInnerJobEvent(event))\n .catch((error) => this.logError(error?.stack ?? error))\n })\n\n // The inner job shares this very context object, and its own start()/executeInTransaction()\n // clear `context.tx` when they terminate: without saving and restoring it here, the first\n // inner job would wipe the parent's transaction handle, leaving every following inner job\n // (and the parent's own beforeSuccess()/beforeEnd()) running outside of the transaction.\n // start() never throws (it swallows/records errors internally), so a plain restore is enough.\n const parentTx = this.context.tx\n await currentInnerJob.start(parentTx)\n this.context.tx = parentTx\n // the inner job events (e.g. canceled, failed) update the status of this job\n await this.innerJobEventsQueue\n\n if (this.cancelRequested || this.isCanceled() || currentInnerJob.isCanceled()) {\n break\n }\n if (currentInnerJob.isSucceeded()) {\n this.incrementProcessedItems()\n } else if (this.stopOnInnerJobFailure) {\n break\n }\n }\n if (\n !this.stopOnInnerJobFailure &&\n !this.cancelRequested &&\n this.innerJobs.some((innerJob) => innerJob.isFailed())\n ) {\n // set only after all inner jobs have run: setting it at the first failure would end this job (onEnd) too early\n await this.setStatus(JobStatus.failed)\n }\n\n this.logDebug(`- ${this.processed} inner jobs processed successfully`)\n }\n\n /**\n * Runs the job's own work when it has no inner jobs. Default implementation is a no-op, so\n * subclasses that only supply inner jobs (and never override this) don't need to. It also means\n * a subclass can safely call `super.execute()` defensively without knowing whether any ancestor\n * overrides it - if this were `abstract` instead, TypeScript would compile away any runtime\n * property for it entirely, so `super.execute()` would throw the moment no ancestor overrides it\n * (confirmed in production: a subclass whose immediate parent didn't override `execute()` crashed\n * with \"(intermediate value).execute is not a function\" the instant it called `super.execute()`).\n */\n protected async execute(): Promise<void> {\n // to be extended by subclasses\n }\n\n /**\n * Determines whether the job should actually run.\n * When it returns false, \"execute\" (or the inner jobs) will be skipped, but the job will still succeed.\n */\n protected shouldExecute(): Promise<boolean> {\n return Promise.resolve(true)\n }\n\n protected incrementProcessedItems(incrementBy = 1): void {\n this.processed += incrementBy\n }\n\n protected async setStatus(status: JobStatus): Promise<void> {\n this.logDebug(`set status: ${status}`)\n this.status = status\n\n const event: JobEvent = { type: JobEventType.statusChange, status, total: this.total, processed: this.processed }\n if (status === JobStatus.failed) {\n event.errors = this.errors\n }\n\n if (this.isEnded()) {\n this.logDebug('onEnd...')\n await this.onEnd()\n this.logDebug(`onEnd run. Job completed in ${this.elapsedTimePrettyFormat}`)\n }\n\n this.notifyEvent(event)\n }\n\n /**\n * Inner job event handler.\n */\n protected async onInnerJobEvent(event: JobEvent): Promise<void> {\n const { status } = event\n if (status === JobStatus.canceled) {\n this.canceledByAdmin = this.getCurrentInnerJob()?.canceledByAdmin ?? false\n return this.setStatus(status)\n }\n if (status === JobStatus.failed) {\n // report the errors of the failed inner job in the failed event (and in the errors) of this job\n Object.assign(this.errors, this.getCurrentInnerJob()?.errors ?? {})\n // when not stopping on inner job failures, this job is set as failed after all the inner jobs have run\n return this.stopOnInnerJobFailure ? this.setStatus(status) : undefined\n }\n if (status === JobStatus.running) {\n this.notifyEvent({\n type: JobEventType.progress,\n status: this.status,\n total: this.total,\n processed: this.processed,\n })\n return\n }\n this.logDebug(`Unknown inner job status: ${status}`)\n }\n\n /**\n * Called when the job just has been started.\n */\n protected async onStart(): Promise<void> {\n this.startTime = new Date()\n await this.setStatus(JobStatus.running)\n }\n\n /**\n * Called before beforeEnd only if the status will change to 'success'.\n * Default implementation stores the value returned by generateResult() via setResult().\n * It runs INSIDE the current db transaction.\n */\n protected async beforeSuccess(): Promise<void> {\n this.setResult(await this.generateResult())\n }\n\n /**\n * Computes the value beforeSuccess() will store as this job's result.\n * Default implementation returns whatever is already in `this.result`\n * (e.g. accumulated via earlier setResult() calls during execute()).\n */\n protected generateResult(): Promise<R | undefined> {\n return Promise.resolve(this.result)\n }\n\n /**\n * Updates this job's result. When both the current and incoming values are plain objects,\n * they are merged (Object.assign); otherwise the incoming value replaces the current one.\n * This keeps it safe for subclasses whose result is a primitive (e.g. a plain number) as\n * well as ones that build an object result incrementally across multiple setResult() calls.\n */\n protected setResult(result: R | undefined): void {\n if (result && typeof result === 'object' && this.result && typeof this.result === 'object') {\n Object.assign(this.result, result)\n } else {\n this.result = result\n }\n }\n\n /**\n * Called before onEnd. Useful for flushing resources used by the job before it terminates completely.\n * It runs INSIDE the current db transaction.\n */\n protected beforeEnd(): Promise<void> {\n this.logDebug('Cleanup')\n return Promise.resolve()\n }\n\n /**\n * Called when the job status changes to success, failed or canceled\n * (it runs OUTSIDE of the current db transaction)\n */\n protected onEnd(): Promise<void> {\n this.endTime = new Date()\n this.cancelNotifyProgress()\n return Promise.resolve()\n }\n\n protected addError(error: any, errorKey?: string): void {\n const key = errorKey ?? String(this.processed + 1)\n this.errors[key] = error\n }\n\n protected throwError(errorKey: string, params?: Dictionary<any>): void {\n // SystemError: getErrorInfo will report the translatable key instead of a generic error\n throw new SystemError(errorKey, params)\n }\n\n protected getErrorInfo(error: any): { key: string; params: Record<string, any> } {\n if (error instanceof SystemError) {\n return { key: `appErrors:${error.key}`, params: error.params }\n }\n return { key: 'appErrors:generic', params: { text: error.toString() } }\n }\n\n protected abstract createLogger(): Logger\n\n protected logDebug(...msgs: any[]): void {\n this.logger.debug(...msgs)\n }\n\n protected logInfo(...msgs: any[]): void {\n this.logger.info(...msgs)\n }\n\n protected logWarn(...msgs: any[]): void {\n this.logger.warn(...msgs)\n }\n\n protected logError(...msgs: any[]): void {\n this.logger.error(...msgs)\n }\n\n private get elapsedTimePrettyFormat(): string {\n const elapsedTime = this.calculateElapsedMillis()\n const elapsedMins = Math.floor(elapsedTime / 1000 / 60)\n const elapsedSeconds = String(Math.floor(elapsedTime / 1000) % 60).padStart(2, '0')\n const elapsedMillis = String(elapsedTime % 1000).padStart(3, '0')\n return `${elapsedMins}:${elapsedSeconds}.${elapsedMillis}`\n }\n\n private calculatePartialProgress(): number {\n if (this.isSucceeded()) return 100\n if (this.total > 0) return Math.floor((100 * this.processed) / this.total)\n return 0\n }\n\n private calculateProgressPercent(): number {\n const partialProgress = this.calculatePartialProgress()\n const currentInnerJob = this.getCurrentInnerJob()\n if (\n !currentInnerJob ||\n this.currentInnerJobIndex < 0 ||\n partialProgress === 100 ||\n this.processed > this.currentInnerJobIndex\n ) {\n return partialProgress\n }\n return partialProgress + Math.floor(currentInnerJob.calculateProgressPercent() / this.total)\n }\n\n private calculateElapsedMillis(): number {\n if (!this.startTime) return 0\n return (this.endTime ?? new Date()).getTime() - this.startTime.getTime()\n }\n\n /**\n * Notifies a progress event to the registered listener, throttled so that fast-changing\n * progress does not flood consumers (e.g. web socket clients).\n */\n private notifyProgress(): void {\n const run = (): void => {\n this.progressThrottleTimeoutId = undefined\n this.progressThrottleLastRunTime = Date.now()\n this.notifyEvent({\n type: JobEventType.progress,\n status: this.status,\n total: this.total,\n processed: this.processed,\n })\n }\n\n if (this.progressThrottleTimeoutId) return\n\n const elapsed = Date.now() - this.progressThrottleLastRunTime\n if (!this.progressThrottleLastRunTime || elapsed >= PROGRESS_NOTIFICATION_THROTTLE_MILLIS) {\n run()\n } else {\n this.progressThrottleTimeoutId = setTimeout(run, PROGRESS_NOTIFICATION_THROTTLE_MILLIS - elapsed)\n }\n }\n\n /**\n * Cancels any pending throttled progress notification and resets the throttle state.\n */\n private cancelNotifyProgress(): void {\n if (this.progressThrottleTimeoutId) {\n clearTimeout(this.progressThrottleTimeoutId)\n this.progressThrottleTimeoutId = undefined\n }\n this.progressThrottleLastRunTime = 0\n }\n\n /**\n * Notifies the event to the registered listener.\n * Listener errors are only logged: they would otherwise fail the job or, when thrown inside the progress\n * throttle timeout, crash the process.\n */\n private notifyEvent(event: JobEvent): void {\n const listener = this.eventListener\n if (!listener) return\n try {\n Promise.resolve(listener(event)).catch((error) => this.logListenerError(error))\n } catch (error) {\n this.logListenerError(error)\n }\n }\n\n private logListenerError(error: any): void {\n this.logError(`error in job event listener: ${error?.stack ?? error}`)\n }\n}\n"]}
|
|
@@ -8,6 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import { SystemError, SystemErrorKeys } from '../error';
|
|
11
|
+
import { NodeDefs } from '../nodeDef';
|
|
11
12
|
import { SurveyDependencyType, Surveys } from '../survey';
|
|
12
13
|
import { Dates, Objects } from '../utils';
|
|
13
14
|
import { Validations } from '../validation/validations';
|
|
@@ -40,7 +41,7 @@ const getDependentValidationNodePointers = (params) => {
|
|
|
40
41
|
return Array.from(uniquePointersByKey.values());
|
|
41
42
|
};
|
|
42
43
|
const _onRecordNodesCreateOrUpdate = (params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
43
|
-
const { user, survey, dateModified = Dates.nowFormattedForStorage() } = params;
|
|
44
|
+
const { user, survey, categoryItemProvider, taxonProvider, areaOfInterestProvider, additionalNodesToValidate, dateModified = Dates.nowFormattedForStorage(), } = params;
|
|
44
45
|
const updateResult = yield RecordNodesUpdater.updateNodesDependents(params);
|
|
45
46
|
const { nodes: updatedNodes, record: updatedRecord } = updateResult;
|
|
46
47
|
const dependentValidationNodePointers = getDependentValidationNodePointers({
|
|
@@ -52,10 +53,13 @@ const _onRecordNodesCreateOrUpdate = (params) => __awaiter(void 0, void 0, void
|
|
|
52
53
|
record: updatedRecord,
|
|
53
54
|
nodePointers: dependentValidationNodePointers,
|
|
54
55
|
});
|
|
55
|
-
const nodesToValidate = Object.assign(Object.assign({}, updatedNodes), dependentValidationNodes);
|
|
56
|
+
const nodesToValidate = Object.assign(Object.assign(Object.assign({}, updatedNodes), dependentValidationNodes), additionalNodesToValidate);
|
|
56
57
|
const validationUpdatedNodes = yield RecordValidator.validateNodes({
|
|
57
58
|
user,
|
|
58
59
|
survey,
|
|
60
|
+
categoryItemProvider,
|
|
61
|
+
taxonProvider,
|
|
62
|
+
areaOfInterestProvider,
|
|
59
63
|
record: updatedRecord,
|
|
60
64
|
nodes: nodesToValidate,
|
|
61
65
|
});
|
|
@@ -91,11 +95,49 @@ const updateAttributeValue = (params) => __awaiter(void 0, void 0, void 0, funct
|
|
|
91
95
|
const _record = Records.addNode(attributeUpdated, { sideEffect })(record);
|
|
92
96
|
return _onRecordNodesCreateOrUpdate(Object.assign(Object.assign({}, params), { record: _record, nodes: nodesUpdated }));
|
|
93
97
|
});
|
|
98
|
+
/**
|
|
99
|
+
* Returns the key and unique attributes of the siblings of the specified entities:
|
|
100
|
+
* they could have been duplicates of the ones being deleted, so they must be validated again.
|
|
101
|
+
*/
|
|
102
|
+
const getEntityKeyOrUniqueAttributes = (params) => {
|
|
103
|
+
const { survey, record, entity, uniqueAttributeDefs } = params;
|
|
104
|
+
return [
|
|
105
|
+
...Records.getEntityKeyNodes(survey, entity)(record),
|
|
106
|
+
...uniqueAttributeDefs.flatMap((uniqueDef) => Records.getChildren(entity, uniqueDef.uuid)(record)),
|
|
107
|
+
].filter(Boolean);
|
|
108
|
+
};
|
|
109
|
+
const getSiblingKeyOrUniqueAttributes = (params) => {
|
|
110
|
+
const { survey, record, nodeUuids } = params;
|
|
111
|
+
const nodeUuidsSet = new Set(nodeUuids);
|
|
112
|
+
const result = {};
|
|
113
|
+
for (const nodeUuid of nodeUuids) {
|
|
114
|
+
const node = Records.getNodeByUuid(nodeUuid)(record);
|
|
115
|
+
const parentNode = node ? Records.getParent(node)(record) : undefined;
|
|
116
|
+
const nodeDef = node ? Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid }) : undefined;
|
|
117
|
+
if (!parentNode || !nodeDef || !NodeDefs.isEntity(nodeDef))
|
|
118
|
+
continue;
|
|
119
|
+
const uniqueAttributeDefs = Surveys.getNodeDefChildren({ survey, nodeDef }).filter((childDef) => { var _a; return (_a = NodeDefs.getValidations(childDef)) === null || _a === void 0 ? void 0 : _a.unique; });
|
|
120
|
+
const siblings = Records.getChildren(parentNode, nodeDef.uuid)(record).filter((s) => !nodeUuidsSet.has(s.uuid));
|
|
121
|
+
for (const sibling of siblings) {
|
|
122
|
+
for (const attribute of getEntityKeyOrUniqueAttributes({
|
|
123
|
+
survey,
|
|
124
|
+
record,
|
|
125
|
+
entity: sibling,
|
|
126
|
+
uniqueAttributeDefs,
|
|
127
|
+
})) {
|
|
128
|
+
result[attribute.uuid] = attribute;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
};
|
|
94
134
|
const deleteNodes = (params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
95
|
-
const { nodeUuids, record: _record, sideEffect = false } = params;
|
|
135
|
+
const { survey, nodeUuids, record: _record, sideEffect = false } = params;
|
|
136
|
+
// found before removing the nodes: the deleted nodes are no longer reachable from their siblings afterwards
|
|
137
|
+
const siblingKeyOrUniqueAttributes = getSiblingKeyOrUniqueAttributes({ survey, record: _record, nodeUuids });
|
|
96
138
|
const updateResult = Records.removeNodes(nodeUuids, { sideEffect })(_record);
|
|
97
139
|
const { record, nodesDeleted } = updateResult;
|
|
98
|
-
const result = yield _onRecordNodesCreateOrUpdate(Object.assign(Object.assign({}, params), { record, nodes: nodesDeleted }));
|
|
140
|
+
const result = yield _onRecordNodesCreateOrUpdate(Object.assign(Object.assign({}, params), { record, nodes: nodesDeleted, additionalNodesToValidate: siblingKeyOrUniqueAttributes }));
|
|
99
141
|
result.nodesDeleted = nodesDeleted;
|
|
100
142
|
return result;
|
|
101
143
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recordUpdater.js","sourceRoot":"","sources":["../../../src/record/recordUpdater.ts"],"names":[],"mappings":"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","sourcesContent":["import { Dictionary } from '../common'\nimport { SystemError, SystemErrorKeys } from '../error'\nimport { ArenaRecordNode, NodePointer, NodesMap } from '../node'\nimport { Survey, SurveyDependencyType, Surveys } from '../survey'\nimport { Dates, Objects } from '../utils'\nimport { Validations } from '../validation/validations'\nimport { NodePointers } from './nodePointers'\nimport { ArenaRecord } from './record'\nimport { RecordNodesUpdater, RecordUpdateResult } from './recordNodesUpdater'\nimport { NodeCreateParams, NodesUpdateParams } from './recordNodesUpdater/recordNodesCreator'\nimport { Records } from './records'\nimport { RecordValidator } from './recordValidator'\n\nconst validationDependencyTypes = [\n SurveyDependencyType.validations,\n SurveyDependencyType.required,\n SurveyDependencyType.minCount,\n SurveyDependencyType.maxCount,\n]\n\nconst getDependentValidationNodePointers = (params: {\n survey: Survey\n record: ArenaRecord\n nodes: NodesMap\n}): NodePointer[] => {\n const { survey, record, nodes } = params\n const uniquePointersByKey = new Map<string, NodePointer>()\n for (const node of Object.values(nodes)) {\n for (const dependencyType of validationDependencyTypes) {\n const nodePointers = Records.getDependentNodePointers(survey, node, dependencyType)(record)\n for (const pointer of nodePointers) {\n const nodeCtxUuid = pointer.nodeCtx.uuid\n const nodeDefUuid = pointer.nodeDef.uuid\n const key = `${nodeCtxUuid ?? ''}:${nodeDefUuid ?? ''}`\n if (!uniquePointersByKey.has(key)) {\n uniquePointersByKey.set(key, pointer)\n }\n }\n }\n }\n return Array.from(uniquePointersByKey.values())\n}\n\nconst _onRecordNodesCreateOrUpdate = async (\n params: NodesUpdateParams & { nodes: Dictionary<ArenaRecordNode> }\n): Promise<RecordUpdateResult> => {\n const { user, survey, dateModified = Dates.nowFormattedForStorage() } = params\n\n const updateResult = await RecordNodesUpdater.updateNodesDependents(params)\n const { nodes: updatedNodes, record: updatedRecord } = updateResult\n\n const dependentValidationNodePointers = getDependentValidationNodePointers({\n survey,\n record: updatedRecord,\n nodes: updatedNodes,\n })\n\n const dependentValidationNodes = NodePointers.getNodesMapFromNodePointers({\n record: updatedRecord,\n nodePointers: dependentValidationNodePointers,\n })\n\n const nodesToValidate = { ...updatedNodes, ...dependentValidationNodes }\n\n const validationUpdatedNodes = await RecordValidator.validateNodes({\n user,\n survey,\n record: updatedRecord,\n nodes: nodesToValidate,\n })\n\n const validation = Validations.mergeValidations(validationUpdatedNodes)(Validations.getValidation(updatedRecord))\n updatedRecord.validation = validation\n updatedRecord.dateModified = dateModified\n\n updateResult.validation = validation\n\n return updateResult\n}\n\nconst createDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { record: updatedRecord, nodes: createdNodes } = await RecordNodesUpdater.addDescendants(params)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: updatedRecord, nodes: createdNodes })\n}\n\nconst createNodeAndDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { record: updatedRecord, nodes: createdNodes } = await RecordNodesUpdater.addNodeAndDescendants(params)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: updatedRecord, nodes: createdNodes })\n}\n\nconst createRootEntity = async (\n params: NodesUpdateParams & {\n createMultipleEntities?: boolean\n }\n): Promise<RecordUpdateResult> => {\n const { survey, createMultipleEntities = true } = params\n\n return createNodeAndDescendants({ ...params, nodeDef: Surveys.getNodeDefRoot({ survey }), createMultipleEntities })\n}\n\nconst updateAttributeValue = async (\n params: NodesUpdateParams & {\n attributeUuid: string\n value: any\n }\n): Promise<RecordUpdateResult> => {\n const { attributeUuid, record, value, dateModified = Dates.nowFormattedForStorage(), sideEffect = false } = params\n const attribute = Records.getNodeByUuid(attributeUuid)(record)\n if (!attribute) throw new SystemError(SystemErrorKeys.record.nodeNotFound, { uuid: attributeUuid })\n\n let attributeUpdated = sideEffect ? attribute : { ...attribute }\n attributeUpdated.value = value\n attributeUpdated.dateModified = dateModified\n // reset defaultValueApplied flag\n attributeUpdated = Objects.dissocPath({ obj: attributeUpdated, path: ['meta', 'defaultValueApplied'], sideEffect })\n\n const nodesUpdated = { [attributeUuid]: attributeUpdated }\n\n const _record = Records.addNode(attributeUpdated, { sideEffect })(record)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: _record, nodes: nodesUpdated })\n}\n\nconst deleteNodes = async (\n params: NodesUpdateParams & {\n nodeUuids: string[]\n }\n): Promise<RecordUpdateResult> => {\n const { nodeUuids, record: _record, sideEffect = false } = params\n\n const updateResult = Records.removeNodes(nodeUuids, { sideEffect })(_record)\n const { record, nodesDeleted } = updateResult\n\n const result = await _onRecordNodesCreateOrUpdate({ ...params, record, nodes: nodesDeleted })\n result.nodesDeleted = nodesDeleted\n return result\n}\n\nconst deleteNode = async (params: NodesUpdateParams & { nodeUuid: string }): Promise<RecordUpdateResult> => {\n const { nodeUuid } = params\n return deleteNodes({ ...params, nodeUuids: [nodeUuid] })\n}\n\nexport const RecordUpdater = {\n /** Creates the descendants of a node and updates dependents (applicability, default values, calculations...) and validation */\n createDescendants,\n /** Creates a node and its descendants and updates dependents (applicability, default values, calculations...) and validation */\n createNodeAndDescendants,\n createRootEntity,\n updateAttributeValue,\n /** Deletes the node and its descendants and updates dependents (applicability, default values, calculations...) and validation */\n deleteNode,\n /** Deletes the nodes and their descendants and updates dependents (applicability, default values, calculations...) and validation */\n deleteNodes,\n}\n"]}
|
|
1
|
+
{"version":3,"file":"recordUpdater.js","sourceRoot":"","sources":["../../../src/record/recordUpdater.ts"],"names":[],"mappings":"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","sourcesContent":["import { Dictionary } from '../common'\nimport { SystemError, SystemErrorKeys } from '../error'\nimport { ArenaRecordNode, NodePointer, NodesMap } from '../node'\nimport { NodeDef, NodeDefs } from '../nodeDef'\nimport { Survey, SurveyDependencyType, Surveys } from '../survey'\nimport { Dates, Objects } from '../utils'\nimport { Validations } from '../validation/validations'\nimport { NodePointers } from './nodePointers'\nimport { ArenaRecord } from './record'\nimport { RecordNodesUpdater, RecordUpdateResult } from './recordNodesUpdater'\nimport { NodeCreateParams, NodesUpdateParams } from './recordNodesUpdater/recordNodesCreator'\nimport { Records } from './records'\nimport { RecordValidator } from './recordValidator'\n\nconst validationDependencyTypes = [\n SurveyDependencyType.validations,\n SurveyDependencyType.required,\n SurveyDependencyType.minCount,\n SurveyDependencyType.maxCount,\n]\n\nconst getDependentValidationNodePointers = (params: {\n survey: Survey\n record: ArenaRecord\n nodes: NodesMap\n}): NodePointer[] => {\n const { survey, record, nodes } = params\n const uniquePointersByKey = new Map<string, NodePointer>()\n for (const node of Object.values(nodes)) {\n for (const dependencyType of validationDependencyTypes) {\n const nodePointers = Records.getDependentNodePointers(survey, node, dependencyType)(record)\n for (const pointer of nodePointers) {\n const nodeCtxUuid = pointer.nodeCtx.uuid\n const nodeDefUuid = pointer.nodeDef.uuid\n const key = `${nodeCtxUuid ?? ''}:${nodeDefUuid ?? ''}`\n if (!uniquePointersByKey.has(key)) {\n uniquePointersByKey.set(key, pointer)\n }\n }\n }\n }\n return Array.from(uniquePointersByKey.values())\n}\n\nconst _onRecordNodesCreateOrUpdate = async (\n params: NodesUpdateParams & { nodes: Dictionary<ArenaRecordNode>; additionalNodesToValidate?: NodesMap }\n): Promise<RecordUpdateResult> => {\n const {\n user,\n survey,\n categoryItemProvider,\n taxonProvider,\n areaOfInterestProvider,\n additionalNodesToValidate,\n dateModified = Dates.nowFormattedForStorage(),\n } = params\n\n const updateResult = await RecordNodesUpdater.updateNodesDependents(params)\n const { nodes: updatedNodes, record: updatedRecord } = updateResult\n\n const dependentValidationNodePointers = getDependentValidationNodePointers({\n survey,\n record: updatedRecord,\n nodes: updatedNodes,\n })\n\n const dependentValidationNodes = NodePointers.getNodesMapFromNodePointers({\n record: updatedRecord,\n nodePointers: dependentValidationNodePointers,\n })\n\n const nodesToValidate = { ...updatedNodes, ...dependentValidationNodes, ...additionalNodesToValidate }\n\n const validationUpdatedNodes = await RecordValidator.validateNodes({\n user,\n survey,\n categoryItemProvider,\n taxonProvider,\n areaOfInterestProvider,\n record: updatedRecord,\n nodes: nodesToValidate,\n })\n\n const validation = Validations.mergeValidations(validationUpdatedNodes)(Validations.getValidation(updatedRecord))\n updatedRecord.validation = validation\n updatedRecord.dateModified = dateModified\n\n updateResult.validation = validation\n\n return updateResult\n}\n\nconst createDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { record: updatedRecord, nodes: createdNodes } = await RecordNodesUpdater.addDescendants(params)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: updatedRecord, nodes: createdNodes })\n}\n\nconst createNodeAndDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { record: updatedRecord, nodes: createdNodes } = await RecordNodesUpdater.addNodeAndDescendants(params)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: updatedRecord, nodes: createdNodes })\n}\n\nconst createRootEntity = async (\n params: NodesUpdateParams & {\n createMultipleEntities?: boolean\n }\n): Promise<RecordUpdateResult> => {\n const { survey, createMultipleEntities = true } = params\n\n return createNodeAndDescendants({ ...params, nodeDef: Surveys.getNodeDefRoot({ survey }), createMultipleEntities })\n}\n\nconst updateAttributeValue = async (\n params: NodesUpdateParams & {\n attributeUuid: string\n value: any\n }\n): Promise<RecordUpdateResult> => {\n const { attributeUuid, record, value, dateModified = Dates.nowFormattedForStorage(), sideEffect = false } = params\n const attribute = Records.getNodeByUuid(attributeUuid)(record)\n if (!attribute) throw new SystemError(SystemErrorKeys.record.nodeNotFound, { uuid: attributeUuid })\n\n let attributeUpdated = sideEffect ? attribute : { ...attribute }\n attributeUpdated.value = value\n attributeUpdated.dateModified = dateModified\n // reset defaultValueApplied flag\n attributeUpdated = Objects.dissocPath({ obj: attributeUpdated, path: ['meta', 'defaultValueApplied'], sideEffect })\n\n const nodesUpdated = { [attributeUuid]: attributeUpdated }\n\n const _record = Records.addNode(attributeUpdated, { sideEffect })(record)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: _record, nodes: nodesUpdated })\n}\n\n/**\n * Returns the key and unique attributes of the siblings of the specified entities:\n * they could have been duplicates of the ones being deleted, so they must be validated again.\n */\nconst getEntityKeyOrUniqueAttributes = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n uniqueAttributeDefs: NodeDef<any>[]\n}): ArenaRecordNode[] => {\n const { survey, record, entity, uniqueAttributeDefs } = params\n return [\n ...Records.getEntityKeyNodes(survey, entity)(record),\n ...uniqueAttributeDefs.flatMap((uniqueDef) => Records.getChildren(entity, uniqueDef.uuid)(record)),\n ].filter(Boolean)\n}\n\nconst getSiblingKeyOrUniqueAttributes = (params: {\n survey: Survey\n record: ArenaRecord\n nodeUuids: string[]\n}): NodesMap => {\n const { survey, record, nodeUuids } = params\n const nodeUuidsSet = new Set(nodeUuids)\n const result: NodesMap = {}\n for (const nodeUuid of nodeUuids) {\n const node = Records.getNodeByUuid(nodeUuid)(record)\n const parentNode = node ? Records.getParent(node)(record) : undefined\n const nodeDef = node ? Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid }) : undefined\n if (!parentNode || !nodeDef || !NodeDefs.isEntity(nodeDef)) continue\n\n const uniqueAttributeDefs = Surveys.getNodeDefChildren({ survey, nodeDef }).filter(\n (childDef) => NodeDefs.getValidations(childDef)?.unique\n )\n const siblings = Records.getChildren(parentNode, nodeDef.uuid)(record).filter((s) => !nodeUuidsSet.has(s.uuid))\n for (const sibling of siblings) {\n for (const attribute of getEntityKeyOrUniqueAttributes({\n survey,\n record,\n entity: sibling,\n uniqueAttributeDefs,\n })) {\n result[attribute.uuid] = attribute\n }\n }\n }\n return result\n}\n\nconst deleteNodes = async (\n params: NodesUpdateParams & {\n nodeUuids: string[]\n }\n): Promise<RecordUpdateResult> => {\n const { survey, nodeUuids, record: _record, sideEffect = false } = params\n\n // found before removing the nodes: the deleted nodes are no longer reachable from their siblings afterwards\n const siblingKeyOrUniqueAttributes = getSiblingKeyOrUniqueAttributes({ survey, record: _record, nodeUuids })\n\n const updateResult = Records.removeNodes(nodeUuids, { sideEffect })(_record)\n const { record, nodesDeleted } = updateResult\n\n const result = await _onRecordNodesCreateOrUpdate({\n ...params,\n record,\n nodes: nodesDeleted,\n additionalNodesToValidate: siblingKeyOrUniqueAttributes,\n })\n result.nodesDeleted = nodesDeleted\n return result\n}\n\nconst deleteNode = async (params: NodesUpdateParams & { nodeUuid: string }): Promise<RecordUpdateResult> => {\n const { nodeUuid } = params\n return deleteNodes({ ...params, nodeUuids: [nodeUuid] })\n}\n\nexport const RecordUpdater = {\n /** Creates the descendants of a node and updates dependents (applicability, default values, calculations...) and validation */\n createDescendants,\n /** Creates a node and its descendants and updates dependents (applicability, default values, calculations...) and validation */\n createNodeAndDescendants,\n createRootEntity,\n updateAttributeValue,\n /** Deletes the node and its descendants and updates dependents (applicability, default values, calculations...) and validation */\n deleteNode,\n /** Deletes the nodes and their descendants and updates dependents (applicability, default values, calculations...) and validation */\n deleteNodes,\n}\n"]}
|
|
@@ -40,7 +40,8 @@ const _isEntityDuplicate = (params) => {
|
|
|
40
40
|
return false;
|
|
41
41
|
}
|
|
42
42
|
const keyValuesByDefUuid = _getKeyValuesByDefUuid({ survey, record, entity, keyDefs, cache });
|
|
43
|
-
|
|
43
|
+
// entities with all key values empty are never duplicates (they are reported as missing values)
|
|
44
|
+
return (!Object.values(keyValuesByDefUuid).every(Objects.isEmpty) &&
|
|
44
45
|
siblingEntities.some((sibilingEntity) => {
|
|
45
46
|
const siblingKeyValuesByDefUuid = _getKeyValuesByDefUuid({
|
|
46
47
|
survey,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attributeKeyValidator.js","sourceRoot":"","sources":["../../../../src/record/recordValidator/attributeKeyValidator.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"attributeKeyValidator.js","sourceRoot":"","sources":["../../../../src/record/recordValidator/attributeKeyValidator.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecord } from '../record'\nimport { Records } from '../records'\nimport { NodeDef, NodeDefs, NodeDefProps, NodeDefType } from '../../nodeDef'\nimport { ArenaRecordNode, Nodes, NodeValues } from '../../node'\nimport { ValidationResult, ValidationResultFactory, ValidationSeverity } from '../../validation'\nimport { Survey, Surveys } from '../../survey'\n\nimport { Dictionary } from '../../common'\nimport { Objects } from '../../utils'\n\n/**\n * Cache of entity key values (and key defs by entity def) used during a single validation run (the record doesn't change):\n * without it, validating the keys of S sibling entities would compute the key values of every sibling S times.\n */\nexport type EntityKeysCache = {\n keyDefsByEntityDefUuid: Map<string, NodeDef<NodeDefType, NodeDefProps>[]>\n keyValuesByEntityUuid: Map<string, Dictionary<any>>\n}\n\nexport const createEntityKeysCache = (): EntityKeysCache => ({\n keyDefsByEntityDefUuid: new Map(),\n keyValuesByEntityUuid: new Map(),\n})\n\nconst _getKeyDefs = (params: {\n survey: Survey\n entityDef: NodeDef<NodeDefType, NodeDefProps>\n cache?: EntityKeysCache\n}): NodeDef<NodeDefType, NodeDefProps>[] => {\n const { survey, entityDef, cache } = params\n let keyDefs = cache?.keyDefsByEntityDefUuid.get(entityDef.uuid)\n if (!keyDefs) {\n keyDefs = Surveys.getNodeDefKeys({ survey, nodeDef: entityDef })\n cache?.keyDefsByEntityDefUuid.set(entityDef.uuid, keyDefs)\n }\n return keyDefs\n}\n\nconst _getKeyValuesByDefUuid = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n keyDefs: NodeDef<NodeDefType, NodeDefProps>[]\n cache?: EntityKeysCache\n}): Dictionary<any> => {\n const { survey, record, entity, keyDefs, cache } = params\n let keyValues = cache?.keyValuesByEntityUuid.get(entity.uuid)\n if (!keyValues) {\n keyValues = Records.getEntityKeyValuesByDefUuid(survey, entity, undefined, keyDefs)(record)\n cache?.keyValuesByEntityUuid.set(entity.uuid, keyValues)\n }\n return keyValues\n}\n\nconst _isEntityDuplicate = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n entityKeysCache?: EntityKeysCache\n}): boolean => {\n const { survey, entity, record, entityKeysCache: cache } = params\n // 1. get sibling entities\n const nodeParent = Records.getParent(entity)(record)\n if (!nodeParent) return false\n\n const siblingEntities = Records.getChildren(\n nodeParent,\n entity.nodeDefUuid\n )(record).filter((node) => !Nodes.areEqual(entity, node))\n\n // 2. get key values\n const entityDef = Surveys.getNodeDefByUuid({ survey, uuid: entity.nodeDefUuid })\n const keyDefs = _getKeyDefs({ survey, entityDef, cache })\n\n if (Objects.isEmpty(siblingEntities) || Objects.isEmpty(keyDefs)) {\n return false\n }\n const keyValuesByDefUuid = _getKeyValuesByDefUuid({ survey, record, entity, keyDefs, cache })\n // entities with all key values empty are never duplicates (they are reported as missing values)\n return (\n !Object.values(keyValuesByDefUuid).every(Objects.isEmpty) &&\n siblingEntities.some((sibilingEntity) => {\n const siblingKeyValuesByDefUuid = _getKeyValuesByDefUuid({\n survey,\n record,\n entity: sibilingEntity,\n keyDefs,\n cache,\n })\n return keyDefs.every((keyDef) => {\n const keyValue = keyValuesByDefUuid[keyDef.uuid]\n const siblingKeyValue = siblingKeyValuesByDefUuid[keyDef.uuid]\n return NodeValues.isValueEqual({\n survey,\n nodeDef: keyDef,\n value: keyValue,\n valueSearch: siblingKeyValue,\n })\n })\n })\n )\n}\n\nconst isNodeDefToBeValidated = (params: { survey: Survey; nodeDef: NodeDef<NodeDefType, NodeDefProps> }): boolean => {\n const { survey, nodeDef } = params\n if (!NodeDefs.isKey(nodeDef)) return false\n\n const nodeDefParent = Surveys.getNodeDefParent({ survey, nodeDef })\n // root entity key attributes will be validated by another validator\n return !!nodeDefParent && !NodeDefs.isRoot(nodeDefParent)\n}\n\nconst validateAttributeKey =\n (params: {\n survey: Survey\n record: ArenaRecord\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n entityKeysCache?: EntityKeysCache\n }) =>\n (_propName: string, node: ArenaRecordNode): Promise<ValidationResult> => {\n const { survey, nodeDef, record, entityKeysCache } = params\n\n if (isNodeDefToBeValidated({ survey, nodeDef })) {\n const entity = Records.getParent(node)(record)\n if (entity && _isEntityDuplicate({ survey, record, entity, entityKeysCache })) {\n return Promise.resolve(\n ValidationResultFactory.createInstance({\n valid: false,\n severity: ValidationSeverity.error,\n key: 'record.entity.keyDuplicate',\n })\n )\n }\n }\n\n return Promise.resolve(ValidationResultFactory.createInstance())\n }\n\nexport const AttributeKeyValidator = {\n validateAttributeKey,\n}\n"]}
|
package/dist/job/JobBase.js
CHANGED
|
@@ -151,7 +151,8 @@ class JobBase {
|
|
|
151
151
|
return;
|
|
152
152
|
this.cancelRequested = true;
|
|
153
153
|
const currentInnerJob = this.getCurrentInnerJob();
|
|
154
|
-
if (currentInnerJob
|
|
154
|
+
if (currentInnerJob && !currentInnerJob.isEnded()) {
|
|
155
|
+
// inner job running or about to start (pending):
|
|
155
156
|
// the parent job status will be set to canceled when handling the canceled event of the inner job
|
|
156
157
|
yield currentInnerJob.cancel({ canceledByAdmin });
|
|
157
158
|
return;
|
|
@@ -184,6 +185,9 @@ class JobBase {
|
|
|
184
185
|
start() {
|
|
185
186
|
return __awaiter(this, arguments, void 0, function* (client = null) {
|
|
186
187
|
var _a;
|
|
188
|
+
// canceled before being started: nothing to execute
|
|
189
|
+
if (this.cancelRequested || this.isEnded())
|
|
190
|
+
return;
|
|
187
191
|
this.logDebug('start');
|
|
188
192
|
try {
|
|
189
193
|
if (client) {
|
|
@@ -216,6 +220,9 @@ class JobBase {
|
|
|
216
220
|
executeInTransaction() {
|
|
217
221
|
return __awaiter(this, void 0, void 0, function* () {
|
|
218
222
|
try {
|
|
223
|
+
// canceled while the transaction was being opened: it must not be set as running again
|
|
224
|
+
if (this.cancelRequested)
|
|
225
|
+
return;
|
|
219
226
|
yield this.onStart();
|
|
220
227
|
const shouldExecute = yield this.shouldExecute();
|
|
221
228
|
if (shouldExecute) {
|
|
@@ -271,6 +278,9 @@ class JobBase {
|
|
|
271
278
|
return __awaiter(this, void 0, void 0, function* () {
|
|
272
279
|
this.logDebug(`- ${this.total} inner jobs found`);
|
|
273
280
|
for (let i = 0; i < this.innerJobs.length; i++) {
|
|
281
|
+
// canceled between two inner jobs: the next ones must not be started
|
|
282
|
+
if (this.cancelRequested)
|
|
283
|
+
break;
|
|
274
284
|
this.logDebug(`- executing inner job ${i + 1}`);
|
|
275
285
|
this.currentInnerJobIndex = i;
|
|
276
286
|
const currentInnerJob = this.innerJobs[i];
|
|
@@ -304,6 +314,12 @@ class JobBase {
|
|
|
304
314
|
break;
|
|
305
315
|
}
|
|
306
316
|
}
|
|
317
|
+
if (!this.stopOnInnerJobFailure &&
|
|
318
|
+
!this.cancelRequested &&
|
|
319
|
+
this.innerJobs.some((innerJob) => innerJob.isFailed())) {
|
|
320
|
+
// set only after all inner jobs have run: setting it at the first failure would end this job (onEnd) too early
|
|
321
|
+
yield this.setStatus(status_1.JobStatus.failed);
|
|
322
|
+
}
|
|
307
323
|
this.logDebug(`- ${this.processed} inner jobs processed successfully`);
|
|
308
324
|
});
|
|
309
325
|
}
|
|
@@ -362,7 +378,8 @@ class JobBase {
|
|
|
362
378
|
if (status === status_1.JobStatus.failed) {
|
|
363
379
|
// report the errors of the failed inner job in the failed event (and in the errors) of this job
|
|
364
380
|
Object.assign(this.errors, (_d = (_b = this.getCurrentInnerJob()) === null || _b === void 0 ? void 0 : _b.errors) !== null && _d !== void 0 ? _d : {});
|
|
365
|
-
|
|
381
|
+
// when not stopping on inner job failures, this job is set as failed after all the inner jobs have run
|
|
382
|
+
return this.stopOnInnerJobFailure ? this.setStatus(status) : undefined;
|
|
366
383
|
}
|
|
367
384
|
if (status === status_1.JobStatus.running) {
|
|
368
385
|
this.notifyEvent({
|
package/dist/job/JobBase.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JobBase.js","sourceRoot":"","sources":["../../src/job/JobBase.ts"],"names":[],"mappings":";;;;;;;;;;;;AAEA,oCAAuD;AAGvD,oCAAgC;AAIhC,yCAAmD;AAEnD,qCAAoC;AAcpC,MAAM,qCAAqC,GAAG,IAAI,CAAA;AAElD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH;IAiCE,YAAmB,OAAU,EAAE,SAAS,GAAsB,EAAE;;QAxBhE,WAAM,GAAc,kBAAS,CAAC,OAAO,CAAA;QAIrC,WAAM,GAAkB,SAAS,CAAA;QACjC,WAAM,GAAwB,EAAE,CAAA;QAMtB,yBAAoB,GAAG,CAAC,CAAC,CAAA;QAE3B,eAAU,GAAG,CAAC,CAAA;QACd,kBAAa,GAA+C,SAAS,CAAA;QACrE,8BAAyB,GAA8C,SAAS,CAAA;QAChF,gCAA2B,GAAG,CAAC,CAAA;QAC/B,2BAAsB,GAAG,IAAI,CAAA;QACrC,8FAA8F;QACtF,oBAAe,GAAG,KAAK,CAAA;QACvB,oBAAe,GAAG,KAAK,CAAA;QAC/B,uFAAuF;QAC/E,wBAAmB,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAA;QAG5D,IAAI,CAAC,OAAO,qBAAQ,OAAO,CAAE,CAAA;QAC7B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;QAE1B,IAAI,CAAC,IAAI,GAAG,aAAK,CAAC,EAAE,EAAE,CAAA;QACtB,IAAI,CAAC,IAAI,SAAG,IAAI,CAAC,OAAO,CAAC,IAAI,mCAAI,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;QACtD,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;QAExD,yFAAyF;QACzF,iFAAiF;QACjF,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,YAAY,EAAE,CAAA;IACnC,CAAC;IAED,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,UAAU,CAAA;IACxB,CAAC;IAED,IAAI,SAAS,CAAC,KAAa;QACzB,IAAI,CAAC,UAAU,GAAG,KAAK,CAAA;QACvB,IAAI,CAAC,cAAc,EAAE,CAAA;IACvB,CAAC;IAED,IAAI,qBAAqB;QACvB,OAAO,IAAI,CAAC,sBAAsB,CAAA;IACpC,CAAC;IAED,IAAI,qBAAqB,CAAC,KAAc;QACtC,IAAI,CAAC,sBAAsB,GAAG,KAAK,CAAA;IACrC,CAAC;IAED,IAAc,QAAQ;;QACpB,aAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,mCAAI,IAAI,CAAA;IACtC,CAAC;IAED,IAAc,IAAI;QAChB,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAA;IAC1B,CAAC;IAED,IAAc,QAAQ;;QACpB,OAAO,MAAA,IAAI,CAAC,IAAI,0CAAE,IAAI,CAAA;IACxB,CAAC;IAED,IAAc,aAAa;QACzB,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAA;IAC5B,CAAC;IAWS,cAAc,CAAC,IAAY,EAAE,YAAY,GAAY,IAAI;QACjE,MAAM,KAAK,GAAI,IAAI,CAAC,OAAmC,CAAC,IAAI,CAAC,CAAA;QAC7D,OAAO,KAAK,aAAL,KAAK,cAAL,KAAK,GAAI,YAAY,CAAA;IAC9B,CAAC;IAES,UAAU,CAAC,OAAmB;QACtC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IACtC,CAAC;IAES,kBAAkB,CAAC,GAAG,SAAmB;QACjD,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;YAC7B,OAAQ,IAAI,CAAC,OAAe,CAAC,QAAQ,CAAC,CAAA;QACxC,CAAC,CAAC,CAAA;IACJ,CAAC;IAED;;;;OAIG;IACH,OAAO,CAAC,QAAsC;QAC5C,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAA;QAC7B,OAAO,IAAI,CAAA;IACb,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,KAAK,kBAAS,CAAC,OAAO,CAAA;IAC1C,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,KAAK,kBAAS,CAAC,OAAO,CAAA;IAC1C,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,MAAM,KAAK,kBAAS,CAAC,SAAS,CAAA;IAC5C,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,MAAM,KAAK,kBAAS,CAAC,MAAM,CAAA;IACzC,CAAC;IAED,UAAU;QACR,OAAO,IAAI,CAAC,MAAM,KAAK,kBAAS,CAAC,QAAQ,CAAA;IAC3C,CAAC;IAED,OAAO;QACL,OAAO,CAAC,kBAAS,CAAC,SAAS,EAAE,kBAAS,CAAC,MAAM,EAAE,kBAAS,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IAC1F,CAAC;IAED,SAAS;QACP,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAA;IAC5C,CAAC;IAES,kBAAkB;QAC1B,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAA;IAClD,CAAC;IAES,uBAAuB;QAC/B,MAAM,OAAO,GAAwB,EAAE,CAAA;QACvC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE,kBAAC,MAAM,CAAC,MAAM,CAAC,OAAO,QAAE,QAAQ,CAAC,MAAM,mCAAI,EAAE,CAAC,GAAA,CAAC,CAAA;QACnF,OAAO,OAAO,CAAA;IAChB,CAAC;IAES,sBAAsB;QAC9B,MAAM,MAAM,GAAwB,EAAE,CAAA;QACtC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE,kBAAC,MAAM,CAAC,MAAM,CAAC,MAAM,QAAE,QAAQ,CAAC,MAAM,mCAAI,EAAE,CAAC,GAAA,CAAC,CAAA;QAClF,OAAO,MAAM,CAAA;IACf,CAAC;IAEK,MAAM;6DAAC,OAAO,GAAkC,EAAE;YACtD,MAAM,EAAE,eAAe,GAAG,KAAK,EAAE,GAAG,OAAO,CAAA;YAC3C,IAAI,IAAI,CAAC,OAAO,EAAE;gBAAE,OAAM;YAE1B,IAAI,CAAC,eAAe,GAAG,IAAI,CAAA;YAE3B,MAAM,eAAe,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAA;YACjD,IAAI,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,SAAS,EAAE,EAAE,CAAC;gBACjC,kGAAkG;gBAClG,MAAM,eAAe,CAAC,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC,CAAA;gBACjD,OAAM;YACR,CAAC;YACD,2EAA2E;YAC3E,IAAI,CAAC,eAAe,GAAG,eAAe,CAAA;YACtC,4FAA4F;YAC5F,MAAM,IAAI,CAAC,YAAY,EAAE,CAAA;YACzB,MAAM,IAAI,CAAC,SAAS,CAAC,kBAAS,CAAC,QAAQ,CAAC,CAAA;QAC1C,CAAC;KAAA;IAED;;;OAGG;IACO,6BAA6B;QACrC,OAAO,IAAI,CAAC,eAAe,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAA;IAClD,CAAC;IAED;;OAEG;IACW,YAAY;;YACxB,IAAI,IAAI,CAAC,eAAe;gBAAE,OAAM;YAChC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAA;YAC3B,MAAM,IAAI,CAAC,SAAS,EAAE,CAAA;QACxB,CAAC;KAAA;IAEK,KAAK;6DAAC,MAAM,GAAuB,IAAI;;YAC3C,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;YAEtB,IAAI,CAAC;gBACH,IAAI,MAAM,EAAE,CAAC;oBACX,MAAM,MAAM,CAAC,EAAE,CAAC,CAAO,EAAO,EAAE,EAAE;wBAChC,IAAI,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,CAAA;wBACpB,MAAM,IAAI,CAAC,oBAAoB,EAAE,CAAA;oBACnC,CAAC,CAAA,CAAC,CAAA;gBACJ,CAAC;qBAAM,CAAC;oBACN,MAAM,IAAI,CAAC,oBAAoB,EAAE,CAAA;gBACnC,CAAC;gBACD,IAAI,CAAC,IAAI,CAAC,6BAA6B,EAAE,EAAE,CAAC;oBAC1C,MAAM,IAAI,CAAC,SAAS,CAAC,kBAAS,CAAC,SAAS,CAAC,CAAA;gBAC3C,CAAC;YACH,CAAC;YAAC,OAAO,KAAU,EAAE,CAAC;gBACpB,+FAA+F;gBAC/F,IAAI,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;oBACpE,IAAI,CAAC,QAAQ,OAAC,KAAK,CAAC,KAAK,mCAAI,KAAK,CAAC,CAAA;oBACnC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAA;oBAChD,IAAI,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;oBACrE,MAAM,IAAI,CAAC,SAAS,CAAC,kBAAS,CAAC,MAAM,CAAC,CAAA;gBACxC,CAAC;YACH,CAAC;oBAAS,CAAC;gBACT,IAAI,CAAC,OAAO,CAAC,EAAE,GAAG,SAAS,CAAA;YAC7B,CAAC;QACH,CAAC;KAAA;IAEa,oBAAoB;;YAChC,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,OAAO,EAAE,CAAA;gBAEpB,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,aAAa,EAAE,CAAA;gBAChD,IAAI,aAAa,EAAE,CAAC;oBAClB,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBAC9B,MAAM,IAAI,CAAC,WAAW,EAAE,CAAA;oBAC1B,CAAC;yBAAM,CAAC;wBACN,MAAM,IAAI,CAAC,OAAO,EAAE,CAAA;oBACtB,CAAC;oBACD,IAAI,CAAC,IAAI,CAAC,6BAA6B,EAAE,EAAE,CAAC;wBAC1C,MAAM,IAAI,CAAC,aAAa,EAAE,CAAA;oBAC5B,CAAC;gBACH,CAAC;YACH,CAAC;oBAAS,CAAC;gBACT,2CAA2C;gBAC3C,MAAM,IAAI,CAAC,YAAY,EAAE,CAAA;gBACzB,IAAI,CAAC,OAAO,CAAC,EAAE,GAAG,SAAS,CAAA;YAC7B,CAAC;YAED,IAAI,IAAI,CAAC,6BAA6B,EAAE,EAAE,CAAC;gBACzC,IAAI,CAAC,UAAU,CAAC,uBAAe,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAA;YAC5D,CAAC;QACH,CAAC;KAAA;IAED;;OAEG;IACH,MAAM;QACJ,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,IAAI,CAAA;QAE/E,OAAO;YACL,IAAI;YACJ,IAAI;YACJ,QAAQ;YACR,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;YACpD,oBAAoB,EAAE,IAAI,CAAC,oBAAoB;YAE/C,MAAM;YACN,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE;YACzB,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE;YACzB,SAAS,EAAE,IAAI,CAAC,WAAW,EAAE;YAC7B,QAAQ,EAAE,IAAI,CAAC,UAAU,EAAE;YAC3B,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE;YACvB,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE;YAErB,KAAK;YACL,SAAS;YACT,eAAe,EAAE,IAAI,CAAC,wBAAwB,EAAE;YAChD,aAAa,EAAE,IAAI,CAAC,sBAAsB,EAAE;YAE5C,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;YAC5C,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;SAChD,CAAA;IACH,CAAC;IAEa,WAAW;;YACvB,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,KAAK,mBAAmB,CAAC,CAAA;YAEjD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC/C,IAAI,CAAC,QAAQ,CAAC,yBAAyB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;gBAC/C,IAAI,CAAC,oBAAoB,GAAG,CAAC,CAAA;gBAC7B,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;gBACzC,IAAI,eAAe,CAAC,OAAO,EAAE,CAAC;oBAC5B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,OAAO,CAAC,CAAA;gBACtD,CAAC;gBACD,eAAe,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;gBACtC,eAAe,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;oBAChC,4GAA4G;oBAC5G,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC,mBAAmB;yBAChD,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;yBACvC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,kBAAC,IAAI,CAAC,QAAQ,OAAC,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,KAAK,mCAAI,KAAK,CAAC,GAAA,CAAC,CAAA;gBAC3D,CAAC,CAAC,CAAA;gBAEF,4FAA4F;gBAC5F,0FAA0F;gBAC1F,0FAA0F;gBAC1F,yFAAyF;gBACzF,8FAA8F;gBAC9F,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,CAAA;gBAChC,MAAM,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;gBACrC,IAAI,CAAC,OAAO,CAAC,EAAE,GAAG,QAAQ,CAAA;gBAC1B,6EAA6E;gBAC7E,MAAM,IAAI,CAAC,mBAAmB,CAAA;gBAE9B,IAAI,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC,UAAU,EAAE,IAAI,eAAe,CAAC,UAAU,EAAE,EAAE,CAAC;oBAC9E,MAAK;gBACP,CAAC;gBACD,IAAI,eAAe,CAAC,WAAW,EAAE,EAAE,CAAC;oBAClC,IAAI,CAAC,uBAAuB,EAAE,CAAA;gBAChC,CAAC;qBAAM,IAAI,IAAI,CAAC,qBAAqB,EAAE,CAAC;oBACtC,MAAK;gBACP,CAAC;YACH,CAAC;YAED,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,SAAS,oCAAoC,CAAC,CAAA;QACxE,CAAC;KAAA;IAED;;;;;;;;OAQG;IACa,OAAO;;YACrB,+BAA+B;QACjC,CAAC;KAAA;IAED;;;OAGG;IACO,aAAa;QACrB,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9B,CAAC;IAES,uBAAuB,CAAC,WAAW,GAAG,CAAC;QAC/C,IAAI,CAAC,SAAS,IAAI,WAAW,CAAA;IAC/B,CAAC;IAEe,SAAS,CAAC,MAAiB;;YACzC,IAAI,CAAC,QAAQ,CAAC,eAAe,MAAM,EAAE,CAAC,CAAA;YACtC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;YAEpB,MAAM,KAAK,GAAa,EAAE,IAAI,EAAE,uBAAY,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAA;YACjH,IAAI,MAAM,KAAK,kBAAS,CAAC,MAAM,EAAE,CAAC;gBAChC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;YAC5B,CAAC;YAED,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;gBACnB,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA;gBACzB,MAAM,IAAI,CAAC,KAAK,EAAE,CAAA;gBAClB,IAAI,CAAC,QAAQ,CAAC,+BAA+B,IAAI,CAAC,uBAAuB,EAAE,CAAC,CAAA;YAC9E,CAAC;YAED,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;QACzB,CAAC;KAAA;IAED;;OAEG;IACa,eAAe,CAAC,KAAe;;;;YAC7C,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAA;YACxB,IAAI,MAAM,KAAK,kBAAS,CAAC,QAAQ,EAAE,CAAC;gBAClC,IAAI,CAAC,eAAe,SAAG,MAAA,IAAI,CAAC,kBAAkB,EAAE,0CAAE,eAAe,mCAAI,KAAK,CAAA;gBAC1E,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;YAC/B,CAAC;YACD,IAAI,MAAM,KAAK,kBAAS,CAAC,MAAM,EAAE,CAAC;gBAChC,gGAAgG;gBAChG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,QAAE,MAAA,IAAI,CAAC,kBAAkB,EAAE,0CAAE,MAAM,mCAAI,EAAE,CAAC,CAAA;gBACnE,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;YAC/B,CAAC;YACD,IAAI,MAAM,KAAK,kBAAS,CAAC,OAAO,EAAE,CAAC;gBACjC,IAAI,CAAC,WAAW,CAAC;oBACf,IAAI,EAAE,uBAAY,CAAC,QAAQ;oBAC3B,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,SAAS,EAAE,IAAI,CAAC,SAAS;iBAC1B,CAAC,CAAA;gBACF,OAAM;YACR,CAAC;YACD,IAAI,CAAC,QAAQ,CAAC,6BAA6B,MAAM,EAAE,CAAC,CAAA;QACtD,CAAC;KAAA;IAED;;OAEG;IACa,OAAO;;YACrB,IAAI,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAA;YAC3B,MAAM,IAAI,CAAC,SAAS,CAAC,kBAAS,CAAC,OAAO,CAAC,CAAA;QACzC,CAAC;KAAA;IAED;;;;OAIG;IACa,aAAa;;YAC3B,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC,CAAA;QAC7C,CAAC;KAAA;IAED;;;;OAIG;IACO,cAAc;QACtB,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACrC,CAAC;IAED;;;;;OAKG;IACO,SAAS,CAAC,MAAqB;QACvC,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC3F,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QACpC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACtB,CAAC;IACH,CAAC;IAED;;;OAGG;IACO,SAAS;QACjB,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QACxB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;IAC1B,CAAC;IAED;;;OAGG;IACO,KAAK;QACb,IAAI,CAAC,OAAO,GAAG,IAAI,IAAI,EAAE,CAAA;QACzB,IAAI,CAAC,oBAAoB,EAAE,CAAA;QAC3B,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;IAC1B,CAAC;IAES,QAAQ,CAAC,KAAU,EAAE,QAAiB;QAC9C,MAAM,GAAG,GAAG,QAAQ,aAAR,QAAQ,cAAR,QAAQ,GAAI,MAAM,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAA;QAClD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;IAC1B,CAAC;IAES,UAAU,CAAC,QAAgB,EAAE,MAAwB;QAC7D,wFAAwF;QACxF,MAAM,IAAI,mBAAW,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IACzC,CAAC;IAES,YAAY,CAAC,KAAU;QAC/B,IAAI,KAAK,YAAY,mBAAW,EAAE,CAAC;YACjC,OAAO,EAAE,GAAG,EAAE,aAAa,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAA;QAChE,CAAC;QACD,OAAO,EAAE,GAAG,EAAE,mBAAmB,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,EAAE,CAAA;IACzE,CAAC;IAIS,QAAQ,CAAC,GAAG,IAAW;QAC/B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAA;IAC5B,CAAC;IAES,OAAO,CAAC,GAAG,IAAW;QAC9B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAA;IAC3B,CAAC;IAES,OAAO,CAAC,GAAG,IAAW;QAC9B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAA;IAC3B,CAAC;IAES,QAAQ,CAAC,GAAG,IAAW;QAC/B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAA;IAC5B,CAAC;IAED,IAAY,uBAAuB;QACjC,MAAM,WAAW,GAAG,IAAI,CAAC,sBAAsB,EAAE,CAAA;QACjD,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,IAAI,GAAG,EAAE,CAAC,CAAA;QACvD,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;QACnF,MAAM,aAAa,GAAG,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;QACjE,OAAO,GAAG,WAAW,IAAI,cAAc,IAAI,aAAa,EAAE,CAAA;IAC5D,CAAC;IAEO,wBAAwB;QAC9B,IAAI,IAAI,CAAC,WAAW,EAAE;YAAE,OAAO,GAAG,CAAA;QAClC,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAA;QAC1E,OAAO,CAAC,CAAA;IACV,CAAC;IAEO,wBAAwB;QAC9B,MAAM,eAAe,GAAG,IAAI,CAAC,wBAAwB,EAAE,CAAA;QACvD,MAAM,eAAe,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAA;QACjD,IACE,CAAC,eAAe;YAChB,IAAI,CAAC,oBAAoB,GAAG,CAAC;YAC7B,eAAe,KAAK,GAAG;YACvB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,oBAAoB,EAC1C,CAAC;YACD,OAAO,eAAe,CAAA;QACxB,CAAC;QACD,OAAO,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,wBAAwB,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAA;IAC9F,CAAC;IAEO,sBAAsB;;QAC5B,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,OAAO,CAAC,CAAA;QAC7B,OAAO,OAAC,IAAI,CAAC,OAAO,mCAAI,IAAI,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAA;IAC1E,CAAC;IAED;;;OAGG;IACK,cAAc;QACpB,MAAM,GAAG,GAAG,GAAS,EAAE;YACrB,IAAI,CAAC,yBAAyB,GAAG,SAAS,CAAA;YAC1C,IAAI,CAAC,2BAA2B,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;YAC7C,IAAI,CAAC,WAAW,CAAC;gBACf,IAAI,EAAE,uBAAY,CAAC,QAAQ;gBAC3B,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,SAAS,EAAE,IAAI,CAAC,SAAS;aAC1B,CAAC,CAAA;QACJ,CAAC,CAAA;QAED,IAAI,IAAI,CAAC,yBAAyB;YAAE,OAAM;QAE1C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,2BAA2B,CAAA;QAC7D,IAAI,CAAC,IAAI,CAAC,2BAA2B,IAAI,OAAO,IAAI,qCAAqC,EAAE,CAAC;YAC1F,GAAG,EAAE,CAAA;QACP,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,yBAAyB,GAAG,UAAU,CAAC,GAAG,EAAE,qCAAqC,GAAG,OAAO,CAAC,CAAA;QACnG,CAAC;IACH,CAAC;IAED;;OAEG;IACK,oBAAoB;QAC1B,IAAI,IAAI,CAAC,yBAAyB,EAAE,CAAC;YACnC,YAAY,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAA;YAC5C,IAAI,CAAC,yBAAyB,GAAG,SAAS,CAAA;QAC5C,CAAC;QACD,IAAI,CAAC,2BAA2B,GAAG,CAAC,CAAA;IACtC,CAAC;IAED;;;;OAIG;IACK,WAAW,CAAC,KAAe;QACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAA;QACnC,IAAI,CAAC,QAAQ;YAAE,OAAM;QACrB,IAAI,CAAC;YACH,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAA;QACjF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAA;QAC9B,CAAC;IACH,CAAC;IAEO,gBAAgB,CAAC,KAAU;;QACjC,IAAI,CAAC,QAAQ,CAAC,gCAAgC,MAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,KAAK,mCAAI,KAAK,EAAE,CAAC,CAAA;IACxE,CAAC;CACF;;AA3jBiB,mBAAW,GAAG;IAC5B,QAAQ,EAAE,UAAU;IACpB,MAAM,EAAE,QAAQ;IAChB,IAAI,EAAE,MAAM;CACb,AAJ0B,CAI1B","sourcesContent":["import { User } from '../auth'\nimport { Dictionary } from '../common'\nimport { SystemError, SystemErrorKeys } from '../error'\nimport { Logger } from '../logger'\nimport { Survey } from '../survey'\nimport { UUIDs } from '../utils'\n\nimport { Job } from './job'\nimport { JobContext } from './jobContext'\nimport { JobEvent, JobEventType } from './jobEvent'\nimport { JobSerialized } from './jobSerialized'\nimport { JobStatus } from './status'\n\nexport interface JobConstructor {\n new <C extends JobContext, R>(context: C, innerJobs?: JobBase<any>[]): JobBase<C, R>\n readonly prototype: JobBase<any, any>\n}\n\n/**\n * Client able to open a transaction (e.g. a pg-promise database): the job runs inside `client.tx(callback)`.\n */\nexport interface JobTxClient {\n tx(callback: (tx: any) => Promise<void>): Promise<unknown>\n}\n\nconst PROGRESS_NOTIFICATION_THROTTLE_MILLIS = 1000\n\n/**\n * Asynchronous task handler.\n *\n * Status workflow:\n * - pending\n * - running\n * - (end)\n * -- succeeded\n * -- failed\n * -- canceled\n *\n * Methods that can be overwritten by subclasses:\n * - shouldExecute (in tx)\n * - onStart (in tx)\n * - execute (in tx)\n * - beforeSuccess (in tx)\n * - generateResult (in tx)\n * - beforeEnd (in tx)\n * - onEnd (out of tx)\n * - getErrorInfo\n */\nexport abstract class JobBase<C extends JobContext, R = undefined> implements Job<R> {\n static readonly keysContext = {\n surveyId: 'surveyId',\n survey: 'survey',\n user: 'user',\n }\n\n readonly uuid: string\n readonly type: string\n status: JobStatus = JobStatus.pending\n startTime?: Date\n endTime?: Date\n total: number\n result: R | undefined = undefined\n errors: Record<string, any> = {}\n canceledByAdmin?: boolean\n\n protected logger: Logger\n protected context: C\n protected innerJobs: JobBase<C, any>[]\n protected currentInnerJobIndex = -1\n\n private _processed = 0\n private eventListener: ((event: JobEvent) => unknown) | undefined = undefined\n private progressThrottleTimeoutId: ReturnType<typeof setTimeout> | undefined = undefined\n private progressThrottleLastRunTime = 0\n private _stopOnInnerJobFailure = true\n // set as soon as cancel() is called (the canceled status is set only after beforeEnd has run)\n private cancelRequested = false\n private beforeEndCalled = false\n // inner job events are handled in sequence; executeJobs waits for them before going on\n private innerJobEventsQueue: Promise<void> = Promise.resolve()\n\n public constructor(context: C, innerJobs: JobBase<C, any>[] = []) {\n this.context = { ...context }\n this.innerJobs = innerJobs\n\n this.uuid = UUIDs.v4()\n this.type = this.context.type ?? this.constructor.name\n this.total = innerJobs.length > 0 ? innerJobs.length : 1\n\n // Created last, so that createLogger() overrides can safely build the logger name out of\n // the job identity (e.g. arena's `Job <ClassName> (<uuid>)` log correlation id).\n this.logger = this.createLogger()\n }\n\n get processed(): number {\n return this._processed\n }\n\n set processed(value: number) {\n this._processed = value\n this.notifyProgress()\n }\n\n get stopOnInnerJobFailure(): boolean {\n return this._stopOnInnerJobFailure\n }\n\n set stopOnInnerJobFailure(value: boolean) {\n this._stopOnInnerJobFailure = value\n }\n\n protected get surveyId(): number | null {\n return this.context.surveyId ?? null\n }\n\n protected get user(): User {\n return this.context.user\n }\n\n protected get userUuid(): string | undefined {\n return this.user?.uuid\n }\n\n protected get contextSurvey(): Survey | undefined {\n return this.context.survey\n }\n\n /**\n * Returns the value of the specified context prop, or the default value (null if not specified) when not set.\n */\n protected getContextProp<K extends keyof C & string>(prop: K, defaultValue?: C[K] | null): C[K] | null\n /** Context prop not declared in the context type: the value is typed by the caller. */\n protected getContextProp<T = any, P extends string = string>(\n prop: P extends keyof C ? never : P,\n defaultValue?: T | null\n ): T | null\n protected getContextProp(prop: string, defaultValue: unknown = null): unknown {\n const value = (this.context as Record<string, unknown>)[prop]\n return value ?? defaultValue\n }\n\n protected setContext(context: Partial<C>): void {\n Object.assign(this.context, context)\n }\n\n protected deleteContextProps(...propNames: string[]): void {\n propNames.forEach((propName) => {\n delete (this.context as any)[propName]\n })\n }\n\n /**\n * Registers the listener notified of job events (status changes and progress).\n * Only one listener can be registered at a time; registering a new one replaces the previous one.\n * Errors thrown (or rejections returned) by the listener are logged and do not affect the job execution.\n */\n onEvent(listener: (event: JobEvent) => unknown): this {\n this.eventListener = listener\n return this\n }\n\n isPending(): boolean {\n return this.status === JobStatus.pending\n }\n\n isRunning(): boolean {\n return this.status === JobStatus.running\n }\n\n isSucceeded(): boolean {\n return this.status === JobStatus.succeeded\n }\n\n isFailed(): boolean {\n return this.status === JobStatus.failed\n }\n\n isCanceled(): boolean {\n return this.status === JobStatus.canceled\n }\n\n isEnded(): boolean {\n return [JobStatus.succeeded, JobStatus.failed, JobStatus.canceled].includes(this.status)\n }\n\n hasErrors(): boolean {\n return Object.keys(this.errors).length > 0\n }\n\n protected getCurrentInnerJob(): JobBase<C, any> | undefined {\n return this.innerJobs[this.currentInnerJobIndex]\n }\n\n protected combineInnerJobsResults(): Record<string, any> {\n const results: Record<string, any> = {}\n this.innerJobs.forEach((innerJob) => Object.assign(results, innerJob.result ?? {}))\n return results\n }\n\n protected combineInnerJobsErrors(): Record<string, any> {\n const errors: Record<string, any> = {}\n this.innerJobs.forEach((innerJob) => Object.assign(errors, innerJob.errors ?? {}))\n return errors\n }\n\n async cancel(options: { canceledByAdmin?: boolean } = {}): Promise<void> {\n const { canceledByAdmin = false } = options\n if (this.isEnded()) return\n\n this.cancelRequested = true\n\n const currentInnerJob = this.getCurrentInnerJob()\n if (currentInnerJob?.isRunning()) {\n // the parent job status will be set to canceled when handling the canceled event of the inner job\n await currentInnerJob.cancel({ canceledByAdmin })\n return\n }\n // no inner job running (job without inner jobs, or between two inner jobs)\n this.canceledByAdmin = canceledByAdmin\n // release the resources (temp files/directories, streams) allocated before the cancellation\n await this.runBeforeEnd()\n await this.setStatus(JobStatus.canceled)\n }\n\n /**\n * Returns true when the job has been canceled (or is no longer running).\n * Long-running subclasses can check it to stop processing items (e.g. in a loop) as soon as the job is canceled.\n */\n protected isCancelRequestedOrNotRunning(): boolean {\n return this.cancelRequested || !this.isRunning()\n }\n\n /**\n * Runs beforeEnd only once (it can be called by cancel() and when the job execution ends).\n */\n private async runBeforeEnd(): Promise<void> {\n if (this.beforeEndCalled) return\n this.beforeEndCalled = true\n await this.beforeEnd()\n }\n\n async start(client: JobTxClient | null = null): Promise<void> {\n this.logDebug('start')\n\n try {\n if (client) {\n await client.tx(async (tx: any) => {\n this.context.tx = tx\n await this.executeInTransaction()\n })\n } else {\n await this.executeInTransaction()\n }\n if (!this.isCancelRequestedOrNotRunning()) {\n await this.setStatus(JobStatus.succeeded)\n }\n } catch (error: any) {\n // a job can fail while still pending (e.g. the transaction cannot be opened, or onStart fails)\n if (!this.cancelRequested && !this.isFailed() && !this.isCanceled()) {\n this.logError(error.stack ?? error)\n const { key, params } = this.getErrorInfo(error)\n this.addError({ error: { valid: false, errors: [{ key, params }] } })\n await this.setStatus(JobStatus.failed)\n }\n } finally {\n this.context.tx = undefined\n }\n }\n\n private async executeInTransaction(): Promise<void> {\n try {\n await this.onStart()\n\n const shouldExecute = await this.shouldExecute()\n if (shouldExecute) {\n if (this.innerJobs.length > 0) {\n await this.executeJobs()\n } else {\n await this.execute()\n }\n if (!this.isCancelRequestedOrNotRunning()) {\n await this.beforeSuccess()\n }\n }\n } finally {\n // runs only if not already run by cancel()\n await this.runBeforeEnd()\n this.context.tx = undefined\n }\n\n if (this.isCancelRequestedOrNotRunning()) {\n this.throwError(SystemErrorKeys.job.canceledOrErrorsFound)\n }\n }\n\n /**\n * Generates a plain JSON-serializable representation of the job, including its inner jobs.\n */\n toJSON(): JobSerialized<R> {\n const { errors, processed, result, status, total, type, userUuid, uuid } = this\n\n return {\n uuid,\n type,\n userUuid,\n surveyId: this.surveyId,\n innerJobs: this.innerJobs.map((job) => job.toJSON()),\n currentInnerJobIndex: this.currentInnerJobIndex,\n\n status,\n pending: this.isPending(),\n running: this.isRunning(),\n succeeded: this.isSucceeded(),\n canceled: this.isCanceled(),\n failed: this.isFailed(),\n ended: this.isEnded(),\n\n total,\n processed,\n progressPercent: this.calculateProgressPercent(),\n elapsedMillis: this.calculateElapsedMillis(),\n\n errors: this.isFailed() ? errors : undefined,\n result: this.isSucceeded() ? result : undefined,\n }\n }\n\n private async executeJobs(): Promise<void> {\n this.logDebug(`- ${this.total} inner jobs found`)\n\n for (let i = 0; i < this.innerJobs.length; i++) {\n this.logDebug(`- executing inner job ${i + 1}`)\n this.currentInnerJobIndex = i\n const currentInnerJob = this.innerJobs[i]\n if (currentInnerJob.context) {\n Object.assign(this.context, currentInnerJob.context)\n }\n currentInnerJob.context = this.context\n currentInnerJob.onEvent((event) => {\n // handle the inner job events in sequence; errors are logged (they would be unhandled rejections otherwise)\n this.innerJobEventsQueue = this.innerJobEventsQueue\n .then(() => this.onInnerJobEvent(event))\n .catch((error) => this.logError(error?.stack ?? error))\n })\n\n // The inner job shares this very context object, and its own start()/executeInTransaction()\n // clear `context.tx` when they terminate: without saving and restoring it here, the first\n // inner job would wipe the parent's transaction handle, leaving every following inner job\n // (and the parent's own beforeSuccess()/beforeEnd()) running outside of the transaction.\n // start() never throws (it swallows/records errors internally), so a plain restore is enough.\n const parentTx = this.context.tx\n await currentInnerJob.start(parentTx)\n this.context.tx = parentTx\n // the inner job events (e.g. canceled, failed) update the status of this job\n await this.innerJobEventsQueue\n\n if (this.cancelRequested || this.isCanceled() || currentInnerJob.isCanceled()) {\n break\n }\n if (currentInnerJob.isSucceeded()) {\n this.incrementProcessedItems()\n } else if (this.stopOnInnerJobFailure) {\n break\n }\n }\n\n this.logDebug(`- ${this.processed} inner jobs processed successfully`)\n }\n\n /**\n * Runs the job's own work when it has no inner jobs. Default implementation is a no-op, so\n * subclasses that only supply inner jobs (and never override this) don't need to. It also means\n * a subclass can safely call `super.execute()` defensively without knowing whether any ancestor\n * overrides it - if this were `abstract` instead, TypeScript would compile away any runtime\n * property for it entirely, so `super.execute()` would throw the moment no ancestor overrides it\n * (confirmed in production: a subclass whose immediate parent didn't override `execute()` crashed\n * with \"(intermediate value).execute is not a function\" the instant it called `super.execute()`).\n */\n protected async execute(): Promise<void> {\n // to be extended by subclasses\n }\n\n /**\n * Determines whether the job should actually run.\n * When it returns false, \"execute\" (or the inner jobs) will be skipped, but the job will still succeed.\n */\n protected shouldExecute(): Promise<boolean> {\n return Promise.resolve(true)\n }\n\n protected incrementProcessedItems(incrementBy = 1): void {\n this.processed += incrementBy\n }\n\n protected async setStatus(status: JobStatus): Promise<void> {\n this.logDebug(`set status: ${status}`)\n this.status = status\n\n const event: JobEvent = { type: JobEventType.statusChange, status, total: this.total, processed: this.processed }\n if (status === JobStatus.failed) {\n event.errors = this.errors\n }\n\n if (this.isEnded()) {\n this.logDebug('onEnd...')\n await this.onEnd()\n this.logDebug(`onEnd run. Job completed in ${this.elapsedTimePrettyFormat}`)\n }\n\n this.notifyEvent(event)\n }\n\n /**\n * Inner job event handler.\n */\n protected async onInnerJobEvent(event: JobEvent): Promise<void> {\n const { status } = event\n if (status === JobStatus.canceled) {\n this.canceledByAdmin = this.getCurrentInnerJob()?.canceledByAdmin ?? false\n return this.setStatus(status)\n }\n if (status === JobStatus.failed) {\n // report the errors of the failed inner job in the failed event (and in the errors) of this job\n Object.assign(this.errors, this.getCurrentInnerJob()?.errors ?? {})\n return this.setStatus(status)\n }\n if (status === JobStatus.running) {\n this.notifyEvent({\n type: JobEventType.progress,\n status: this.status,\n total: this.total,\n processed: this.processed,\n })\n return\n }\n this.logDebug(`Unknown inner job status: ${status}`)\n }\n\n /**\n * Called when the job just has been started.\n */\n protected async onStart(): Promise<void> {\n this.startTime = new Date()\n await this.setStatus(JobStatus.running)\n }\n\n /**\n * Called before beforeEnd only if the status will change to 'success'.\n * Default implementation stores the value returned by generateResult() via setResult().\n * It runs INSIDE the current db transaction.\n */\n protected async beforeSuccess(): Promise<void> {\n this.setResult(await this.generateResult())\n }\n\n /**\n * Computes the value beforeSuccess() will store as this job's result.\n * Default implementation returns whatever is already in `this.result`\n * (e.g. accumulated via earlier setResult() calls during execute()).\n */\n protected generateResult(): Promise<R | undefined> {\n return Promise.resolve(this.result)\n }\n\n /**\n * Updates this job's result. When both the current and incoming values are plain objects,\n * they are merged (Object.assign); otherwise the incoming value replaces the current one.\n * This keeps it safe for subclasses whose result is a primitive (e.g. a plain number) as\n * well as ones that build an object result incrementally across multiple setResult() calls.\n */\n protected setResult(result: R | undefined): void {\n if (result && typeof result === 'object' && this.result && typeof this.result === 'object') {\n Object.assign(this.result, result)\n } else {\n this.result = result\n }\n }\n\n /**\n * Called before onEnd. Useful for flushing resources used by the job before it terminates completely.\n * It runs INSIDE the current db transaction.\n */\n protected beforeEnd(): Promise<void> {\n this.logDebug('Cleanup')\n return Promise.resolve()\n }\n\n /**\n * Called when the job status changes to success, failed or canceled\n * (it runs OUTSIDE of the current db transaction)\n */\n protected onEnd(): Promise<void> {\n this.endTime = new Date()\n this.cancelNotifyProgress()\n return Promise.resolve()\n }\n\n protected addError(error: any, errorKey?: string): void {\n const key = errorKey ?? String(this.processed + 1)\n this.errors[key] = error\n }\n\n protected throwError(errorKey: string, params?: Dictionary<any>): void {\n // SystemError: getErrorInfo will report the translatable key instead of a generic error\n throw new SystemError(errorKey, params)\n }\n\n protected getErrorInfo(error: any): { key: string; params: Record<string, any> } {\n if (error instanceof SystemError) {\n return { key: `appErrors:${error.key}`, params: error.params }\n }\n return { key: 'appErrors:generic', params: { text: error.toString() } }\n }\n\n protected abstract createLogger(): Logger\n\n protected logDebug(...msgs: any[]): void {\n this.logger.debug(...msgs)\n }\n\n protected logInfo(...msgs: any[]): void {\n this.logger.info(...msgs)\n }\n\n protected logWarn(...msgs: any[]): void {\n this.logger.warn(...msgs)\n }\n\n protected logError(...msgs: any[]): void {\n this.logger.error(...msgs)\n }\n\n private get elapsedTimePrettyFormat(): string {\n const elapsedTime = this.calculateElapsedMillis()\n const elapsedMins = Math.floor(elapsedTime / 1000 / 60)\n const elapsedSeconds = String(Math.floor(elapsedTime / 1000) % 60).padStart(2, '0')\n const elapsedMillis = String(elapsedTime % 1000).padStart(3, '0')\n return `${elapsedMins}:${elapsedSeconds}.${elapsedMillis}`\n }\n\n private calculatePartialProgress(): number {\n if (this.isSucceeded()) return 100\n if (this.total > 0) return Math.floor((100 * this.processed) / this.total)\n return 0\n }\n\n private calculateProgressPercent(): number {\n const partialProgress = this.calculatePartialProgress()\n const currentInnerJob = this.getCurrentInnerJob()\n if (\n !currentInnerJob ||\n this.currentInnerJobIndex < 0 ||\n partialProgress === 100 ||\n this.processed > this.currentInnerJobIndex\n ) {\n return partialProgress\n }\n return partialProgress + Math.floor(currentInnerJob.calculateProgressPercent() / this.total)\n }\n\n private calculateElapsedMillis(): number {\n if (!this.startTime) return 0\n return (this.endTime ?? new Date()).getTime() - this.startTime.getTime()\n }\n\n /**\n * Notifies a progress event to the registered listener, throttled so that fast-changing\n * progress does not flood consumers (e.g. web socket clients).\n */\n private notifyProgress(): void {\n const run = (): void => {\n this.progressThrottleTimeoutId = undefined\n this.progressThrottleLastRunTime = Date.now()\n this.notifyEvent({\n type: JobEventType.progress,\n status: this.status,\n total: this.total,\n processed: this.processed,\n })\n }\n\n if (this.progressThrottleTimeoutId) return\n\n const elapsed = Date.now() - this.progressThrottleLastRunTime\n if (!this.progressThrottleLastRunTime || elapsed >= PROGRESS_NOTIFICATION_THROTTLE_MILLIS) {\n run()\n } else {\n this.progressThrottleTimeoutId = setTimeout(run, PROGRESS_NOTIFICATION_THROTTLE_MILLIS - elapsed)\n }\n }\n\n /**\n * Cancels any pending throttled progress notification and resets the throttle state.\n */\n private cancelNotifyProgress(): void {\n if (this.progressThrottleTimeoutId) {\n clearTimeout(this.progressThrottleTimeoutId)\n this.progressThrottleTimeoutId = undefined\n }\n this.progressThrottleLastRunTime = 0\n }\n\n /**\n * Notifies the event to the registered listener.\n * Listener errors are only logged: they would otherwise fail the job or, when thrown inside the progress\n * throttle timeout, crash the process.\n */\n private notifyEvent(event: JobEvent): void {\n const listener = this.eventListener\n if (!listener) return\n try {\n Promise.resolve(listener(event)).catch((error) => this.logListenerError(error))\n } catch (error) {\n this.logListenerError(error)\n }\n }\n\n private logListenerError(error: any): void {\n this.logError(`error in job event listener: ${error?.stack ?? error}`)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"JobBase.js","sourceRoot":"","sources":["../../src/job/JobBase.ts"],"names":[],"mappings":"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","sourcesContent":["import { User } from '../auth'\nimport { Dictionary } from '../common'\nimport { SystemError, SystemErrorKeys } from '../error'\nimport { Logger } from '../logger'\nimport { Survey } from '../survey'\nimport { UUIDs } from '../utils'\n\nimport { Job } from './job'\nimport { JobContext } from './jobContext'\nimport { JobEvent, JobEventType } from './jobEvent'\nimport { JobSerialized } from './jobSerialized'\nimport { JobStatus } from './status'\n\nexport interface JobConstructor {\n new <C extends JobContext, R>(context: C, innerJobs?: JobBase<any>[]): JobBase<C, R>\n readonly prototype: JobBase<any, any>\n}\n\n/**\n * Client able to open a transaction (e.g. a pg-promise database): the job runs inside `client.tx(callback)`.\n */\nexport interface JobTxClient {\n tx(callback: (tx: any) => Promise<void>): Promise<unknown>\n}\n\nconst PROGRESS_NOTIFICATION_THROTTLE_MILLIS = 1000\n\n/**\n * Asynchronous task handler.\n *\n * Status workflow:\n * - pending\n * - running\n * - (end)\n * -- succeeded\n * -- failed\n * -- canceled\n *\n * Methods that can be overwritten by subclasses:\n * - shouldExecute (in tx)\n * - onStart (in tx)\n * - execute (in tx)\n * - beforeSuccess (in tx)\n * - generateResult (in tx)\n * - beforeEnd (in tx)\n * - onEnd (out of tx)\n * - getErrorInfo\n */\nexport abstract class JobBase<C extends JobContext, R = undefined> implements Job<R> {\n static readonly keysContext = {\n surveyId: 'surveyId',\n survey: 'survey',\n user: 'user',\n }\n\n readonly uuid: string\n readonly type: string\n status: JobStatus = JobStatus.pending\n startTime?: Date\n endTime?: Date\n total: number\n result: R | undefined = undefined\n errors: Record<string, any> = {}\n canceledByAdmin?: boolean\n\n protected logger: Logger\n protected context: C\n protected innerJobs: JobBase<C, any>[]\n protected currentInnerJobIndex = -1\n\n private _processed = 0\n private eventListener: ((event: JobEvent) => unknown) | undefined = undefined\n private progressThrottleTimeoutId: ReturnType<typeof setTimeout> | undefined = undefined\n private progressThrottleLastRunTime = 0\n private _stopOnInnerJobFailure = true\n // set as soon as cancel() is called (the canceled status is set only after beforeEnd has run)\n private cancelRequested = false\n private beforeEndCalled = false\n // inner job events are handled in sequence; executeJobs waits for them before going on\n private innerJobEventsQueue: Promise<void> = Promise.resolve()\n\n public constructor(context: C, innerJobs: JobBase<C, any>[] = []) {\n this.context = { ...context }\n this.innerJobs = innerJobs\n\n this.uuid = UUIDs.v4()\n this.type = this.context.type ?? this.constructor.name\n this.total = innerJobs.length > 0 ? innerJobs.length : 1\n\n // Created last, so that createLogger() overrides can safely build the logger name out of\n // the job identity (e.g. arena's `Job <ClassName> (<uuid>)` log correlation id).\n this.logger = this.createLogger()\n }\n\n get processed(): number {\n return this._processed\n }\n\n set processed(value: number) {\n this._processed = value\n this.notifyProgress()\n }\n\n get stopOnInnerJobFailure(): boolean {\n return this._stopOnInnerJobFailure\n }\n\n set stopOnInnerJobFailure(value: boolean) {\n this._stopOnInnerJobFailure = value\n }\n\n protected get surveyId(): number | null {\n return this.context.surveyId ?? null\n }\n\n protected get user(): User {\n return this.context.user\n }\n\n protected get userUuid(): string | undefined {\n return this.user?.uuid\n }\n\n protected get contextSurvey(): Survey | undefined {\n return this.context.survey\n }\n\n /**\n * Returns the value of the specified context prop, or the default value (null if not specified) when not set.\n */\n protected getContextProp<K extends keyof C & string>(prop: K, defaultValue?: C[K] | null): C[K] | null\n /** Context prop not declared in the context type: the value is typed by the caller. */\n protected getContextProp<T = any, P extends string = string>(\n prop: P extends keyof C ? never : P,\n defaultValue?: T | null\n ): T | null\n protected getContextProp(prop: string, defaultValue: unknown = null): unknown {\n const value = (this.context as Record<string, unknown>)[prop]\n return value ?? defaultValue\n }\n\n protected setContext(context: Partial<C>): void {\n Object.assign(this.context, context)\n }\n\n protected deleteContextProps(...propNames: string[]): void {\n propNames.forEach((propName) => {\n delete (this.context as any)[propName]\n })\n }\n\n /**\n * Registers the listener notified of job events (status changes and progress).\n * Only one listener can be registered at a time; registering a new one replaces the previous one.\n * Errors thrown (or rejections returned) by the listener are logged and do not affect the job execution.\n */\n onEvent(listener: (event: JobEvent) => unknown): this {\n this.eventListener = listener\n return this\n }\n\n isPending(): boolean {\n return this.status === JobStatus.pending\n }\n\n isRunning(): boolean {\n return this.status === JobStatus.running\n }\n\n isSucceeded(): boolean {\n return this.status === JobStatus.succeeded\n }\n\n isFailed(): boolean {\n return this.status === JobStatus.failed\n }\n\n isCanceled(): boolean {\n return this.status === JobStatus.canceled\n }\n\n isEnded(): boolean {\n return [JobStatus.succeeded, JobStatus.failed, JobStatus.canceled].includes(this.status)\n }\n\n hasErrors(): boolean {\n return Object.keys(this.errors).length > 0\n }\n\n protected getCurrentInnerJob(): JobBase<C, any> | undefined {\n return this.innerJobs[this.currentInnerJobIndex]\n }\n\n protected combineInnerJobsResults(): Record<string, any> {\n const results: Record<string, any> = {}\n this.innerJobs.forEach((innerJob) => Object.assign(results, innerJob.result ?? {}))\n return results\n }\n\n protected combineInnerJobsErrors(): Record<string, any> {\n const errors: Record<string, any> = {}\n this.innerJobs.forEach((innerJob) => Object.assign(errors, innerJob.errors ?? {}))\n return errors\n }\n\n async cancel(options: { canceledByAdmin?: boolean } = {}): Promise<void> {\n const { canceledByAdmin = false } = options\n if (this.isEnded()) return\n\n this.cancelRequested = true\n\n const currentInnerJob = this.getCurrentInnerJob()\n if (currentInnerJob && !currentInnerJob.isEnded()) {\n // inner job running or about to start (pending):\n // the parent job status will be set to canceled when handling the canceled event of the inner job\n await currentInnerJob.cancel({ canceledByAdmin })\n return\n }\n // no inner job running (job without inner jobs, or between two inner jobs)\n this.canceledByAdmin = canceledByAdmin\n // release the resources (temp files/directories, streams) allocated before the cancellation\n await this.runBeforeEnd()\n await this.setStatus(JobStatus.canceled)\n }\n\n /**\n * Returns true when the job has been canceled (or is no longer running).\n * Long-running subclasses can check it to stop processing items (e.g. in a loop) as soon as the job is canceled.\n */\n protected isCancelRequestedOrNotRunning(): boolean {\n return this.cancelRequested || !this.isRunning()\n }\n\n /**\n * Runs beforeEnd only once (it can be called by cancel() and when the job execution ends).\n */\n private async runBeforeEnd(): Promise<void> {\n if (this.beforeEndCalled) return\n this.beforeEndCalled = true\n await this.beforeEnd()\n }\n\n async start(client: JobTxClient | null = null): Promise<void> {\n // canceled before being started: nothing to execute\n if (this.cancelRequested || this.isEnded()) return\n\n this.logDebug('start')\n\n try {\n if (client) {\n await client.tx(async (tx: any) => {\n this.context.tx = tx\n await this.executeInTransaction()\n })\n } else {\n await this.executeInTransaction()\n }\n if (!this.isCancelRequestedOrNotRunning()) {\n await this.setStatus(JobStatus.succeeded)\n }\n } catch (error: any) {\n // a job can fail while still pending (e.g. the transaction cannot be opened, or onStart fails)\n if (!this.cancelRequested && !this.isFailed() && !this.isCanceled()) {\n this.logError(error.stack ?? error)\n const { key, params } = this.getErrorInfo(error)\n this.addError({ error: { valid: false, errors: [{ key, params }] } })\n await this.setStatus(JobStatus.failed)\n }\n } finally {\n this.context.tx = undefined\n }\n }\n\n private async executeInTransaction(): Promise<void> {\n try {\n // canceled while the transaction was being opened: it must not be set as running again\n if (this.cancelRequested) return\n\n await this.onStart()\n\n const shouldExecute = await this.shouldExecute()\n if (shouldExecute) {\n if (this.innerJobs.length > 0) {\n await this.executeJobs()\n } else {\n await this.execute()\n }\n if (!this.isCancelRequestedOrNotRunning()) {\n await this.beforeSuccess()\n }\n }\n } finally {\n // runs only if not already run by cancel()\n await this.runBeforeEnd()\n this.context.tx = undefined\n }\n\n if (this.isCancelRequestedOrNotRunning()) {\n this.throwError(SystemErrorKeys.job.canceledOrErrorsFound)\n }\n }\n\n /**\n * Generates a plain JSON-serializable representation of the job, including its inner jobs.\n */\n toJSON(): JobSerialized<R> {\n const { errors, processed, result, status, total, type, userUuid, uuid } = this\n\n return {\n uuid,\n type,\n userUuid,\n surveyId: this.surveyId,\n innerJobs: this.innerJobs.map((job) => job.toJSON()),\n currentInnerJobIndex: this.currentInnerJobIndex,\n\n status,\n pending: this.isPending(),\n running: this.isRunning(),\n succeeded: this.isSucceeded(),\n canceled: this.isCanceled(),\n failed: this.isFailed(),\n ended: this.isEnded(),\n\n total,\n processed,\n progressPercent: this.calculateProgressPercent(),\n elapsedMillis: this.calculateElapsedMillis(),\n\n errors: this.isFailed() ? errors : undefined,\n result: this.isSucceeded() ? result : undefined,\n }\n }\n\n private async executeJobs(): Promise<void> {\n this.logDebug(`- ${this.total} inner jobs found`)\n\n for (let i = 0; i < this.innerJobs.length; i++) {\n // canceled between two inner jobs: the next ones must not be started\n if (this.cancelRequested) break\n\n this.logDebug(`- executing inner job ${i + 1}`)\n this.currentInnerJobIndex = i\n const currentInnerJob = this.innerJobs[i]\n if (currentInnerJob.context) {\n Object.assign(this.context, currentInnerJob.context)\n }\n currentInnerJob.context = this.context\n currentInnerJob.onEvent((event) => {\n // handle the inner job events in sequence; errors are logged (they would be unhandled rejections otherwise)\n this.innerJobEventsQueue = this.innerJobEventsQueue\n .then(() => this.onInnerJobEvent(event))\n .catch((error) => this.logError(error?.stack ?? error))\n })\n\n // The inner job shares this very context object, and its own start()/executeInTransaction()\n // clear `context.tx` when they terminate: without saving and restoring it here, the first\n // inner job would wipe the parent's transaction handle, leaving every following inner job\n // (and the parent's own beforeSuccess()/beforeEnd()) running outside of the transaction.\n // start() never throws (it swallows/records errors internally), so a plain restore is enough.\n const parentTx = this.context.tx\n await currentInnerJob.start(parentTx)\n this.context.tx = parentTx\n // the inner job events (e.g. canceled, failed) update the status of this job\n await this.innerJobEventsQueue\n\n if (this.cancelRequested || this.isCanceled() || currentInnerJob.isCanceled()) {\n break\n }\n if (currentInnerJob.isSucceeded()) {\n this.incrementProcessedItems()\n } else if (this.stopOnInnerJobFailure) {\n break\n }\n }\n if (\n !this.stopOnInnerJobFailure &&\n !this.cancelRequested &&\n this.innerJobs.some((innerJob) => innerJob.isFailed())\n ) {\n // set only after all inner jobs have run: setting it at the first failure would end this job (onEnd) too early\n await this.setStatus(JobStatus.failed)\n }\n\n this.logDebug(`- ${this.processed} inner jobs processed successfully`)\n }\n\n /**\n * Runs the job's own work when it has no inner jobs. Default implementation is a no-op, so\n * subclasses that only supply inner jobs (and never override this) don't need to. It also means\n * a subclass can safely call `super.execute()` defensively without knowing whether any ancestor\n * overrides it - if this were `abstract` instead, TypeScript would compile away any runtime\n * property for it entirely, so `super.execute()` would throw the moment no ancestor overrides it\n * (confirmed in production: a subclass whose immediate parent didn't override `execute()` crashed\n * with \"(intermediate value).execute is not a function\" the instant it called `super.execute()`).\n */\n protected async execute(): Promise<void> {\n // to be extended by subclasses\n }\n\n /**\n * Determines whether the job should actually run.\n * When it returns false, \"execute\" (or the inner jobs) will be skipped, but the job will still succeed.\n */\n protected shouldExecute(): Promise<boolean> {\n return Promise.resolve(true)\n }\n\n protected incrementProcessedItems(incrementBy = 1): void {\n this.processed += incrementBy\n }\n\n protected async setStatus(status: JobStatus): Promise<void> {\n this.logDebug(`set status: ${status}`)\n this.status = status\n\n const event: JobEvent = { type: JobEventType.statusChange, status, total: this.total, processed: this.processed }\n if (status === JobStatus.failed) {\n event.errors = this.errors\n }\n\n if (this.isEnded()) {\n this.logDebug('onEnd...')\n await this.onEnd()\n this.logDebug(`onEnd run. Job completed in ${this.elapsedTimePrettyFormat}`)\n }\n\n this.notifyEvent(event)\n }\n\n /**\n * Inner job event handler.\n */\n protected async onInnerJobEvent(event: JobEvent): Promise<void> {\n const { status } = event\n if (status === JobStatus.canceled) {\n this.canceledByAdmin = this.getCurrentInnerJob()?.canceledByAdmin ?? false\n return this.setStatus(status)\n }\n if (status === JobStatus.failed) {\n // report the errors of the failed inner job in the failed event (and in the errors) of this job\n Object.assign(this.errors, this.getCurrentInnerJob()?.errors ?? {})\n // when not stopping on inner job failures, this job is set as failed after all the inner jobs have run\n return this.stopOnInnerJobFailure ? this.setStatus(status) : undefined\n }\n if (status === JobStatus.running) {\n this.notifyEvent({\n type: JobEventType.progress,\n status: this.status,\n total: this.total,\n processed: this.processed,\n })\n return\n }\n this.logDebug(`Unknown inner job status: ${status}`)\n }\n\n /**\n * Called when the job just has been started.\n */\n protected async onStart(): Promise<void> {\n this.startTime = new Date()\n await this.setStatus(JobStatus.running)\n }\n\n /**\n * Called before beforeEnd only if the status will change to 'success'.\n * Default implementation stores the value returned by generateResult() via setResult().\n * It runs INSIDE the current db transaction.\n */\n protected async beforeSuccess(): Promise<void> {\n this.setResult(await this.generateResult())\n }\n\n /**\n * Computes the value beforeSuccess() will store as this job's result.\n * Default implementation returns whatever is already in `this.result`\n * (e.g. accumulated via earlier setResult() calls during execute()).\n */\n protected generateResult(): Promise<R | undefined> {\n return Promise.resolve(this.result)\n }\n\n /**\n * Updates this job's result. When both the current and incoming values are plain objects,\n * they are merged (Object.assign); otherwise the incoming value replaces the current one.\n * This keeps it safe for subclasses whose result is a primitive (e.g. a plain number) as\n * well as ones that build an object result incrementally across multiple setResult() calls.\n */\n protected setResult(result: R | undefined): void {\n if (result && typeof result === 'object' && this.result && typeof this.result === 'object') {\n Object.assign(this.result, result)\n } else {\n this.result = result\n }\n }\n\n /**\n * Called before onEnd. Useful for flushing resources used by the job before it terminates completely.\n * It runs INSIDE the current db transaction.\n */\n protected beforeEnd(): Promise<void> {\n this.logDebug('Cleanup')\n return Promise.resolve()\n }\n\n /**\n * Called when the job status changes to success, failed or canceled\n * (it runs OUTSIDE of the current db transaction)\n */\n protected onEnd(): Promise<void> {\n this.endTime = new Date()\n this.cancelNotifyProgress()\n return Promise.resolve()\n }\n\n protected addError(error: any, errorKey?: string): void {\n const key = errorKey ?? String(this.processed + 1)\n this.errors[key] = error\n }\n\n protected throwError(errorKey: string, params?: Dictionary<any>): void {\n // SystemError: getErrorInfo will report the translatable key instead of a generic error\n throw new SystemError(errorKey, params)\n }\n\n protected getErrorInfo(error: any): { key: string; params: Record<string, any> } {\n if (error instanceof SystemError) {\n return { key: `appErrors:${error.key}`, params: error.params }\n }\n return { key: 'appErrors:generic', params: { text: error.toString() } }\n }\n\n protected abstract createLogger(): Logger\n\n protected logDebug(...msgs: any[]): void {\n this.logger.debug(...msgs)\n }\n\n protected logInfo(...msgs: any[]): void {\n this.logger.info(...msgs)\n }\n\n protected logWarn(...msgs: any[]): void {\n this.logger.warn(...msgs)\n }\n\n protected logError(...msgs: any[]): void {\n this.logger.error(...msgs)\n }\n\n private get elapsedTimePrettyFormat(): string {\n const elapsedTime = this.calculateElapsedMillis()\n const elapsedMins = Math.floor(elapsedTime / 1000 / 60)\n const elapsedSeconds = String(Math.floor(elapsedTime / 1000) % 60).padStart(2, '0')\n const elapsedMillis = String(elapsedTime % 1000).padStart(3, '0')\n return `${elapsedMins}:${elapsedSeconds}.${elapsedMillis}`\n }\n\n private calculatePartialProgress(): number {\n if (this.isSucceeded()) return 100\n if (this.total > 0) return Math.floor((100 * this.processed) / this.total)\n return 0\n }\n\n private calculateProgressPercent(): number {\n const partialProgress = this.calculatePartialProgress()\n const currentInnerJob = this.getCurrentInnerJob()\n if (\n !currentInnerJob ||\n this.currentInnerJobIndex < 0 ||\n partialProgress === 100 ||\n this.processed > this.currentInnerJobIndex\n ) {\n return partialProgress\n }\n return partialProgress + Math.floor(currentInnerJob.calculateProgressPercent() / this.total)\n }\n\n private calculateElapsedMillis(): number {\n if (!this.startTime) return 0\n return (this.endTime ?? new Date()).getTime() - this.startTime.getTime()\n }\n\n /**\n * Notifies a progress event to the registered listener, throttled so that fast-changing\n * progress does not flood consumers (e.g. web socket clients).\n */\n private notifyProgress(): void {\n const run = (): void => {\n this.progressThrottleTimeoutId = undefined\n this.progressThrottleLastRunTime = Date.now()\n this.notifyEvent({\n type: JobEventType.progress,\n status: this.status,\n total: this.total,\n processed: this.processed,\n })\n }\n\n if (this.progressThrottleTimeoutId) return\n\n const elapsed = Date.now() - this.progressThrottleLastRunTime\n if (!this.progressThrottleLastRunTime || elapsed >= PROGRESS_NOTIFICATION_THROTTLE_MILLIS) {\n run()\n } else {\n this.progressThrottleTimeoutId = setTimeout(run, PROGRESS_NOTIFICATION_THROTTLE_MILLIS - elapsed)\n }\n }\n\n /**\n * Cancels any pending throttled progress notification and resets the throttle state.\n */\n private cancelNotifyProgress(): void {\n if (this.progressThrottleTimeoutId) {\n clearTimeout(this.progressThrottleTimeoutId)\n this.progressThrottleTimeoutId = undefined\n }\n this.progressThrottleLastRunTime = 0\n }\n\n /**\n * Notifies the event to the registered listener.\n * Listener errors are only logged: they would otherwise fail the job or, when thrown inside the progress\n * throttle timeout, crash the process.\n */\n private notifyEvent(event: JobEvent): void {\n const listener = this.eventListener\n if (!listener) return\n try {\n Promise.resolve(listener(event)).catch((error) => this.logListenerError(error))\n } catch (error) {\n this.logListenerError(error)\n }\n }\n\n private logListenerError(error: any): void {\n this.logError(`error in job event listener: ${error?.stack ?? error}`)\n }\n}\n"]}
|
|
@@ -11,6 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.RecordUpdater = void 0;
|
|
13
13
|
const error_1 = require("../error");
|
|
14
|
+
const nodeDef_1 = require("../nodeDef");
|
|
14
15
|
const survey_1 = require("../survey");
|
|
15
16
|
const utils_1 = require("../utils");
|
|
16
17
|
const validations_1 = require("../validation/validations");
|
|
@@ -43,7 +44,7 @@ const getDependentValidationNodePointers = (params) => {
|
|
|
43
44
|
return Array.from(uniquePointersByKey.values());
|
|
44
45
|
};
|
|
45
46
|
const _onRecordNodesCreateOrUpdate = (params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
46
|
-
const { user, survey, dateModified = utils_1.Dates.nowFormattedForStorage() } = params;
|
|
47
|
+
const { user, survey, categoryItemProvider, taxonProvider, areaOfInterestProvider, additionalNodesToValidate, dateModified = utils_1.Dates.nowFormattedForStorage(), } = params;
|
|
47
48
|
const updateResult = yield recordNodesUpdater_1.RecordNodesUpdater.updateNodesDependents(params);
|
|
48
49
|
const { nodes: updatedNodes, record: updatedRecord } = updateResult;
|
|
49
50
|
const dependentValidationNodePointers = getDependentValidationNodePointers({
|
|
@@ -55,10 +56,13 @@ const _onRecordNodesCreateOrUpdate = (params) => __awaiter(void 0, void 0, void
|
|
|
55
56
|
record: updatedRecord,
|
|
56
57
|
nodePointers: dependentValidationNodePointers,
|
|
57
58
|
});
|
|
58
|
-
const nodesToValidate = Object.assign(Object.assign({}, updatedNodes), dependentValidationNodes);
|
|
59
|
+
const nodesToValidate = Object.assign(Object.assign(Object.assign({}, updatedNodes), dependentValidationNodes), additionalNodesToValidate);
|
|
59
60
|
const validationUpdatedNodes = yield recordValidator_1.RecordValidator.validateNodes({
|
|
60
61
|
user,
|
|
61
62
|
survey,
|
|
63
|
+
categoryItemProvider,
|
|
64
|
+
taxonProvider,
|
|
65
|
+
areaOfInterestProvider,
|
|
62
66
|
record: updatedRecord,
|
|
63
67
|
nodes: nodesToValidate,
|
|
64
68
|
});
|
|
@@ -94,11 +98,49 @@ const updateAttributeValue = (params) => __awaiter(void 0, void 0, void 0, funct
|
|
|
94
98
|
const _record = records_1.Records.addNode(attributeUpdated, { sideEffect })(record);
|
|
95
99
|
return _onRecordNodesCreateOrUpdate(Object.assign(Object.assign({}, params), { record: _record, nodes: nodesUpdated }));
|
|
96
100
|
});
|
|
101
|
+
/**
|
|
102
|
+
* Returns the key and unique attributes of the siblings of the specified entities:
|
|
103
|
+
* they could have been duplicates of the ones being deleted, so they must be validated again.
|
|
104
|
+
*/
|
|
105
|
+
const getEntityKeyOrUniqueAttributes = (params) => {
|
|
106
|
+
const { survey, record, entity, uniqueAttributeDefs } = params;
|
|
107
|
+
return [
|
|
108
|
+
...records_1.Records.getEntityKeyNodes(survey, entity)(record),
|
|
109
|
+
...uniqueAttributeDefs.flatMap((uniqueDef) => records_1.Records.getChildren(entity, uniqueDef.uuid)(record)),
|
|
110
|
+
].filter(Boolean);
|
|
111
|
+
};
|
|
112
|
+
const getSiblingKeyOrUniqueAttributes = (params) => {
|
|
113
|
+
const { survey, record, nodeUuids } = params;
|
|
114
|
+
const nodeUuidsSet = new Set(nodeUuids);
|
|
115
|
+
const result = {};
|
|
116
|
+
for (const nodeUuid of nodeUuids) {
|
|
117
|
+
const node = records_1.Records.getNodeByUuid(nodeUuid)(record);
|
|
118
|
+
const parentNode = node ? records_1.Records.getParent(node)(record) : undefined;
|
|
119
|
+
const nodeDef = node ? survey_1.Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid }) : undefined;
|
|
120
|
+
if (!parentNode || !nodeDef || !nodeDef_1.NodeDefs.isEntity(nodeDef))
|
|
121
|
+
continue;
|
|
122
|
+
const uniqueAttributeDefs = survey_1.Surveys.getNodeDefChildren({ survey, nodeDef }).filter((childDef) => { var _a; return (_a = nodeDef_1.NodeDefs.getValidations(childDef)) === null || _a === void 0 ? void 0 : _a.unique; });
|
|
123
|
+
const siblings = records_1.Records.getChildren(parentNode, nodeDef.uuid)(record).filter((s) => !nodeUuidsSet.has(s.uuid));
|
|
124
|
+
for (const sibling of siblings) {
|
|
125
|
+
for (const attribute of getEntityKeyOrUniqueAttributes({
|
|
126
|
+
survey,
|
|
127
|
+
record,
|
|
128
|
+
entity: sibling,
|
|
129
|
+
uniqueAttributeDefs,
|
|
130
|
+
})) {
|
|
131
|
+
result[attribute.uuid] = attribute;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
136
|
+
};
|
|
97
137
|
const deleteNodes = (params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
98
|
-
const { nodeUuids, record: _record, sideEffect = false } = params;
|
|
138
|
+
const { survey, nodeUuids, record: _record, sideEffect = false } = params;
|
|
139
|
+
// found before removing the nodes: the deleted nodes are no longer reachable from their siblings afterwards
|
|
140
|
+
const siblingKeyOrUniqueAttributes = getSiblingKeyOrUniqueAttributes({ survey, record: _record, nodeUuids });
|
|
99
141
|
const updateResult = records_1.Records.removeNodes(nodeUuids, { sideEffect })(_record);
|
|
100
142
|
const { record, nodesDeleted } = updateResult;
|
|
101
|
-
const result = yield _onRecordNodesCreateOrUpdate(Object.assign(Object.assign({}, params), { record, nodes: nodesDeleted }));
|
|
143
|
+
const result = yield _onRecordNodesCreateOrUpdate(Object.assign(Object.assign({}, params), { record, nodes: nodesDeleted, additionalNodesToValidate: siblingKeyOrUniqueAttributes }));
|
|
102
144
|
result.nodesDeleted = nodesDeleted;
|
|
103
145
|
return result;
|
|
104
146
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recordUpdater.js","sourceRoot":"","sources":["../../src/record/recordUpdater.ts"],"names":[],"mappings":";;;;;;;;;;;;AACA,oCAAuD;AAEvD,sCAAiE;AACjE,oCAAyC;AACzC,2DAAuD;AACvD,iDAA6C;AAE7C,6DAA6E;AAE7E,uCAAmC;AACnC,uDAAmD;AAEnD,MAAM,yBAAyB,GAAG;IAChC,6BAAoB,CAAC,WAAW;IAChC,6BAAoB,CAAC,QAAQ;IAC7B,6BAAoB,CAAC,QAAQ;IAC7B,6BAAoB,CAAC,QAAQ;CAC9B,CAAA;AAED,MAAM,kCAAkC,GAAG,CAAC,MAI3C,EAAiB,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAAA;IACxC,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAuB,CAAA;IAC1D,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACxC,KAAK,MAAM,cAAc,IAAI,yBAAyB,EAAE,CAAC;YACvD,MAAM,YAAY,GAAG,iBAAO,CAAC,wBAAwB,CAAC,MAAM,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;YAC3F,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE,CAAC;gBACnC,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAA;gBACxC,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAA;gBACxC,MAAM,GAAG,GAAG,GAAG,WAAW,aAAX,WAAW,cAAX,WAAW,GAAI,EAAE,IAAI,WAAW,aAAX,WAAW,cAAX,WAAW,GAAI,EAAE,EAAE,CAAA;gBACvD,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;oBAClC,mBAAmB,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;gBACvC,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,MAAM,EAAE,CAAC,CAAA;AACjD,CAAC,CAAA;AAED,MAAM,4BAA4B,GAAG,CACnC,MAAkE,EACrC,EAAE;IAC/B,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,GAAG,aAAK,CAAC,sBAAsB,EAAE,EAAE,GAAG,MAAM,CAAA;IAE9E,MAAM,YAAY,GAAG,MAAM,uCAAkB,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAA;IAC3E,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,YAAY,CAAA;IAEnE,MAAM,+BAA+B,GAAG,kCAAkC,CAAC;QACzE,MAAM;QACN,MAAM,EAAE,aAAa;QACrB,KAAK,EAAE,YAAY;KACpB,CAAC,CAAA;IAEF,MAAM,wBAAwB,GAAG,2BAAY,CAAC,2BAA2B,CAAC;QACxE,MAAM,EAAE,aAAa;QACrB,YAAY,EAAE,+BAA+B;KAC9C,CAAC,CAAA;IAEF,MAAM,eAAe,mCAAQ,YAAY,GAAK,wBAAwB,CAAE,CAAA;IAExE,MAAM,sBAAsB,GAAG,MAAM,iCAAe,CAAC,aAAa,CAAC;QACjE,IAAI;QACJ,MAAM;QACN,MAAM,EAAE,aAAa;QACrB,KAAK,EAAE,eAAe;KACvB,CAAC,CAAA;IAEF,MAAM,UAAU,GAAG,yBAAW,CAAC,gBAAgB,CAAC,sBAAsB,CAAC,CAAC,yBAAW,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAA;IACjH,aAAa,CAAC,UAAU,GAAG,UAAU,CAAA;IACrC,aAAa,CAAC,YAAY,GAAG,YAAY,CAAA;IAEzC,YAAY,CAAC,UAAU,GAAG,UAAU,CAAA;IAEpC,OAAO,YAAY,CAAA;AACrB,CAAC,CAAA,CAAA;AAED,MAAM,iBAAiB,GAAG,CAAO,MAAwB,EAA+B,EAAE;IACxF,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,MAAM,uCAAkB,CAAC,cAAc,CAAC,MAAM,CAAC,CAAA;IAEtG,OAAO,4BAA4B,iCAAM,MAAM,KAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,IAAG,CAAA;AAChG,CAAC,CAAA,CAAA;AAED,MAAM,wBAAwB,GAAG,CAAO,MAAwB,EAA+B,EAAE;IAC/F,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,MAAM,uCAAkB,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAA;IAE7G,OAAO,4BAA4B,iCAAM,MAAM,KAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,IAAG,CAAA;AAChG,CAAC,CAAA,CAAA;AAED,MAAM,gBAAgB,GAAG,CACvB,MAEC,EAC4B,EAAE;IAC/B,MAAM,EAAE,MAAM,EAAE,sBAAsB,GAAG,IAAI,EAAE,GAAG,MAAM,CAAA;IAExD,OAAO,wBAAwB,iCAAM,MAAM,KAAE,OAAO,EAAE,gBAAO,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,sBAAsB,IAAG,CAAA;AACrH,CAAC,CAAA,CAAA;AAED,MAAM,oBAAoB,GAAG,CAC3B,MAGC,EAC4B,EAAE;IAC/B,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,aAAK,CAAC,sBAAsB,EAAE,EAAE,UAAU,GAAG,KAAK,EAAE,GAAG,MAAM,CAAA;IAClH,MAAM,SAAS,GAAG,iBAAO,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;IAC9D,IAAI,CAAC,SAAS;QAAE,MAAM,IAAI,mBAAW,CAAC,uBAAe,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAA;IAEnG,IAAI,gBAAgB,GAAG,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,mBAAM,SAAS,CAAE,CAAA;IAChE,gBAAgB,CAAC,KAAK,GAAG,KAAK,CAAA;IAC9B,gBAAgB,CAAC,YAAY,GAAG,YAAY,CAAA;IAC5C,iCAAiC;IACjC,gBAAgB,GAAG,eAAO,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,qBAAqB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAA;IAEnH,MAAM,YAAY,GAAG,EAAE,CAAC,aAAa,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAE1D,MAAM,OAAO,GAAG,iBAAO,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC,CAAA;IAEzE,OAAO,4BAA4B,iCAAM,MAAM,KAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,IAAG,CAAA;AAC1F,CAAC,CAAA,CAAA;AAED,MAAM,WAAW,GAAG,CAClB,MAEC,EAC4B,EAAE;IAC/B,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,KAAK,EAAE,GAAG,MAAM,CAAA;IAEjE,MAAM,YAAY,GAAG,iBAAO,CAAC,WAAW,CAAC,SAAS,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC,CAAA;IAC5E,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAA;IAE7C,MAAM,MAAM,GAAG,MAAM,4BAA4B,iCAAM,MAAM,KAAE,MAAM,EAAE,KAAK,EAAE,YAAY,IAAG,CAAA;IAC7F,MAAM,CAAC,YAAY,GAAG,YAAY,CAAA;IAClC,OAAO,MAAM,CAAA;AACf,CAAC,CAAA,CAAA;AAED,MAAM,UAAU,GAAG,CAAO,MAAgD,EAA+B,EAAE;IACzG,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAA;IAC3B,OAAO,WAAW,iCAAM,MAAM,KAAE,SAAS,EAAE,CAAC,QAAQ,CAAC,IAAG,CAAA;AAC1D,CAAC,CAAA,CAAA;AAEY,QAAA,aAAa,GAAG;IAC3B,+HAA+H;IAC/H,iBAAiB;IACjB,gIAAgI;IAChI,wBAAwB;IACxB,gBAAgB;IAChB,oBAAoB;IACpB,kIAAkI;IAClI,UAAU;IACV,qIAAqI;IACrI,WAAW;CACZ,CAAA","sourcesContent":["import { Dictionary } from '../common'\nimport { SystemError, SystemErrorKeys } from '../error'\nimport { ArenaRecordNode, NodePointer, NodesMap } from '../node'\nimport { Survey, SurveyDependencyType, Surveys } from '../survey'\nimport { Dates, Objects } from '../utils'\nimport { Validations } from '../validation/validations'\nimport { NodePointers } from './nodePointers'\nimport { ArenaRecord } from './record'\nimport { RecordNodesUpdater, RecordUpdateResult } from './recordNodesUpdater'\nimport { NodeCreateParams, NodesUpdateParams } from './recordNodesUpdater/recordNodesCreator'\nimport { Records } from './records'\nimport { RecordValidator } from './recordValidator'\n\nconst validationDependencyTypes = [\n SurveyDependencyType.validations,\n SurveyDependencyType.required,\n SurveyDependencyType.minCount,\n SurveyDependencyType.maxCount,\n]\n\nconst getDependentValidationNodePointers = (params: {\n survey: Survey\n record: ArenaRecord\n nodes: NodesMap\n}): NodePointer[] => {\n const { survey, record, nodes } = params\n const uniquePointersByKey = new Map<string, NodePointer>()\n for (const node of Object.values(nodes)) {\n for (const dependencyType of validationDependencyTypes) {\n const nodePointers = Records.getDependentNodePointers(survey, node, dependencyType)(record)\n for (const pointer of nodePointers) {\n const nodeCtxUuid = pointer.nodeCtx.uuid\n const nodeDefUuid = pointer.nodeDef.uuid\n const key = `${nodeCtxUuid ?? ''}:${nodeDefUuid ?? ''}`\n if (!uniquePointersByKey.has(key)) {\n uniquePointersByKey.set(key, pointer)\n }\n }\n }\n }\n return Array.from(uniquePointersByKey.values())\n}\n\nconst _onRecordNodesCreateOrUpdate = async (\n params: NodesUpdateParams & { nodes: Dictionary<ArenaRecordNode> }\n): Promise<RecordUpdateResult> => {\n const { user, survey, dateModified = Dates.nowFormattedForStorage() } = params\n\n const updateResult = await RecordNodesUpdater.updateNodesDependents(params)\n const { nodes: updatedNodes, record: updatedRecord } = updateResult\n\n const dependentValidationNodePointers = getDependentValidationNodePointers({\n survey,\n record: updatedRecord,\n nodes: updatedNodes,\n })\n\n const dependentValidationNodes = NodePointers.getNodesMapFromNodePointers({\n record: updatedRecord,\n nodePointers: dependentValidationNodePointers,\n })\n\n const nodesToValidate = { ...updatedNodes, ...dependentValidationNodes }\n\n const validationUpdatedNodes = await RecordValidator.validateNodes({\n user,\n survey,\n record: updatedRecord,\n nodes: nodesToValidate,\n })\n\n const validation = Validations.mergeValidations(validationUpdatedNodes)(Validations.getValidation(updatedRecord))\n updatedRecord.validation = validation\n updatedRecord.dateModified = dateModified\n\n updateResult.validation = validation\n\n return updateResult\n}\n\nconst createDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { record: updatedRecord, nodes: createdNodes } = await RecordNodesUpdater.addDescendants(params)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: updatedRecord, nodes: createdNodes })\n}\n\nconst createNodeAndDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { record: updatedRecord, nodes: createdNodes } = await RecordNodesUpdater.addNodeAndDescendants(params)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: updatedRecord, nodes: createdNodes })\n}\n\nconst createRootEntity = async (\n params: NodesUpdateParams & {\n createMultipleEntities?: boolean\n }\n): Promise<RecordUpdateResult> => {\n const { survey, createMultipleEntities = true } = params\n\n return createNodeAndDescendants({ ...params, nodeDef: Surveys.getNodeDefRoot({ survey }), createMultipleEntities })\n}\n\nconst updateAttributeValue = async (\n params: NodesUpdateParams & {\n attributeUuid: string\n value: any\n }\n): Promise<RecordUpdateResult> => {\n const { attributeUuid, record, value, dateModified = Dates.nowFormattedForStorage(), sideEffect = false } = params\n const attribute = Records.getNodeByUuid(attributeUuid)(record)\n if (!attribute) throw new SystemError(SystemErrorKeys.record.nodeNotFound, { uuid: attributeUuid })\n\n let attributeUpdated = sideEffect ? attribute : { ...attribute }\n attributeUpdated.value = value\n attributeUpdated.dateModified = dateModified\n // reset defaultValueApplied flag\n attributeUpdated = Objects.dissocPath({ obj: attributeUpdated, path: ['meta', 'defaultValueApplied'], sideEffect })\n\n const nodesUpdated = { [attributeUuid]: attributeUpdated }\n\n const _record = Records.addNode(attributeUpdated, { sideEffect })(record)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: _record, nodes: nodesUpdated })\n}\n\nconst deleteNodes = async (\n params: NodesUpdateParams & {\n nodeUuids: string[]\n }\n): Promise<RecordUpdateResult> => {\n const { nodeUuids, record: _record, sideEffect = false } = params\n\n const updateResult = Records.removeNodes(nodeUuids, { sideEffect })(_record)\n const { record, nodesDeleted } = updateResult\n\n const result = await _onRecordNodesCreateOrUpdate({ ...params, record, nodes: nodesDeleted })\n result.nodesDeleted = nodesDeleted\n return result\n}\n\nconst deleteNode = async (params: NodesUpdateParams & { nodeUuid: string }): Promise<RecordUpdateResult> => {\n const { nodeUuid } = params\n return deleteNodes({ ...params, nodeUuids: [nodeUuid] })\n}\n\nexport const RecordUpdater = {\n /** Creates the descendants of a node and updates dependents (applicability, default values, calculations...) and validation */\n createDescendants,\n /** Creates a node and its descendants and updates dependents (applicability, default values, calculations...) and validation */\n createNodeAndDescendants,\n createRootEntity,\n updateAttributeValue,\n /** Deletes the node and its descendants and updates dependents (applicability, default values, calculations...) and validation */\n deleteNode,\n /** Deletes the nodes and their descendants and updates dependents (applicability, default values, calculations...) and validation */\n deleteNodes,\n}\n"]}
|
|
1
|
+
{"version":3,"file":"recordUpdater.js","sourceRoot":"","sources":["../../src/record/recordUpdater.ts"],"names":[],"mappings":"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","sourcesContent":["import { Dictionary } from '../common'\nimport { SystemError, SystemErrorKeys } from '../error'\nimport { ArenaRecordNode, NodePointer, NodesMap } from '../node'\nimport { NodeDef, NodeDefs } from '../nodeDef'\nimport { Survey, SurveyDependencyType, Surveys } from '../survey'\nimport { Dates, Objects } from '../utils'\nimport { Validations } from '../validation/validations'\nimport { NodePointers } from './nodePointers'\nimport { ArenaRecord } from './record'\nimport { RecordNodesUpdater, RecordUpdateResult } from './recordNodesUpdater'\nimport { NodeCreateParams, NodesUpdateParams } from './recordNodesUpdater/recordNodesCreator'\nimport { Records } from './records'\nimport { RecordValidator } from './recordValidator'\n\nconst validationDependencyTypes = [\n SurveyDependencyType.validations,\n SurveyDependencyType.required,\n SurveyDependencyType.minCount,\n SurveyDependencyType.maxCount,\n]\n\nconst getDependentValidationNodePointers = (params: {\n survey: Survey\n record: ArenaRecord\n nodes: NodesMap\n}): NodePointer[] => {\n const { survey, record, nodes } = params\n const uniquePointersByKey = new Map<string, NodePointer>()\n for (const node of Object.values(nodes)) {\n for (const dependencyType of validationDependencyTypes) {\n const nodePointers = Records.getDependentNodePointers(survey, node, dependencyType)(record)\n for (const pointer of nodePointers) {\n const nodeCtxUuid = pointer.nodeCtx.uuid\n const nodeDefUuid = pointer.nodeDef.uuid\n const key = `${nodeCtxUuid ?? ''}:${nodeDefUuid ?? ''}`\n if (!uniquePointersByKey.has(key)) {\n uniquePointersByKey.set(key, pointer)\n }\n }\n }\n }\n return Array.from(uniquePointersByKey.values())\n}\n\nconst _onRecordNodesCreateOrUpdate = async (\n params: NodesUpdateParams & { nodes: Dictionary<ArenaRecordNode>; additionalNodesToValidate?: NodesMap }\n): Promise<RecordUpdateResult> => {\n const {\n user,\n survey,\n categoryItemProvider,\n taxonProvider,\n areaOfInterestProvider,\n additionalNodesToValidate,\n dateModified = Dates.nowFormattedForStorage(),\n } = params\n\n const updateResult = await RecordNodesUpdater.updateNodesDependents(params)\n const { nodes: updatedNodes, record: updatedRecord } = updateResult\n\n const dependentValidationNodePointers = getDependentValidationNodePointers({\n survey,\n record: updatedRecord,\n nodes: updatedNodes,\n })\n\n const dependentValidationNodes = NodePointers.getNodesMapFromNodePointers({\n record: updatedRecord,\n nodePointers: dependentValidationNodePointers,\n })\n\n const nodesToValidate = { ...updatedNodes, ...dependentValidationNodes, ...additionalNodesToValidate }\n\n const validationUpdatedNodes = await RecordValidator.validateNodes({\n user,\n survey,\n categoryItemProvider,\n taxonProvider,\n areaOfInterestProvider,\n record: updatedRecord,\n nodes: nodesToValidate,\n })\n\n const validation = Validations.mergeValidations(validationUpdatedNodes)(Validations.getValidation(updatedRecord))\n updatedRecord.validation = validation\n updatedRecord.dateModified = dateModified\n\n updateResult.validation = validation\n\n return updateResult\n}\n\nconst createDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { record: updatedRecord, nodes: createdNodes } = await RecordNodesUpdater.addDescendants(params)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: updatedRecord, nodes: createdNodes })\n}\n\nconst createNodeAndDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { record: updatedRecord, nodes: createdNodes } = await RecordNodesUpdater.addNodeAndDescendants(params)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: updatedRecord, nodes: createdNodes })\n}\n\nconst createRootEntity = async (\n params: NodesUpdateParams & {\n createMultipleEntities?: boolean\n }\n): Promise<RecordUpdateResult> => {\n const { survey, createMultipleEntities = true } = params\n\n return createNodeAndDescendants({ ...params, nodeDef: Surveys.getNodeDefRoot({ survey }), createMultipleEntities })\n}\n\nconst updateAttributeValue = async (\n params: NodesUpdateParams & {\n attributeUuid: string\n value: any\n }\n): Promise<RecordUpdateResult> => {\n const { attributeUuid, record, value, dateModified = Dates.nowFormattedForStorage(), sideEffect = false } = params\n const attribute = Records.getNodeByUuid(attributeUuid)(record)\n if (!attribute) throw new SystemError(SystemErrorKeys.record.nodeNotFound, { uuid: attributeUuid })\n\n let attributeUpdated = sideEffect ? attribute : { ...attribute }\n attributeUpdated.value = value\n attributeUpdated.dateModified = dateModified\n // reset defaultValueApplied flag\n attributeUpdated = Objects.dissocPath({ obj: attributeUpdated, path: ['meta', 'defaultValueApplied'], sideEffect })\n\n const nodesUpdated = { [attributeUuid]: attributeUpdated }\n\n const _record = Records.addNode(attributeUpdated, { sideEffect })(record)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: _record, nodes: nodesUpdated })\n}\n\n/**\n * Returns the key and unique attributes of the siblings of the specified entities:\n * they could have been duplicates of the ones being deleted, so they must be validated again.\n */\nconst getEntityKeyOrUniqueAttributes = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n uniqueAttributeDefs: NodeDef<any>[]\n}): ArenaRecordNode[] => {\n const { survey, record, entity, uniqueAttributeDefs } = params\n return [\n ...Records.getEntityKeyNodes(survey, entity)(record),\n ...uniqueAttributeDefs.flatMap((uniqueDef) => Records.getChildren(entity, uniqueDef.uuid)(record)),\n ].filter(Boolean)\n}\n\nconst getSiblingKeyOrUniqueAttributes = (params: {\n survey: Survey\n record: ArenaRecord\n nodeUuids: string[]\n}): NodesMap => {\n const { survey, record, nodeUuids } = params\n const nodeUuidsSet = new Set(nodeUuids)\n const result: NodesMap = {}\n for (const nodeUuid of nodeUuids) {\n const node = Records.getNodeByUuid(nodeUuid)(record)\n const parentNode = node ? Records.getParent(node)(record) : undefined\n const nodeDef = node ? Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid }) : undefined\n if (!parentNode || !nodeDef || !NodeDefs.isEntity(nodeDef)) continue\n\n const uniqueAttributeDefs = Surveys.getNodeDefChildren({ survey, nodeDef }).filter(\n (childDef) => NodeDefs.getValidations(childDef)?.unique\n )\n const siblings = Records.getChildren(parentNode, nodeDef.uuid)(record).filter((s) => !nodeUuidsSet.has(s.uuid))\n for (const sibling of siblings) {\n for (const attribute of getEntityKeyOrUniqueAttributes({\n survey,\n record,\n entity: sibling,\n uniqueAttributeDefs,\n })) {\n result[attribute.uuid] = attribute\n }\n }\n }\n return result\n}\n\nconst deleteNodes = async (\n params: NodesUpdateParams & {\n nodeUuids: string[]\n }\n): Promise<RecordUpdateResult> => {\n const { survey, nodeUuids, record: _record, sideEffect = false } = params\n\n // found before removing the nodes: the deleted nodes are no longer reachable from their siblings afterwards\n const siblingKeyOrUniqueAttributes = getSiblingKeyOrUniqueAttributes({ survey, record: _record, nodeUuids })\n\n const updateResult = Records.removeNodes(nodeUuids, { sideEffect })(_record)\n const { record, nodesDeleted } = updateResult\n\n const result = await _onRecordNodesCreateOrUpdate({\n ...params,\n record,\n nodes: nodesDeleted,\n additionalNodesToValidate: siblingKeyOrUniqueAttributes,\n })\n result.nodesDeleted = nodesDeleted\n return result\n}\n\nconst deleteNode = async (params: NodesUpdateParams & { nodeUuid: string }): Promise<RecordUpdateResult> => {\n const { nodeUuid } = params\n return deleteNodes({ ...params, nodeUuids: [nodeUuid] })\n}\n\nexport const RecordUpdater = {\n /** Creates the descendants of a node and updates dependents (applicability, default values, calculations...) and validation */\n createDescendants,\n /** Creates a node and its descendants and updates dependents (applicability, default values, calculations...) and validation */\n createNodeAndDescendants,\n createRootEntity,\n updateAttributeValue,\n /** Deletes the node and its descendants and updates dependents (applicability, default values, calculations...) and validation */\n deleteNode,\n /** Deletes the nodes and their descendants and updates dependents (applicability, default values, calculations...) and validation */\n deleteNodes,\n}\n"]}
|
|
@@ -44,7 +44,8 @@ const _isEntityDuplicate = (params) => {
|
|
|
44
44
|
return false;
|
|
45
45
|
}
|
|
46
46
|
const keyValuesByDefUuid = _getKeyValuesByDefUuid({ survey, record, entity, keyDefs, cache });
|
|
47
|
-
|
|
47
|
+
// entities with all key values empty are never duplicates (they are reported as missing values)
|
|
48
|
+
return (!Object.values(keyValuesByDefUuid).every(utils_1.Objects.isEmpty) &&
|
|
48
49
|
siblingEntities.some((sibilingEntity) => {
|
|
49
50
|
const siblingKeyValuesByDefUuid = _getKeyValuesByDefUuid({
|
|
50
51
|
survey,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attributeKeyValidator.js","sourceRoot":"","sources":["../../../src/record/recordValidator/attributeKeyValidator.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"attributeKeyValidator.js","sourceRoot":"","sources":["../../../src/record/recordValidator/attributeKeyValidator.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecord } from '../record'\nimport { Records } from '../records'\nimport { NodeDef, NodeDefs, NodeDefProps, NodeDefType } from '../../nodeDef'\nimport { ArenaRecordNode, Nodes, NodeValues } from '../../node'\nimport { ValidationResult, ValidationResultFactory, ValidationSeverity } from '../../validation'\nimport { Survey, Surveys } from '../../survey'\n\nimport { Dictionary } from '../../common'\nimport { Objects } from '../../utils'\n\n/**\n * Cache of entity key values (and key defs by entity def) used during a single validation run (the record doesn't change):\n * without it, validating the keys of S sibling entities would compute the key values of every sibling S times.\n */\nexport type EntityKeysCache = {\n keyDefsByEntityDefUuid: Map<string, NodeDef<NodeDefType, NodeDefProps>[]>\n keyValuesByEntityUuid: Map<string, Dictionary<any>>\n}\n\nexport const createEntityKeysCache = (): EntityKeysCache => ({\n keyDefsByEntityDefUuid: new Map(),\n keyValuesByEntityUuid: new Map(),\n})\n\nconst _getKeyDefs = (params: {\n survey: Survey\n entityDef: NodeDef<NodeDefType, NodeDefProps>\n cache?: EntityKeysCache\n}): NodeDef<NodeDefType, NodeDefProps>[] => {\n const { survey, entityDef, cache } = params\n let keyDefs = cache?.keyDefsByEntityDefUuid.get(entityDef.uuid)\n if (!keyDefs) {\n keyDefs = Surveys.getNodeDefKeys({ survey, nodeDef: entityDef })\n cache?.keyDefsByEntityDefUuid.set(entityDef.uuid, keyDefs)\n }\n return keyDefs\n}\n\nconst _getKeyValuesByDefUuid = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n keyDefs: NodeDef<NodeDefType, NodeDefProps>[]\n cache?: EntityKeysCache\n}): Dictionary<any> => {\n const { survey, record, entity, keyDefs, cache } = params\n let keyValues = cache?.keyValuesByEntityUuid.get(entity.uuid)\n if (!keyValues) {\n keyValues = Records.getEntityKeyValuesByDefUuid(survey, entity, undefined, keyDefs)(record)\n cache?.keyValuesByEntityUuid.set(entity.uuid, keyValues)\n }\n return keyValues\n}\n\nconst _isEntityDuplicate = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n entityKeysCache?: EntityKeysCache\n}): boolean => {\n const { survey, entity, record, entityKeysCache: cache } = params\n // 1. get sibling entities\n const nodeParent = Records.getParent(entity)(record)\n if (!nodeParent) return false\n\n const siblingEntities = Records.getChildren(\n nodeParent,\n entity.nodeDefUuid\n )(record).filter((node) => !Nodes.areEqual(entity, node))\n\n // 2. get key values\n const entityDef = Surveys.getNodeDefByUuid({ survey, uuid: entity.nodeDefUuid })\n const keyDefs = _getKeyDefs({ survey, entityDef, cache })\n\n if (Objects.isEmpty(siblingEntities) || Objects.isEmpty(keyDefs)) {\n return false\n }\n const keyValuesByDefUuid = _getKeyValuesByDefUuid({ survey, record, entity, keyDefs, cache })\n // entities with all key values empty are never duplicates (they are reported as missing values)\n return (\n !Object.values(keyValuesByDefUuid).every(Objects.isEmpty) &&\n siblingEntities.some((sibilingEntity) => {\n const siblingKeyValuesByDefUuid = _getKeyValuesByDefUuid({\n survey,\n record,\n entity: sibilingEntity,\n keyDefs,\n cache,\n })\n return keyDefs.every((keyDef) => {\n const keyValue = keyValuesByDefUuid[keyDef.uuid]\n const siblingKeyValue = siblingKeyValuesByDefUuid[keyDef.uuid]\n return NodeValues.isValueEqual({\n survey,\n nodeDef: keyDef,\n value: keyValue,\n valueSearch: siblingKeyValue,\n })\n })\n })\n )\n}\n\nconst isNodeDefToBeValidated = (params: { survey: Survey; nodeDef: NodeDef<NodeDefType, NodeDefProps> }): boolean => {\n const { survey, nodeDef } = params\n if (!NodeDefs.isKey(nodeDef)) return false\n\n const nodeDefParent = Surveys.getNodeDefParent({ survey, nodeDef })\n // root entity key attributes will be validated by another validator\n return !!nodeDefParent && !NodeDefs.isRoot(nodeDefParent)\n}\n\nconst validateAttributeKey =\n (params: {\n survey: Survey\n record: ArenaRecord\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n entityKeysCache?: EntityKeysCache\n }) =>\n (_propName: string, node: ArenaRecordNode): Promise<ValidationResult> => {\n const { survey, nodeDef, record, entityKeysCache } = params\n\n if (isNodeDefToBeValidated({ survey, nodeDef })) {\n const entity = Records.getParent(node)(record)\n if (entity && _isEntityDuplicate({ survey, record, entity, entityKeysCache })) {\n return Promise.resolve(\n ValidationResultFactory.createInstance({\n valid: false,\n severity: ValidationSeverity.error,\n key: 'record.entity.keyDuplicate',\n })\n )\n }\n }\n\n return Promise.resolve(ValidationResultFactory.createInstance())\n }\n\nexport const AttributeKeyValidator = {\n validateAttributeKey,\n}\n"]}
|