@jumoo/processing 17.4.2 → 17.4.3-prerelease.10

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.
Files changed (32) hide show
  1. package/dist/api/index.d.ts +1 -1
  2. package/dist/api/index.js +1 -1
  3. package/dist/api/sdk.gen.d.ts +4 -1
  4. package/dist/api/sdk.gen.js +25 -0
  5. package/dist/api/types.gen.d.ts +197 -1
  6. package/dist/api/types.gen.js +17 -0
  7. package/dist/lang/files/en.d.ts +26 -0
  8. package/dist/lang/files/en.js +26 -0
  9. package/dist/pipeline/components/background.element.js +24 -23
  10. package/dist/pipeline/components/pipeline-list.element.js +29 -28
  11. package/dist/pipeline/components/pipeline-table.element.js +23 -23
  12. package/dist/pipeline/components/process-error.element.js +39 -39
  13. package/dist/pipeline/components/progress.element.js +122 -122
  14. package/dist/pipeline/components/progress.step.element.js +5 -5
  15. package/dist/pipeline/index.d.ts +1 -0
  16. package/dist/pipeline/index.js +1 -0
  17. package/dist/pipeline/modal/errors/error-modal.element.js +87 -87
  18. package/dist/pipeline/modal/pipeline-modal.element.js +34 -34
  19. package/dist/pipeline/pipeline-conflict.d.ts +31 -0
  20. package/dist/pipeline/pipeline-conflict.js +42 -0
  21. package/dist/pipeline/pipeline.context.d.ts +6 -1
  22. package/dist/pipeline/pipeline.context.js +43 -8
  23. package/dist/pipeline/pipeline.element.d.ts +11 -0
  24. package/dist/pipeline/pipeline.element.js +157 -27
  25. package/dist/pipeline/pipeline.repository.d.ts +26 -0
  26. package/dist/pipeline/pipeline.repository.js +22 -0
  27. package/dist/pipeline/pipeline.source.d.ts +19 -1
  28. package/dist/pipeline/pipeline.source.js +23 -4
  29. package/dist/pipeline/workspace/views/default-pipeline-view.js +18 -18
  30. package/dist/queue/components/queue-list.element.js +17 -17
  31. package/dist/queue/components/queue-table.element.js +15 -15
  32. package/package.json +58 -51
@@ -38,41 +38,41 @@ let uSyncProcessingModal = class uSyncProcessingModal extends UmbModalBaseElemen
38
38
  this.closeLabel = this.data?.closeLabel ?? 'Close';
39
39
  }
40
40
  render() {
41
- return html ` ${__classPrivateFieldGet(this, _uSyncProcessingModal_instances, "m", _uSyncProcessingModal_getDialogStyles).call(this)}
42
- <umb-body-layout id="jumoo-processing-modal">
43
- <div slot="header" id="header">
44
- <h3>${this.headline}</h3>
45
- ${this.data?.description}
46
- </div>
47
- ${this.renderProcess()}
48
- <div slot="actions">${this.renderActions()}</div>
41
+ return html ` ${__classPrivateFieldGet(this, _uSyncProcessingModal_instances, "m", _uSyncProcessingModal_getDialogStyles).call(this)}
42
+ <umb-body-layout id="jumoo-processing-modal">
43
+ <div slot="header" id="header">
44
+ <h3>${this.headline}</h3>
45
+ ${this.data?.description}
46
+ </div>
47
+ ${this.renderProcess()}
48
+ <div slot="actions">${this.renderActions()}</div>
49
49
  </umb-body-layout>`;
50
50
  }
51
51
  renderProcess() {
52
52
  if (!this.data)
53
53
  return;
54
- return html `<jumoo-pipeline
55
- .processor=${this.data.processor}
56
- .strategy=${this.data.strategy}
57
- .setup=${this.data.options}
58
- .debug=${this.data.debug ?? false}
59
- @pipeline-status=${__classPrivateFieldGet(this, _uSyncProcessingModal_instances, "m", _uSyncProcessingModal_onStatusChange)}
60
- @pipeline-valid=${__classPrivateFieldGet(this, _uSyncProcessingModal_instances, "m", _uSyncProcessingModal_onSetValid)}
61
- @pipeline-complete=${__classPrivateFieldGet(this, _uSyncProcessingModal_instances, "m", _uSyncProcessingModal_onComplete)}
54
+ return html `<jumoo-pipeline
55
+ .processor=${this.data.processor}
56
+ .strategy=${this.data.strategy}
57
+ .setup=${this.data.options}
58
+ .debug=${this.data.debug ?? false}
59
+ @pipeline-status=${__classPrivateFieldGet(this, _uSyncProcessingModal_instances, "m", _uSyncProcessingModal_onStatusChange)}
60
+ @pipeline-valid=${__classPrivateFieldGet(this, _uSyncProcessingModal_instances, "m", _uSyncProcessingModal_onSetValid)}
61
+ @pipeline-complete=${__classPrivateFieldGet(this, _uSyncProcessingModal_instances, "m", _uSyncProcessingModal_onComplete)}
62
62
  @pipeline-modal-update=${__classPrivateFieldGet(this, _uSyncProcessingModal_instances, "m", _uSyncProcessingModal_onUpdateModal)}></jumoo-pipeline>`;
63
63
  }
64
64
  renderActions() {
65
65
  const actionButton = this._status === PipelineStatus.WAITING && this._isValid && !this._isLastAction
66
- ? html `<uui-button
67
- @click=${__classPrivateFieldGet(this, _uSyncProcessingModal_instances, "m", _uSyncProcessingModal_onProcess)}
68
- color="positive"
69
- look="primary"
66
+ ? html `<uui-button
67
+ @click=${__classPrivateFieldGet(this, _uSyncProcessingModal_instances, "m", _uSyncProcessingModal_onProcess)}
68
+ color="positive"
69
+ look="primary"
70
70
  .label=${this.progressLabel}></uui-button>`
71
71
  : nothing;
72
- const closeButton = html `<uui-button
73
- @click=${__classPrivateFieldGet(this, _uSyncProcessingModal_instances, "m", _uSyncProcessingModal_onClose)}
74
- color="default"
75
- look="default"
72
+ const closeButton = html `<uui-button
73
+ @click=${__classPrivateFieldGet(this, _uSyncProcessingModal_instances, "m", _uSyncProcessingModal_onClose)}
74
+ color="default"
75
+ look="default"
76
76
  label=${this.closeLabel}></uui-button>`;
77
77
  return html `${closeButton} ${actionButton}`;
78
78
  }
@@ -110,18 +110,18 @@ _uSyncProcessingModal_onProcess = function _uSyncProcessingModal_onProcess() {
110
110
  };
111
111
  _uSyncProcessingModal_getDialogStyles = function _uSyncProcessingModal_getDialogStyles() {
112
112
  const width = (this.data?.width ?? this.modalType === 'dialog') ? 600 : 0;
113
- return html `<style>
114
- #jumoo-processing-modal {
115
- min-width: ${width > 0 ? width + 'px' : 'auto'};
116
- min-height: ${this.data?.height ? this.data.height + 'px' : 'auto'};
117
- }
113
+ return html `<style>
114
+ #jumoo-processing-modal {
115
+ min-width: ${width > 0 ? width + 'px' : 'auto'};
116
+ min-height: ${this.data?.height ? this.data.height + 'px' : 'auto'};
117
+ }
118
118
  </style>`;
119
119
  };
120
- uSyncProcessingModal.styles = css `
121
- #header h3 {
122
- display: block;
123
- margin: 0;
124
- }
120
+ uSyncProcessingModal.styles = css `
121
+ #header h3 {
122
+ display: block;
123
+ margin: 0;
124
+ }
125
125
  `;
126
126
  __decorate([
127
127
  state()
@@ -0,0 +1,31 @@
1
+ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
2
+ import type { PipelineRunStatus } from '../api';
3
+ /**
4
+ * @description a pipeline that couldn't be created or resumed because another one holds its
5
+ * concurrency key (the server's 409 `PipelineConflictProblemDetails`).
6
+ */
7
+ export interface PipelineConflict {
8
+ /** the pipeline holding the key */
9
+ runningPipelineId: string;
10
+ /** the key that is held */
11
+ concurrencyKey: string;
12
+ /** the server's message */
13
+ detail?: string | null;
14
+ /** the holder's status, when the current user can see it */
15
+ holder?: PipelineRunStatus;
16
+ }
17
+ /**
18
+ * @description reads the 409 "already running" body off an error, whether it is the raw
19
+ * problem details the generated client throws or the `UmbApiError` tryExecute wraps it in.
20
+ * @param error the error from a request.
21
+ * @returns the conflict, or undefined for any other error.
22
+ */
23
+ export declare function getPipelineConflict(error: unknown): PipelineConflict | undefined;
24
+ /**
25
+ * @description tryExecute, except a 409 "already running" comes back as the error without a
26
+ * notification, so the caller can show it inline. Every other error notifies as normal.
27
+ * @param host the host the request (and any notification) belongs to.
28
+ * @param promise the request.
29
+ * @returns the response, as tryExecute returns it.
30
+ */
31
+ export declare function tryExecuteAllowingConflict<T>(host: UmbControllerHost, promise: Promise<T>): Promise<import("@umbraco-cms/backoffice/resources").UmbApiResponse<T>>;
@@ -0,0 +1,42 @@
1
+ import { tryExecute, UmbApiError } from '@umbraco-cms/backoffice/resources';
2
+ /**
3
+ * @description reads the 409 "already running" body off an error, whether it is the raw
4
+ * problem details the generated client throws or the `UmbApiError` tryExecute wraps it in.
5
+ * @param error the error from a request.
6
+ * @returns the conflict, or undefined for any other error.
7
+ */
8
+ export function getPipelineConflict(error) {
9
+ const body = UmbApiError.isUmbApiError(error) ? error.problemDetails : error;
10
+ if (!body || typeof body !== 'object')
11
+ return undefined;
12
+ const problem = body;
13
+ if (problem.status !== 409 || typeof problem.runningPipelineId !== 'string')
14
+ return undefined;
15
+ return {
16
+ runningPipelineId: problem.runningPipelineId,
17
+ concurrencyKey: typeof problem.concurrencyKey === 'string' ? problem.concurrencyKey : '',
18
+ detail: typeof problem.detail === 'string' ? problem.detail : undefined,
19
+ };
20
+ }
21
+ /**
22
+ * @description tryExecute, except a 409 "already running" comes back as the error without a
23
+ * notification, so the caller can show it inline. Every other error notifies as normal.
24
+ * @param host the host the request (and any notification) belongs to.
25
+ * @param promise the request.
26
+ * @returns the response, as tryExecute returns it.
27
+ */
28
+ export async function tryExecuteAllowingConflict(host, promise) {
29
+ let conflict;
30
+ const response = await tryExecute(host, promise.catch((error) => {
31
+ if (!getPipelineConflict(error))
32
+ throw error;
33
+ conflict = error;
34
+ return undefined;
35
+ }));
36
+ if (!conflict)
37
+ return response;
38
+ const problem = conflict;
39
+ return {
40
+ error: new UmbApiError(problem.detail ?? problem.title ?? 'Conflict', 409, null, problem),
41
+ };
42
+ }
@@ -1,7 +1,7 @@
1
1
  import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
2
2
  import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
3
3
  import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
4
- import { PipelineState, ProcessingMessage, ProcessingOptions, ProcessingResults } from '@jumoo/processing';
4
+ import { PipelineState, ProcessingMessage, ProcessingOptions, ProcessingResults, PipelineConflict } from '@jumoo/processing';
5
5
  /**
6
6
  * @description context for keeping pipeline status and progress as we do things.
7
7
  */
@@ -13,6 +13,11 @@ export declare class PipelineContext extends UmbControllerBase {
13
13
  readonly results: import("rxjs").Observable<ProcessingResults | undefined>;
14
14
  readonly message: import("rxjs").Observable<ProcessingMessage | undefined>;
15
15
  readonly backgroundState: import("rxjs").Observable<PipelineState | undefined>;
16
+ /**
17
+ * @description set when the pipeline couldn't be created or resumed because another one
18
+ * holds its concurrency key - "already running".
19
+ */
20
+ readonly conflict: import("rxjs").Observable<PipelineConflict | undefined>;
16
21
  constructor(host: UmbControllerHost);
17
22
  private isMessage;
18
23
  private isState;
@@ -9,11 +9,11 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _PipelineContext_instances, _PipelineContext_repository, _PipelineContext_hubContext, _PipelineContext_pipeline, _PipelineContext_state, _PipelineContext_options, _PipelineContext_results, _PipelineContext_message, _PipelineContext_backgroundState, _PipelineContext_clientId, _PipelineContext_hasConnectedBefore, _PipelineContext_onClientIdChanged, _PipelineContext_isInFlight, _PipelineContext_trigger, _PipelineContext_close, _PipelineContext_finalized, _PipelineContext_valid, _PipelineContext_pending;
12
+ var _PipelineContext_instances, _PipelineContext_repository, _PipelineContext_hubContext, _PipelineContext_pipeline, _PipelineContext_state, _PipelineContext_options, _PipelineContext_results, _PipelineContext_message, _PipelineContext_backgroundState, _PipelineContext_conflict, _PipelineContext_clientId, _PipelineContext_hasConnectedBefore, _PipelineContext_onClientIdChanged, _PipelineContext_isInFlight, _PipelineContext_setConflict, _PipelineContext_trigger, _PipelineContext_close, _PipelineContext_finalized, _PipelineContext_valid, _PipelineContext_pending;
13
13
  import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
14
14
  import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
15
15
  import { UmbBooleanState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
16
- import { PipelineStatus, PipelineRepository, ProcessingSignalRContext, } from '@jumoo/processing';
16
+ import { PipelineStatus, PipelineRepository, ProcessingSignalRContext, getPipelineConflict, } from '@jumoo/processing';
17
17
  /**
18
18
  * @description context for keeping pipeline status and progress as we do things.
19
19
  */
@@ -35,6 +35,12 @@ export class PipelineContext extends UmbControllerBase {
35
35
  this.message = __classPrivateFieldGet(this, _PipelineContext_message, "f").asObservable();
36
36
  _PipelineContext_backgroundState.set(this, new UmbObjectState(undefined));
37
37
  this.backgroundState = __classPrivateFieldGet(this, _PipelineContext_backgroundState, "f").asObservable();
38
+ _PipelineContext_conflict.set(this, new UmbObjectState(undefined));
39
+ /**
40
+ * @description set when the pipeline couldn't be created or resumed because another one
41
+ * holds its concurrency key - "already running".
42
+ */
43
+ this.conflict = __classPrivateFieldGet(this, _PipelineContext_conflict, "f").asObservable();
38
44
  // tracks the SignalR connection id ourselves, alongside the hub context's own clientId
39
45
  // observable, so #onClientIdChanged can tell "first connect" (nothing to reattach - there's
40
46
  // no prior, now-dead connection) apart from "changed after already having one" (a genuine
@@ -86,8 +92,13 @@ export class PipelineContext extends UmbControllerBase {
86
92
  return message !== undefined;
87
93
  }
88
94
  async createPipeline(processor, strategy, options) {
89
- const pipeline = await __classPrivateFieldGet(this, _PipelineContext_repository, "f").create(processor, strategy, options);
90
- __classPrivateFieldGet(this, _PipelineContext_pipeline, "f").setValue(pipeline);
95
+ __classPrivateFieldGet(this, _PipelineContext_conflict, "f").setValue(undefined);
96
+ const { data, error } = await __classPrivateFieldGet(this, _PipelineContext_repository, "f").tryCreate(processor, strategy, options);
97
+ if (error) {
98
+ await __classPrivateFieldGet(this, _PipelineContext_instances, "m", _PipelineContext_setConflict).call(this, error);
99
+ return;
100
+ }
101
+ __classPrivateFieldGet(this, _PipelineContext_pipeline, "f").setValue(data);
91
102
  }
92
103
  async loadPipeline(id) {
93
104
  const pipeline = await __classPrivateFieldGet(this, _PipelineContext_repository, "f").Get(id);
@@ -113,7 +124,11 @@ export class PipelineContext extends UmbControllerBase {
113
124
  if (!__classPrivateFieldGet(this, _PipelineContext_pipeline, "f").value)
114
125
  return;
115
126
  const clientId = __classPrivateFieldGet(this, _PipelineContext_hubContext, "f")?.getClientId() ?? null;
116
- const response = await __classPrivateFieldGet(this, _PipelineContext_repository, "f").process(__classPrivateFieldGet(this, _PipelineContext_pipeline, "f").value, clientId);
127
+ const { data: response, error } = await __classPrivateFieldGet(this, _PipelineContext_repository, "f").tryProcess(__classPrivateFieldGet(this, _PipelineContext_pipeline, "f").value, clientId);
128
+ if (error) {
129
+ await __classPrivateFieldGet(this, _PipelineContext_instances, "m", _PipelineContext_setConflict).call(this, error);
130
+ return undefined;
131
+ }
117
132
  if (response?.state) {
118
133
  __classPrivateFieldGet(this, _PipelineContext_state, "f").setValue(response.state);
119
134
  }
@@ -128,7 +143,9 @@ export class PipelineContext extends UmbControllerBase {
128
143
  async updateOptions(options) {
129
144
  if (!__classPrivateFieldGet(this, _PipelineContext_pipeline, "f").value)
130
145
  return;
131
- const response = await __classPrivateFieldGet(this, _PipelineContext_repository, "f").update(__classPrivateFieldGet(this, _PipelineContext_pipeline, "f").value, options);
146
+ const { data: response, error } = await __classPrivateFieldGet(this, _PipelineContext_repository, "f").tryUpdate(__classPrivateFieldGet(this, _PipelineContext_pipeline, "f").value, options);
147
+ if (error)
148
+ await __classPrivateFieldGet(this, _PipelineContext_instances, "m", _PipelineContext_setConflict).call(this, error);
132
149
  if (response?.options) {
133
150
  console.debug('update: pipeline.options', response);
134
151
  __classPrivateFieldGet(this, _PipelineContext_options, "f").setValue(response.options);
@@ -140,7 +157,9 @@ export class PipelineContext extends UmbControllerBase {
140
157
  async saveOptions() {
141
158
  if (!__classPrivateFieldGet(this, _PipelineContext_pipeline, "f").value)
142
159
  return;
143
- const response = await __classPrivateFieldGet(this, _PipelineContext_repository, "f").update(__classPrivateFieldGet(this, _PipelineContext_pipeline, "f").value, __classPrivateFieldGet(this, _PipelineContext_options, "f").value);
160
+ const { data: response, error } = await __classPrivateFieldGet(this, _PipelineContext_repository, "f").tryUpdate(__classPrivateFieldGet(this, _PipelineContext_pipeline, "f").value, __classPrivateFieldGet(this, _PipelineContext_options, "f").value);
161
+ if (error)
162
+ await __classPrivateFieldGet(this, _PipelineContext_instances, "m", _PipelineContext_setConflict).call(this, error);
144
163
  if (response?.options) {
145
164
  console.debug('save:pipeline.options', response);
146
165
  __classPrivateFieldGet(this, _PipelineContext_options, "f").setValue(response.options);
@@ -217,7 +236,7 @@ export class PipelineContext extends UmbControllerBase {
217
236
  __classPrivateFieldGet(this, _PipelineContext_pending, "f").setValue(__classPrivateFieldGet(this, _PipelineContext_options, "f").value);
218
237
  }
219
238
  }
220
- _PipelineContext_repository = new WeakMap(), _PipelineContext_hubContext = new WeakMap(), _PipelineContext_pipeline = new WeakMap(), _PipelineContext_state = new WeakMap(), _PipelineContext_options = new WeakMap(), _PipelineContext_results = new WeakMap(), _PipelineContext_message = new WeakMap(), _PipelineContext_backgroundState = new WeakMap(), _PipelineContext_clientId = new WeakMap(), _PipelineContext_hasConnectedBefore = new WeakMap(), _PipelineContext_trigger = new WeakMap(), _PipelineContext_close = new WeakMap(), _PipelineContext_finalized = new WeakMap(), _PipelineContext_valid = new WeakMap(), _PipelineContext_pending = new WeakMap(), _PipelineContext_instances = new WeakSet(), _PipelineContext_onClientIdChanged =
239
+ _PipelineContext_repository = new WeakMap(), _PipelineContext_hubContext = new WeakMap(), _PipelineContext_pipeline = new WeakMap(), _PipelineContext_state = new WeakMap(), _PipelineContext_options = new WeakMap(), _PipelineContext_results = new WeakMap(), _PipelineContext_message = new WeakMap(), _PipelineContext_backgroundState = new WeakMap(), _PipelineContext_conflict = new WeakMap(), _PipelineContext_clientId = new WeakMap(), _PipelineContext_hasConnectedBefore = new WeakMap(), _PipelineContext_trigger = new WeakMap(), _PipelineContext_close = new WeakMap(), _PipelineContext_finalized = new WeakMap(), _PipelineContext_valid = new WeakMap(), _PipelineContext_pending = new WeakMap(), _PipelineContext_instances = new WeakSet(), _PipelineContext_onClientIdChanged =
221
240
  /**
222
241
  * @description called whenever the SignalR connection id changes - which includes the very
223
242
  * first connect, where there's nothing to reattach yet, and a reconnect, where a pipeline
@@ -255,6 +274,22 @@ async function _PipelineContext_onClientIdChanged(clientId) {
255
274
  default:
256
275
  return false;
257
276
  }
277
+ }, _PipelineContext_setConflict =
278
+ /**
279
+ * @description records a 409 "already running", with the holder's status when the user can
280
+ * see it.
281
+ * @param error the error from a create, process or update.
282
+ * @returns true when it was a conflict.
283
+ */
284
+ async function _PipelineContext_setConflict(error) {
285
+ const conflict = getPipelineConflict(error);
286
+ if (!conflict)
287
+ return false;
288
+ __classPrivateFieldGet(this, _PipelineContext_conflict, "f").setValue(conflict);
289
+ const holder = await __classPrivateFieldGet(this, _PipelineContext_repository, "f").getRunStatus(conflict.runningPipelineId);
290
+ if (holder)
291
+ __classPrivateFieldGet(this, _PipelineContext_conflict, "f").setValue({ ...conflict, holder });
292
+ return true;
258
293
  };
259
294
  export default PipelineContext;
260
295
  export const JUMOO_PROCESSING_CONTEXT = new UmbContextToken('JumooPipelineContext');
@@ -9,6 +9,8 @@ import { ProcessingOptions } from '@jumoo/processing';
9
9
  * @fires pipeline-waiting - when the pipeline is waiting for user input
10
10
  * @fires pipeline-last-action - when the pipeline is on the last action
11
11
  * @fires pipeline-status - the current status of the pipeline.
12
+ * @fires pipeline-conflict - when the pipeline can't be created or resumed because another
13
+ * process is already running for the same target (detail is the PipelineConflict).
12
14
  */
13
15
  export declare class JumooPipelineElement extends UmbLitElement {
14
16
  private context?;
@@ -35,6 +37,7 @@ export declare class JumooPipelineElement extends UmbLitElement {
35
37
  private results;
36
38
  private message;
37
39
  private backgroundState;
40
+ private conflict;
38
41
  constructor();
39
42
  connectedCallback(): void;
40
43
  private process;
@@ -57,6 +60,14 @@ export declare class JumooPipelineElement extends UmbLitElement {
57
60
  */
58
61
  private onLastAction;
59
62
  render(): import("lit-html").TemplateResult<1>;
63
+ private renderConflict;
64
+ /**
65
+ * @description the time worth showing for the holder: when it was queued, when it last
66
+ * moved, how long it has been paused, or when it is due.
67
+ * @param holder the holder's status.
68
+ * @returns the label key and the time, or undefined when there is nothing to show.
69
+ */
70
+ private conflictTime;
60
71
  private renderDebug;
61
72
  private renderStatus;
62
73
  private renderBackground;
@@ -7,6 +7,24 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  import { css, customElement, html, nothing, property, state, } from '@umbraco-cms/backoffice/external/lit';
8
8
  import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
9
9
  import { PipelineStatus, JUMOO_PROCESSING_CONTEXT, } from '@jumoo/processing';
10
+ import { PipelineRunState } from '../api/index.js';
11
+ /**
12
+ * @description the suffix on the conflict panel's headline and message keys for the holder's
13
+ * status. A holder that isn't moving or waiting gets the general wording.
14
+ * @param status the holder's status.
15
+ * @returns the key suffix.
16
+ */
17
+ function conflictSuffix(status) {
18
+ switch (status) {
19
+ case PipelineRunState.RUNNING:
20
+ case PipelineRunState.QUEUED:
21
+ case PipelineRunState.WAITING:
22
+ case PipelineRunState.SCHEDULED:
23
+ return status;
24
+ default:
25
+ return '';
26
+ }
27
+ }
10
28
  /**
11
29
  * @property {string} processor - the processor to use for the pipeline
12
30
  * @property {string} strategy - the strategy to use for the pipeline
@@ -16,6 +34,8 @@ import { PipelineStatus, JUMOO_PROCESSING_CONTEXT, } from '@jumoo/processing';
16
34
  * @fires pipeline-waiting - when the pipeline is waiting for user input
17
35
  * @fires pipeline-last-action - when the pipeline is on the last action
18
36
  * @fires pipeline-status - the current status of the pipeline.
37
+ * @fires pipeline-conflict - when the pipeline can't be created or resumed because another
38
+ * process is already running for the same target (detail is the PipelineConflict).
19
39
  */
20
40
  let JumooPipelineElement = class JumooPipelineElement extends UmbLitElement {
21
41
  constructor() {
@@ -73,6 +93,17 @@ let JumooPipelineElement = class JumooPipelineElement extends UmbLitElement {
73
93
  this.observe(this.context.results, (results) => {
74
94
  this.results = results;
75
95
  });
96
+ this.observe(this.context.conflict, (conflict) => {
97
+ const isNew = conflict && !this.conflict;
98
+ this.conflict = conflict;
99
+ if (isNew) {
100
+ this.dispatchEvent(new CustomEvent('pipeline-conflict', {
101
+ bubbles: true,
102
+ composed: true,
103
+ detail: conflict,
104
+ }));
105
+ }
106
+ });
76
107
  });
77
108
  }
78
109
  connectedCallback() {
@@ -139,7 +170,68 @@ let JumooPipelineElement = class JumooPipelineElement extends UmbLitElement {
139
170
  this.fireEvent('pipeline-last-action');
140
171
  }
141
172
  render() {
142
- return html `${this.renderStatus()}${this.renderDebug()}`;
173
+ return html `${this.conflict ? this.renderConflict(this.conflict) : this.renderStatus()}${this.renderDebug()}`;
174
+ }
175
+ renderConflict(conflict) {
176
+ const holder = conflict.holder;
177
+ const suffix = holder ? conflictSuffix(holder.status) : '';
178
+ const time = holder ? this.conflictTime(holder) : undefined;
179
+ const dateFormat = {
180
+ dateStyle: 'medium',
181
+ timeStyle: 'short',
182
+ };
183
+ return html `<div class="conflict">
184
+ <div class="header">
185
+ <uui-icon name="icon-alert"></uui-icon>
186
+ <h3>${this.localize.term(`jumooProcConflict_headline${suffix}`)}</h3>
187
+ </div>
188
+ <p>${this.localize.term(`jumooProcConflict_message${suffix}`)}</p>
189
+ ${holder
190
+ ? html `<dl>
191
+ <dt><umb-localize key="jumooProcConflict_status">Status</umb-localize></dt>
192
+ <dd>${this.localize.term(`jumooProcConflict_state${holder.status}`)}</dd>
193
+ ${holder.startedByName
194
+ ? html `<dt>
195
+ <umb-localize key="jumooProcConflict_startedBy"
196
+ >Started by</umb-localize
197
+ >
198
+ </dt>
199
+ <dd>${holder.startedByName}</dd>`
200
+ : nothing}
201
+ ${time
202
+ ? html `<dt>${this.localize.term(time.label)}</dt>
203
+ <dd>${this.localize.date(time.value, dateFormat)}</dd>`
204
+ : nothing}
205
+ ${holder.status === PipelineRunState.RUNNING
206
+ ? html `<dt>
207
+ <umb-localize key="jumooProcConflict_progress"
208
+ >Progress</umb-localize
209
+ >
210
+ </dt>
211
+ <dd>${Math.round(holder.progress.percentage)}%</dd>`
212
+ : nothing}
213
+ </dl>`
214
+ : nothing}
215
+ </div>`;
216
+ }
217
+ /**
218
+ * @description the time worth showing for the holder: when it was queued, when it last
219
+ * moved, how long it has been paused, or when it is due.
220
+ * @param holder the holder's status.
221
+ * @returns the label key and the time, or undefined when there is nothing to show.
222
+ */
223
+ conflictTime(holder) {
224
+ const pick = (label, value) => value ? { label: `jumooProcConflict_${label}`, value } : undefined;
225
+ switch (holder.status) {
226
+ case PipelineRunState.QUEUED:
227
+ return pick('queued', holder.created);
228
+ case PipelineRunState.WAITING:
229
+ return pick('waitingSince', holder.modified);
230
+ case PipelineRunState.SCHEDULED:
231
+ return pick('scheduledFor', holder.scheduledFor);
232
+ default:
233
+ return pick('lastActivity', holder.modified ?? holder.created);
234
+ }
143
235
  }
144
236
  renderDebug() {
145
237
  return this.debug
@@ -155,6 +247,7 @@ let JumooPipelineElement = class JumooPipelineElement extends UmbLitElement {
155
247
  case PipelineStatus.RUNNING:
156
248
  return this.renderSteps();
157
249
  case PipelineStatus.FAILED:
250
+ case PipelineStatus.CANCELLED:
158
251
  return this.renderFail();
159
252
  case PipelineStatus.BACKGROUND:
160
253
  return this.renderBackground();
@@ -164,11 +257,11 @@ let JumooPipelineElement = class JumooPipelineElement extends UmbLitElement {
164
257
  }
165
258
  renderBackground() {
166
259
  return !this.state?.backgroundState?.backgroundElement
167
- ? html `<jumoo-pipeline-background
168
- .message=${this.message}
169
- .state=${this.backgroundState ?? this.state}
170
- .pipelineId=${this._pipelineId}
171
- .background=${this.state?.backgroundState}
260
+ ? html `<jumoo-pipeline-background
261
+ .message=${this.message}
262
+ .state=${this.backgroundState ?? this.state}
263
+ .pipelineId=${this._pipelineId}
264
+ .background=${this.state?.backgroundState}
172
265
  .options=${this.options}></jumoo-pipeline-background>`
173
266
  : this.renderElement(this.state?.backgroundState?.backgroundElement);
174
267
  }
@@ -177,48 +270,82 @@ let JumooPipelineElement = class JumooPipelineElement extends UmbLitElement {
177
270
  ? !this.state?.currentRequest?.action?.element
178
271
  ? html `<div>complete</div>`
179
272
  : this.renderElement(this.state?.currentRequest?.action?.element)
180
- : html `<jumoo-pipeline-background
181
- .message=${this.message}
182
- .state=${this.backgroundState ?? this.state}
183
- .pipelineId=${this._pipelineId}
184
- .background=${this.state?.backgroundState}
273
+ : html `<jumoo-pipeline-background
274
+ .message=${this.message}
275
+ .state=${this.backgroundState ?? this.state}
276
+ .pipelineId=${this._pipelineId}
277
+ .background=${this.state?.backgroundState}
185
278
  .options=${this.options}></jumoo-pipeline-background>`;
186
279
  }
187
280
  renderSteps() {
188
281
  if (!this.state?.progress)
189
282
  return nothing;
190
- return html `<jumoo-pipeline-progress
191
- .progress=${this.state?.progress}
283
+ return html `<jumoo-pipeline-progress
284
+ .progress=${this.state?.progress}
192
285
  .message=${this.message}></jumoo-pipeline-progress>`;
193
286
  }
194
287
  renderElement(element) {
195
288
  return !element
196
289
  ? nothing
197
- : html `
198
- <div class="wrapper">
199
- <umb-extension-slot
200
- type="jumoo-process-step"
201
- .filter=${(ext) => ext.elementName === element}
290
+ : html `
291
+ <div class="wrapper">
292
+ <umb-extension-slot
293
+ type="jumoo-process-step"
294
+ .filter=${(ext) => ext.elementName === element}
202
295
  .props=${{
203
296
  pipelineId: this._pipelineId,
204
297
  state: this.state,
205
298
  options: this.options,
206
299
  results: this.results,
207
- }}></umb-extension-slot>
208
- </div>
300
+ }}></umb-extension-slot>
301
+ </div>
209
302
  `;
210
303
  }
211
304
  renderFail() {
212
- return html `
213
- <jumoo-processing-error .error=${this.results?.error}></jumoo-processing-error>
305
+ return html `
306
+ <jumoo-processing-error .error=${this.results?.error}></jumoo-processing-error>
214
307
  `;
215
308
  }
216
309
  };
217
- JumooPipelineElement.styles = css `
218
- .wrapper,
219
- umb-extension-slot {
220
- height: 100%;
221
- }
310
+ JumooPipelineElement.styles = css `
311
+ .wrapper,
312
+ umb-extension-slot {
313
+ height: 100%;
314
+ }
315
+
316
+ .conflict {
317
+ padding: var(--uui-size-space-5);
318
+ border: 1px solid var(--uui-color-warning-standalone);
319
+ border-left-width: 4px;
320
+ border-radius: var(--uui-border-radius);
321
+ background-color: var(--uui-color-surface);
322
+ }
323
+
324
+ .conflict .header {
325
+ display: flex;
326
+ align-items: center;
327
+ gap: var(--uui-size-space-3);
328
+ color: var(--uui-color-warning-standalone);
329
+ }
330
+
331
+ .conflict h3 {
332
+ margin: 0;
333
+ }
334
+
335
+ .conflict dl {
336
+ display: grid;
337
+ grid-template-columns: max-content 1fr;
338
+ gap: var(--uui-size-space-2) var(--uui-size-space-5);
339
+ margin: var(--uui-size-space-4) 0;
340
+ }
341
+
342
+ .conflict dt {
343
+ font-weight: bold;
344
+ }
345
+
346
+ .conflict dd {
347
+ margin: 0;
348
+ }
222
349
  `;
223
350
  __decorate([
224
351
  property({ type: String })
@@ -253,6 +380,9 @@ __decorate([
253
380
  __decorate([
254
381
  state()
255
382
  ], JumooPipelineElement.prototype, "backgroundState", void 0);
383
+ __decorate([
384
+ state()
385
+ ], JumooPipelineElement.prototype, "conflict", void 0);
256
386
  JumooPipelineElement = __decorate([
257
387
  customElement('jumoo-pipeline')
258
388
  ], JumooPipelineElement);
@@ -14,6 +14,32 @@ export declare class PipelineRepository extends UmbControllerBase {
14
14
  getProcessors(): Promise<string[]>;
15
15
  Get(id: string): Promise<import("@jumoo/processing").Pipeline>;
16
16
  create(processor: string, strategy: string, options: ProcessingOptions | undefined): Promise<string>;
17
+ /**
18
+ * @description create, returning the whole response rather than just the id, so the caller
19
+ * can tell a 409 "already running" (see getPipelineConflict) from any other failure.
20
+ */
21
+ tryCreate(processor: string, strategy: string, options: ProcessingOptions | undefined): Promise<import("@umbraco-cms/backoffice/resources").UmbApiResponse<{
22
+ data: string;
23
+ request: Request;
24
+ response: Response;
25
+ }>>;
26
+ /**
27
+ * @description process, returning the whole response - see tryCreate.
28
+ */
29
+ tryProcess(id: string, clientId: string | null): Promise<import("@umbraco-cms/backoffice/resources").UmbApiResponse<{
30
+ data: import("@jumoo/processing").Pipeline;
31
+ request: Request;
32
+ response: Response;
33
+ }>>;
34
+ /**
35
+ * @description update, returning the whole response - see tryCreate.
36
+ */
37
+ tryUpdate(id: string, body: ProcessingOptions | undefined): Promise<import("@umbraco-cms/backoffice/resources").UmbApiResponse<{
38
+ data: import("@jumoo/processing").ProcessingOptionsResults;
39
+ request: Request;
40
+ response: Response;
41
+ }>>;
42
+ getRunStatus(id: string): Promise<import("@jumoo/processing").PipelineRunStatus>;
17
43
  complete(id: string): Promise<void>;
18
44
  getResults(id: string): Promise<import("@jumoo/processing").ProcessingResults>;
19
45
  listPipelines(status: PipelineStatus): Promise<import("@jumoo/processing").Pipeline[]>;
@@ -29,6 +29,28 @@ export class PipelineRepository extends UmbControllerBase {
29
29
  async create(processor, strategy, options) {
30
30
  return (await this.source.create(processor, strategy, options)).data;
31
31
  }
32
+ /**
33
+ * @description create, returning the whole response rather than just the id, so the caller
34
+ * can tell a 409 "already running" (see getPipelineConflict) from any other failure.
35
+ */
36
+ async tryCreate(processor, strategy, options) {
37
+ return await this.source.create(processor, strategy, options);
38
+ }
39
+ /**
40
+ * @description process, returning the whole response - see tryCreate.
41
+ */
42
+ async tryProcess(id, clientId) {
43
+ return await this.source.process(id, clientId);
44
+ }
45
+ /**
46
+ * @description update, returning the whole response - see tryCreate.
47
+ */
48
+ async tryUpdate(id, body) {
49
+ return await this.source.update(id, body);
50
+ }
51
+ async getRunStatus(id) {
52
+ return (await this.source.getRunStatus(id)).data;
53
+ }
32
54
  async complete(id) {
33
55
  console.debug('complete', id);
34
56
  }