@truedat/dd 8.13.5 → 8.13.7
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/package.json +3 -3
- package/src/components/GraphGenerationDialog.js +125 -0
- package/src/components/GraphGenerationNotices.js +67 -0
- package/src/components/GraphLoader.js +12 -17
- package/src/components/GraphRedirector.js +4 -4
- package/src/components/LineageGraph.js +38 -49
- package/src/components/LineageRoutes.js +20 -13
- package/src/components/StructureNotesEdit.js +8 -2
- package/src/components/SystemForm.js +10 -1
- package/src/components/__tests__/GraphGenerationDialog.spec.js +117 -0
- package/src/components/__tests__/GraphGenerationNotices.spec.js +60 -0
- package/src/components/__tests__/GraphGenerationTranslations.spec.js +74 -0
- package/src/components/__tests__/GraphLoader.spec.js +83 -0
- package/src/components/__tests__/LineageGraphInteraction.spec.js +78 -7
- package/src/components/__tests__/StructureNotesEdit.spec.js +105 -1
- package/src/components/__tests__/SystemForm.dateRestrictions.spec.js +102 -0
- package/src/components/__tests__/__snapshots__/LineageRoutes.spec.js.snap +12 -1
- package/src/components/__tests__/fixtures/graphGenerationMessages.json +28 -0
- package/src/messages/en.js +0 -29
- package/src/messages/es.js +0 -30
- package/src/reducers/__tests__/graphGenerationState.spec.js +113 -0
- package/src/reducers/__tests__/structureNoteErrorMessage.spec.js +78 -0
- package/src/reducers/ddMessage.js +16 -1
- package/src/reducers/graphGenerationState.js +113 -0
- package/src/reducers/graphLoading.js +2 -0
- package/src/reducers/index.js +2 -0
- package/src/routines.js +3 -0
- package/src/sagas/__tests__/createGraph.spec.js +57 -122
- package/src/sagas/__tests__/fetchGraph.spec.js +19 -0
- package/src/sagas/createGraph.js +13 -65
- package/src/sagas/fetchGraph.js +6 -38
- package/src/sagas/graph/__tests__/backgroundGraphs.spec.js +22 -0
- package/src/sagas/graph/__tests__/generation.spec.js +247 -0
- package/src/sagas/graph/__tests__/polling.spec.js +88 -0
- package/src/sagas/graph/generation.js +99 -0
- package/src/sagas/graph/polling.js +26 -35
- package/src/sagas/index.js +2 -0
- package/src/styles/LineageGraph.less +117 -0
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
exports[`<LineageRoutes /> renders correctly with GRAPHS route 1`] = `
|
|
4
4
|
<div>
|
|
5
|
+
<div
|
|
6
|
+
aria-live="polite"
|
|
7
|
+
class="lineage-generation-notices"
|
|
8
|
+
/>
|
|
5
9
|
<div
|
|
6
10
|
class="ui icon visible bottom attached message center"
|
|
7
11
|
>
|
|
@@ -25,4 +29,11 @@ exports[`<LineageRoutes /> renders correctly with GRAPHS route 1`] = `
|
|
|
25
29
|
</div>
|
|
26
30
|
`;
|
|
27
31
|
|
|
28
|
-
exports[`<LineageRoutes /> renders correctly with default route 1`] =
|
|
32
|
+
exports[`<LineageRoutes /> renders correctly with default route 1`] = `
|
|
33
|
+
<div>
|
|
34
|
+
<div
|
|
35
|
+
aria-live="polite"
|
|
36
|
+
class="lineage-generation-notices"
|
|
37
|
+
/>
|
|
38
|
+
</div>
|
|
39
|
+
`;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"en": {
|
|
3
|
+
"lineage.graph.loading": "Analyzing...",
|
|
4
|
+
"lineage.graph.wait.background": "Generate in background",
|
|
5
|
+
"lineage.graph.wait.background.notice": "The graph is being generated in the background. We will notify you when it is ready.",
|
|
6
|
+
"lineage.graph.wait.close": "Close and continue generating in background",
|
|
7
|
+
"lineage.graph.wait.completed": "The graph is ready.",
|
|
8
|
+
"lineage.graph.wait.continue": "Keep waiting",
|
|
9
|
+
"lineage.graph.wait.dismiss": "Dismiss notification",
|
|
10
|
+
"lineage.graph.wait.explanation": "This graph is taking longer than expected. You can keep waiting or continue working while it is generated in the background.",
|
|
11
|
+
"lineage.graph.wait.failed": "Unable to complete graph generation. Please try again.",
|
|
12
|
+
"lineage.graph.wait.open": "Open graph",
|
|
13
|
+
"lineage.graph.wait.waiting": "Waiting…"
|
|
14
|
+
},
|
|
15
|
+
"es": {
|
|
16
|
+
"lineage.graph.loading": "Analizando...",
|
|
17
|
+
"lineage.graph.wait.background": "Generar en segundo plano",
|
|
18
|
+
"lineage.graph.wait.background.notice": "El grafo se está generando en segundo plano. Te avisaremos cuando esté listo.",
|
|
19
|
+
"lineage.graph.wait.close": "Cerrar y continuar generando en segundo plano",
|
|
20
|
+
"lineage.graph.wait.completed": "El grafo está listo.",
|
|
21
|
+
"lineage.graph.wait.continue": "Seguir esperando",
|
|
22
|
+
"lineage.graph.wait.dismiss": "Descartar notificación",
|
|
23
|
+
"lineage.graph.wait.explanation": "Este grafo está tardando más de lo esperado. Puedes seguir esperando o continuar trabajando mientras se genera en segundo plano.",
|
|
24
|
+
"lineage.graph.wait.failed": "No se ha podido completar la generación del grafo. Vuelve a intentarlo.",
|
|
25
|
+
"lineage.graph.wait.open": "Abrir grafo",
|
|
26
|
+
"lineage.graph.wait.waiting": "Esperando…"
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/messages/en.js
CHANGED
|
@@ -195,35 +195,6 @@ export default {
|
|
|
195
195
|
"joinType.left": "outer left",
|
|
196
196
|
"joinType.right": "outer right",
|
|
197
197
|
"joinType.full_outer": "full outer join",
|
|
198
|
-
"lineage.events.header": "My graphs",
|
|
199
|
-
"lineage.events.props.graph_data": "Graph",
|
|
200
|
-
"lineage.events.props.inserted_at": "Date",
|
|
201
|
-
"lineage.events.props.message": "Message",
|
|
202
|
-
"lineage.events.props.status": "Status",
|
|
203
|
-
"lineage.events.search.results.empty": "No graphs found",
|
|
204
|
-
"lineage.events.subheader": "Lineage graphs",
|
|
205
|
-
"lineage.form.header": "Select Structures",
|
|
206
|
-
"lineage.form.levels.all.label": "All",
|
|
207
|
-
"lineage.form.levels.header": "Levels",
|
|
208
|
-
"lineage.form.levels.select.input": "Select...",
|
|
209
|
-
"lineage.form.levels.select.label": "Depth",
|
|
210
|
-
"lineage.graph.loading": "Analyzing...",
|
|
211
|
-
"lineage.graph.loading.background.launched": "Background task launched",
|
|
212
|
-
"lineage.graph.loading.background.existing":
|
|
213
|
-
"The task for this graph was already launched",
|
|
214
|
-
"lineage.graph.loading.polling": "Poll retry #{retry}",
|
|
215
|
-
"lineage.graph.status.already_calculated": "Already calculated",
|
|
216
|
-
"lineage.graph.status.already_started": "Already started",
|
|
217
|
-
"lineage.graph.status.completed": "Completed",
|
|
218
|
-
"lineage.graph.status.failed": "Failed",
|
|
219
|
-
"lineage.graph.status.just_started": "Just started",
|
|
220
|
-
"lineage.graph.status.started": "Started",
|
|
221
|
-
"lineage.graph.status.timed_out": "Timed out",
|
|
222
|
-
"lineage.graph.metadata.header": "Information",
|
|
223
|
-
"lineage.graph.metadata.close.button": "Close",
|
|
224
|
-
"lineage.group.placeholder": "Select Top Level Structure",
|
|
225
|
-
"lineage.resources.placeholder": "Select Child Structures",
|
|
226
|
-
"lineage.subgroup.placeholder": "Select Child Structure",
|
|
227
198
|
metadata: "Metadata",
|
|
228
199
|
"metadataView.fields": "Available metadata fields",
|
|
229
200
|
"metadataView.fields.placeholder":
|
package/src/messages/es.js
CHANGED
|
@@ -199,36 +199,6 @@ export default {
|
|
|
199
199
|
"joinType.left": "outer left",
|
|
200
200
|
"joinType.right": "outer right",
|
|
201
201
|
"joinType.full_outer": "full outer join",
|
|
202
|
-
"lineage.events.header": "Mis grafos",
|
|
203
|
-
"lineage.events.props.graph_data": "Grafo",
|
|
204
|
-
"lineage.events.props.inserted_at": "Fecha",
|
|
205
|
-
"lineage.events.props.message": "Mensaje",
|
|
206
|
-
"lineage.events.props.status": "Estado",
|
|
207
|
-
"lineage.events.search.results.empty": "Ningún grafo encontrado",
|
|
208
|
-
"lineage.events.subheader": "Grafos de linaje",
|
|
209
|
-
"lineage.form.header": "Seleccionar Estructuras",
|
|
210
|
-
"lineage.form.levels.all.label": "Completo",
|
|
211
|
-
"lineage.form.levels.header": "Niveles",
|
|
212
|
-
"lineage.form.levels.select.input": "Seleccionar...",
|
|
213
|
-
"lineage.form.levels.select.label": "Profundidad",
|
|
214
|
-
"lineage.graph.loading": "Analizando...",
|
|
215
|
-
"lineage.graph.loading.background.launched":
|
|
216
|
-
"Tarea en segundo plano lanzada para grafo",
|
|
217
|
-
"lineage.graph.loading.background.existing":
|
|
218
|
-
"La tarea en segundo plano para este grafo ya fue lanzada",
|
|
219
|
-
"lineage.graph.loading.polling": "Sondeo #{retry}",
|
|
220
|
-
"lineage.graph.status.already_calculated": "Previamente calculado",
|
|
221
|
-
"lineage.graph.status.already_started": "Previamente iniciado",
|
|
222
|
-
"lineage.graph.status.completed": "Finalizado",
|
|
223
|
-
"lineage.graph.status.failed": "Fallido",
|
|
224
|
-
"lineage.graph.status.just_started": "Acaba de iniciarse",
|
|
225
|
-
"lineage.graph.status.started": "Iniciado",
|
|
226
|
-
"lineage.graph.status.timed_out": "Tiempo de espera máximo sobrepasado",
|
|
227
|
-
"lineage.graph.metadata.header": "Información",
|
|
228
|
-
"lineage.graph.metadata.close.button": "Cerrar",
|
|
229
|
-
"lineage.group.placeholder": "Seleccionar Estructura Padre",
|
|
230
|
-
"lineage.resources.placeholder": "Seleccionar Estructuras Hijos",
|
|
231
|
-
"lineage.subgroup.placeholder": "Seleccionar Estructura Hijo",
|
|
232
202
|
metadata: "Metadata",
|
|
233
203
|
"metadataView.fields": "Campos",
|
|
234
204
|
"metadataView.fields.placeholder":
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { graphGenerationState as reduce } from "../graphGenerationState";
|
|
2
|
+
import {
|
|
3
|
+
backgroundGraph,
|
|
4
|
+
clearGraph,
|
|
5
|
+
graphGeneration,
|
|
6
|
+
fetchLineageEvents,
|
|
7
|
+
} from "../../routines";
|
|
8
|
+
|
|
9
|
+
test("a new request backgrounds the previous one and ignores unrelated events", () => {
|
|
10
|
+
let state = reduce(undefined, graphGeneration.request({ requestId: "a" }));
|
|
11
|
+
state = reduce(state, graphGeneration({ requestId: "a", hash: "hash-a" }));
|
|
12
|
+
state = reduce(state, graphGeneration.request({ requestId: "b" }));
|
|
13
|
+
state = reduce(
|
|
14
|
+
state,
|
|
15
|
+
fetchLineageEvents.success({
|
|
16
|
+
data: [{ graph_hash: "other", graph_id: 2, status: "COMPLETED" }],
|
|
17
|
+
}),
|
|
18
|
+
);
|
|
19
|
+
expect(state.jobs.a).toMatchObject({ status: "pending", background: true });
|
|
20
|
+
expect(state.jobs.b.background).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("background completion matches a graph id, is stable and cannot be reverted by a late poll", () => {
|
|
24
|
+
let state = reduce(undefined, graphGeneration.request({ requestId: "a" }));
|
|
25
|
+
state = reduce(state, backgroundGraph());
|
|
26
|
+
state = reduce(
|
|
27
|
+
state,
|
|
28
|
+
graphGeneration.success({
|
|
29
|
+
requestId: "a",
|
|
30
|
+
graphId: 5,
|
|
31
|
+
status: "awaitingEvent",
|
|
32
|
+
}),
|
|
33
|
+
);
|
|
34
|
+
const complete = fetchLineageEvents.success({
|
|
35
|
+
data: [{ graph_id: 5, status: "COMPLETED" }],
|
|
36
|
+
});
|
|
37
|
+
state = reduce(state, complete);
|
|
38
|
+
expect(state.jobs.a.status).toBe("completed");
|
|
39
|
+
expect(reduce(state, complete)).toEqual(state);
|
|
40
|
+
expect(
|
|
41
|
+
reduce(
|
|
42
|
+
state,
|
|
43
|
+
graphGeneration.success({ requestId: "a", status: "awaitingEvent" }),
|
|
44
|
+
),
|
|
45
|
+
).toEqual(state);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("failed events notify failure and completed events without an id cannot be opened", () => {
|
|
49
|
+
let state = reduce(undefined, graphGeneration.request({ requestId: "a" }));
|
|
50
|
+
state = reduce(state, graphGeneration({ requestId: "a", hash: "a" }));
|
|
51
|
+
state = reduce(state, backgroundGraph());
|
|
52
|
+
state = reduce(
|
|
53
|
+
state,
|
|
54
|
+
fetchLineageEvents.success({
|
|
55
|
+
data: [{ graph_hash: "a", status: "COMPLETED" }],
|
|
56
|
+
}),
|
|
57
|
+
);
|
|
58
|
+
expect(state.jobs.a.status).toBe("pending");
|
|
59
|
+
state = reduce(
|
|
60
|
+
state,
|
|
61
|
+
fetchLineageEvents.success({
|
|
62
|
+
data: [{ graph_hash: "a", status: "FAILED" }],
|
|
63
|
+
}),
|
|
64
|
+
);
|
|
65
|
+
expect(state.jobs.a.status).toBe("failed");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test.each([clearGraph(), backgroundGraph({ notify: false })])(
|
|
69
|
+
"implicit background and manual close do not enable notifications: %j",
|
|
70
|
+
(action) => {
|
|
71
|
+
let state = reduce(undefined, graphGeneration.request({ requestId: "a" }));
|
|
72
|
+
state = reduce(state, action);
|
|
73
|
+
expect(state.jobs.a.notify).toBe(false);
|
|
74
|
+
},
|
|
75
|
+
);
|
|
76
|
+
test("only an explicit background choice enables notifications", () => {
|
|
77
|
+
let state = reduce(undefined, graphGeneration.request({ requestId: "a" }));
|
|
78
|
+
state = reduce(state, graphGeneration.request({ requestId: "b" }));
|
|
79
|
+
expect(state.jobs.a.notify).not.toBe(true);
|
|
80
|
+
state = reduce(state, backgroundGraph());
|
|
81
|
+
expect(state.jobs.b.notify).toBe(true);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("unrelated and repeated events preserve state and job references", () => {
|
|
85
|
+
let state = reduce(undefined, graphGeneration.request({ requestId: "a" }));
|
|
86
|
+
state = reduce(state, graphGeneration({ requestId: "a", hash: "a" }));
|
|
87
|
+
state = reduce(state, backgroundGraph());
|
|
88
|
+
expect(reduce(state, fetchLineageEvents.success({ data: [] }))).toBe(state);
|
|
89
|
+
const event = fetchLineageEvents.success({
|
|
90
|
+
data: [{ graph_hash: "a", graph_id: 5, status: "COMPLETED" }],
|
|
91
|
+
});
|
|
92
|
+
const completed = reduce(state, event);
|
|
93
|
+
expect(completed.jobs.a.status).toBe("completed");
|
|
94
|
+
expect(reduce(completed, event)).toBe(completed);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("event indexing preserves the first match across graph id and hash", () => {
|
|
98
|
+
const state = {
|
|
99
|
+
active: "a",
|
|
100
|
+
jobs: { a: { graphId: 5, hash: "a", notify: true, status: "pending" } },
|
|
101
|
+
};
|
|
102
|
+
const next = reduce(
|
|
103
|
+
state,
|
|
104
|
+
fetchLineageEvents.success({
|
|
105
|
+
data: [
|
|
106
|
+
{ graph_hash: "a", graph_id: 6, status: "COMPLETED" },
|
|
107
|
+
{ graph_id: 5, status: "FAILED" },
|
|
108
|
+
{ graph_hash: "a", graph_id: 7, status: "FAILED" },
|
|
109
|
+
],
|
|
110
|
+
}),
|
|
111
|
+
);
|
|
112
|
+
expect(next.jobs.a).toMatchObject({ graphId: 6, status: "completed" });
|
|
113
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { doStructureNoteAction } from "../../routines";
|
|
2
|
+
import { ddMessage, initialState } from "../ddMessage";
|
|
3
|
+
|
|
4
|
+
describe("reducers: ddMessage on a failed structure note action", () => {
|
|
5
|
+
it("surfaces the backend's field error", () => {
|
|
6
|
+
const state = ddMessage(initialState, {
|
|
7
|
+
type: doStructureNoteAction.FAILURE,
|
|
8
|
+
payload: {
|
|
9
|
+
status: 422,
|
|
10
|
+
data: {
|
|
11
|
+
errors: {
|
|
12
|
+
fecha: ["2026-06-30 is not within the allowed range"],
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
expect(state.error).toBe(true);
|
|
19
|
+
expect(state.header).toBe("alert.doStructureNoteAction.failed.header");
|
|
20
|
+
expect(state.content).toBe("2026-06-30 is not within the allowed range");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("names every field that failed", () => {
|
|
24
|
+
const state = ddMessage(initialState, {
|
|
25
|
+
type: doStructureNoteAction.FAILURE,
|
|
26
|
+
payload: {
|
|
27
|
+
status: 422,
|
|
28
|
+
data: {
|
|
29
|
+
errors: {
|
|
30
|
+
fecha: ["2026-06-30 is not within the allowed range"],
|
|
31
|
+
nombre: ["can't be blank"],
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
expect(state.content).toContain(
|
|
38
|
+
"2026-06-30 is not within the allowed range",
|
|
39
|
+
);
|
|
40
|
+
expect(state.content).toContain("can't be blank");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("still falls back to the header when the payload carries no errors", () => {
|
|
44
|
+
const state = ddMessage(initialState, {
|
|
45
|
+
type: doStructureNoteAction.FAILURE,
|
|
46
|
+
payload: { status: 500, data: {} },
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
expect(state.error).toBe(true);
|
|
50
|
+
expect(state.header).toBe("alert.doStructureNoteAction.failed.header");
|
|
51
|
+
expect(state.content).toBeFalsy();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("keeps a non-validation error message when the backend sends one", () => {
|
|
55
|
+
const state = ddMessage(initialState, {
|
|
56
|
+
type: doStructureNoteAction.FAILURE,
|
|
57
|
+
payload: { status: 400, data: { error: "structure note is locked" } },
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(state.content).toBe("structure note is locked");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("passes structured date errors to the shared Alert", () => {
|
|
64
|
+
const error = {
|
|
65
|
+
name: "structureNote.error.errors.date_restrictions.out_of_range",
|
|
66
|
+
field: "fecha max fija",
|
|
67
|
+
values: { limit: "2026-10-15" },
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const state = ddMessage(initialState, {
|
|
71
|
+
type: doStructureNoteAction.FAILURE,
|
|
72
|
+
payload: { status: 422, data: { errors: [error] } },
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
expect(state.errorList).toEqual([error]);
|
|
76
|
+
expect(state.content).toBeUndefined();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -132,11 +132,26 @@ const ddMessage = (state = initialState, { type, payload }) => {
|
|
|
132
132
|
error: true,
|
|
133
133
|
header: "alert.doStructureNoteAction.failed.header",
|
|
134
134
|
icon: "attention",
|
|
135
|
-
|
|
135
|
+
...noteActionErrorMessage(payload),
|
|
136
136
|
};
|
|
137
137
|
default:
|
|
138
138
|
return state;
|
|
139
139
|
}
|
|
140
140
|
};
|
|
141
141
|
|
|
142
|
+
const noteActionErrorMessage = (payload) => {
|
|
143
|
+
const errors = payload?.data?.errors;
|
|
144
|
+
|
|
145
|
+
if (Array.isArray(errors)) {
|
|
146
|
+
return { errorList: errors };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (errors && typeof errors === "object") {
|
|
150
|
+
const messages = Object.values(errors).flat().filter(Boolean);
|
|
151
|
+
if (messages.length) return { content: messages.join(" ") };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return { content: payload?.data?.error };
|
|
155
|
+
};
|
|
156
|
+
|
|
142
157
|
export { ddMessage };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import {
|
|
2
|
+
backgroundGraph,
|
|
3
|
+
clearGraph,
|
|
4
|
+
dismissGraphGeneration,
|
|
5
|
+
graphGeneration,
|
|
6
|
+
fetchLineageEvents,
|
|
7
|
+
} from "../routines";
|
|
8
|
+
import {
|
|
9
|
+
isCompletedStatus,
|
|
10
|
+
isFailedStatus,
|
|
11
|
+
} from "../components/lineageEventHelpers";
|
|
12
|
+
|
|
13
|
+
export const initialState = { active: null, jobs: {} };
|
|
14
|
+
|
|
15
|
+
export const graphGenerationState = (
|
|
16
|
+
state = initialState,
|
|
17
|
+
{ type, payload },
|
|
18
|
+
) => {
|
|
19
|
+
const activeJob = state.jobs[state.active];
|
|
20
|
+
switch (type) {
|
|
21
|
+
case graphGeneration.REQUEST:
|
|
22
|
+
return {
|
|
23
|
+
active: payload.requestId,
|
|
24
|
+
jobs: {
|
|
25
|
+
...Object.fromEntries(
|
|
26
|
+
Object.entries(state.jobs).filter(
|
|
27
|
+
([, job]) =>
|
|
28
|
+
job.notify || (!job.background && job.status === "failed"),
|
|
29
|
+
),
|
|
30
|
+
),
|
|
31
|
+
...(activeJob?.status === "pending"
|
|
32
|
+
? { [state.active]: { ...activeJob, background: true } }
|
|
33
|
+
: {}),
|
|
34
|
+
[payload.requestId]: {
|
|
35
|
+
requestId: payload.requestId,
|
|
36
|
+
status: "pending",
|
|
37
|
+
background: false,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
case backgroundGraph.TRIGGER:
|
|
42
|
+
case clearGraph.TRIGGER:
|
|
43
|
+
return activeJob?.status === "pending"
|
|
44
|
+
? {
|
|
45
|
+
...state,
|
|
46
|
+
jobs: {
|
|
47
|
+
...state.jobs,
|
|
48
|
+
[state.active]: {
|
|
49
|
+
...activeJob,
|
|
50
|
+
background: true,
|
|
51
|
+
notify:
|
|
52
|
+
type === backgroundGraph.TRIGGER
|
|
53
|
+
? payload?.notify !== false
|
|
54
|
+
: activeJob.notify === true,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
: state;
|
|
59
|
+
case graphGeneration.TRIGGER:
|
|
60
|
+
case graphGeneration.SUCCESS:
|
|
61
|
+
case graphGeneration.FAILURE: {
|
|
62
|
+
const job = state.jobs[payload.requestId];
|
|
63
|
+
if (!job || ["completed", "failed"].includes(job.status)) return state;
|
|
64
|
+
return {
|
|
65
|
+
...state,
|
|
66
|
+
jobs: { ...state.jobs, [payload.requestId]: { ...job, ...payload } },
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
case fetchLineageEvents.SUCCESS: {
|
|
70
|
+
const byId = new Map();
|
|
71
|
+
const byHash = new Map();
|
|
72
|
+
(payload.data || []).forEach((event, index) => {
|
|
73
|
+
const entry = { event, index };
|
|
74
|
+
if (event.graph_id != null && !byId.has(String(event.graph_id)))
|
|
75
|
+
byId.set(String(event.graph_id), entry);
|
|
76
|
+
if (event.graph_hash && !byHash.has(event.graph_hash))
|
|
77
|
+
byHash.set(event.graph_hash, entry);
|
|
78
|
+
});
|
|
79
|
+
const entries = Object.entries(state.jobs).map(([key, job]) => {
|
|
80
|
+
if (!job.notify || !["pending", "awaitingEvent"].includes(job.status))
|
|
81
|
+
return [key, job];
|
|
82
|
+
const idMatch =
|
|
83
|
+
job.graphId != null ? byId.get(String(job.graphId)) : undefined;
|
|
84
|
+
const hashMatch = job.hash ? byHash.get(job.hash) : undefined;
|
|
85
|
+
const event = (
|
|
86
|
+
idMatch && (!hashMatch || idMatch.index < hashMatch.index)
|
|
87
|
+
? idMatch
|
|
88
|
+
: hashMatch
|
|
89
|
+
)?.event;
|
|
90
|
+
if (isFailedStatus(event?.status))
|
|
91
|
+
return [key, { ...job, status: "failed" }];
|
|
92
|
+
if (isCompletedStatus(event?.status) && event.graph_id != null)
|
|
93
|
+
return [
|
|
94
|
+
key,
|
|
95
|
+
{ ...job, status: "completed", graphId: event.graph_id },
|
|
96
|
+
];
|
|
97
|
+
return [key, job];
|
|
98
|
+
});
|
|
99
|
+
return entries.some(([key, job]) => state.jobs[key] !== job)
|
|
100
|
+
? { ...state, jobs: Object.fromEntries(entries) }
|
|
101
|
+
: state;
|
|
102
|
+
}
|
|
103
|
+
case dismissGraphGeneration.TRIGGER:
|
|
104
|
+
return {
|
|
105
|
+
...state,
|
|
106
|
+
jobs: Object.fromEntries(
|
|
107
|
+
Object.entries(state.jobs).filter(([key]) => key !== payload),
|
|
108
|
+
),
|
|
109
|
+
};
|
|
110
|
+
default:
|
|
111
|
+
return state;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
backgroundGraph,
|
|
2
3
|
clearGraph,
|
|
3
4
|
clearStructure,
|
|
4
5
|
createGraph,
|
|
@@ -10,6 +11,7 @@ import {
|
|
|
10
11
|
|
|
11
12
|
export const graphLoading = (state = false, { type, payload }) => {
|
|
12
13
|
switch (type) {
|
|
14
|
+
case backgroundGraph.TRIGGER:
|
|
13
15
|
case clearGraph.TRIGGER:
|
|
14
16
|
return false;
|
|
15
17
|
case clearStructure.TRIGGER:
|
package/src/reducers/index.js
CHANGED
|
@@ -19,6 +19,7 @@ import { grantSelectedFilter } from "./grantSelectedFilter";
|
|
|
19
19
|
import { grantsPageSize } from "./grantsPageSize";
|
|
20
20
|
|
|
21
21
|
import { graph } from "./graph";
|
|
22
|
+
import { graphGenerationState } from "./graphGenerationState";
|
|
22
23
|
import { graphLoading } from "./graphLoading";
|
|
23
24
|
import { graphRedirect } from "./graphRedirect";
|
|
24
25
|
import { lineageEvents } from "./lineageEvents";
|
|
@@ -98,6 +99,7 @@ export {
|
|
|
98
99
|
grantSelectedFilter,
|
|
99
100
|
grantsPageSize,
|
|
100
101
|
graph,
|
|
102
|
+
graphGenerationState,
|
|
101
103
|
graphLoading,
|
|
102
104
|
graphRedirect,
|
|
103
105
|
lineageEvents,
|
package/src/routines.js
CHANGED
|
@@ -6,6 +6,7 @@ export const addStructureSelectedFilter = createRoutine(
|
|
|
6
6
|
"ADD_STRUCTURE_SELECTED_FILTER",
|
|
7
7
|
);
|
|
8
8
|
export const applyUserSearchFilter = createRoutine("APPLY_USER_SEARCH_FILTER");
|
|
9
|
+
export const backgroundGraph = createRoutine("BACKGROUND_GRAPH");
|
|
9
10
|
export const bulkUpdateStructures = createRoutine("BULK_UPDATE_STRUCTURES");
|
|
10
11
|
export const cancelPoll = createRoutine("CANCEL_POLL");
|
|
11
12
|
export const checkoutGrantRequest = createRoutine("CHECKOUT_GRANT_REQUEST");
|
|
@@ -66,6 +67,7 @@ export const deleteTag = createRoutine("DELETE_TAG");
|
|
|
66
67
|
export const deleteUserSearchFilter = createRoutine(
|
|
67
68
|
"DELETE_USER_SEARCH_FILTER",
|
|
68
69
|
);
|
|
70
|
+
export const dismissGraphGeneration = createRoutine("DISMISS_GRAPH_GENERATION");
|
|
69
71
|
export const doStructureNoteAction = createRoutine("DO_STRUCTURE_NOTE_ACTION");
|
|
70
72
|
export const downloadCsvGraph = createRoutine("DOWNLOAD_CSV_GRAPH");
|
|
71
73
|
export const downloadReferenceDataset = createRoutine(
|
|
@@ -104,6 +106,7 @@ export const fetchUserSearchFilters = createRoutine(
|
|
|
104
106
|
"FETCH_USER_SEARCH_FILTERS",
|
|
105
107
|
);
|
|
106
108
|
export const filterStructures = createRoutine("FILTER_STRUCTURES");
|
|
109
|
+
export const graphGeneration = createRoutine("GRAPH_GENERATION");
|
|
107
110
|
export const hoverNode = createRoutine("HOVER_NODE");
|
|
108
111
|
export const initSvg = createRoutine("INIT_SVG");
|
|
109
112
|
export const linkStructureToStructure = createRoutine(
|