@truedat/dd 4.40.14 → 4.41.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.41.2] 2022-03-30
4
+
5
+ ### Fixed
6
+
7
+ - [TD-4535] "Show more" on long Alert errors
8
+
9
+ ## [4.41.1] 2022-03-25
10
+
11
+ ### Changed
12
+
13
+ - [TD-4617] Use domainMenuSelector on editDomainForm
14
+
15
+ ## [4.41.0] 2022-03-25
16
+
17
+ ### Added
18
+
19
+ - [TD-4535] Structures domains CSV upload bulk update
20
+
3
21
  ## [4.40.14] 2022-03-22
4
22
 
5
23
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/dd",
3
- "version": "4.40.14",
3
+ "version": "4.41.2",
4
4
  "description": "Truedat Web Data Dictionary",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -34,7 +34,7 @@
34
34
  "@testing-library/jest-dom": "^5.14.1",
35
35
  "@testing-library/react": "^12.0.0",
36
36
  "@testing-library/user-event": "^13.2.1",
37
- "@truedat/test": "4.40.14",
37
+ "@truedat/test": "4.41.2",
38
38
  "babel-jest": "^27.0.6",
39
39
  "babel-plugin-dynamic-import-node": "^2.3.3",
40
40
  "babel-plugin-lodash": "^3.3.4",
@@ -84,9 +84,9 @@
84
84
  ]
85
85
  },
86
86
  "dependencies": {
87
- "@truedat/auth": "4.40.14",
88
- "@truedat/core": "4.40.14",
89
- "@truedat/df": "4.40.14",
87
+ "@truedat/auth": "4.41.2",
88
+ "@truedat/core": "4.41.2",
89
+ "@truedat/df": "4.41.2",
90
90
  "lodash": "^4.17.21",
91
91
  "path-to-regexp": "^1.7.0",
92
92
  "prop-types": "^15.7.2",
@@ -107,5 +107,5 @@
107
107
  "react-dom": ">= 16.8.6 < 17",
108
108
  "semantic-ui-react": ">= 0.88.2 < 2.1"
109
109
  },
110
- "gitHead": "11d6d5749f624fd3db6005f79f0df6ce05bc7793"
110
+ "gitHead": "de1e8cd9c744c0dae85be4585fccc1c50fdf6a07"
111
111
  }
package/src/api.js CHANGED
@@ -31,6 +31,8 @@ const API_STRUCTURE_TAG = "/api/data_structure_tags/:id";
31
31
  const API_STRUCTURE_TAGS = "/api/data_structure_tags";
32
32
  const API_STRUCTURE_TYPE = "/api/data_structure_types/:id";
33
33
  const API_STRUCTURE_TYPES = "/api/data_structure_types";
34
+ const API_STRUCTURES_DOMAINS_UPLOAD =
35
+ "/api/data_structures/bulk_upload_domains";
34
36
  const API_STRUCTURES_TAG = "/api/data_structures/:data_structure_id/tags/:id";
35
37
  const API_STRUCTURES_TAGS = "/api/data_structures/:data_structure_id/tags";
36
38
  const API_USER_SEARCH_FILTERS = "/api/user_search_filters";
@@ -79,6 +81,7 @@ export {
79
81
  API_USER_SEARCH_FILTERS,
80
82
  API_USER_SEARCH_FILTER,
81
83
  API_GET_USER_SEARCH_FILTERS,
84
+ API_STRUCTURES_DOMAINS_UPLOAD,
82
85
  API_STRUCTURES_TAG,
83
86
  API_STRUCTURES_TAGS,
84
87
  API_STRUCTURES_UPLOAD,
@@ -1,11 +1,19 @@
1
1
  import _ from "lodash/fp";
2
2
  import React, { useState } from "react";
3
- import { Button, Checkbox, Container, Form, Header } from "semantic-ui-react";
4
- import { DomainDropdownSelector } from "@truedat/bg/components";
3
+ import {
4
+ Button,
5
+ Checkbox,
6
+ Container,
7
+ Form,
8
+ Header,
9
+ Segment,
10
+ } from "semantic-ui-react";
5
11
  import { linkTo } from "@truedat/core/routes";
6
12
  import PropTypes from "prop-types";
7
13
  import { useIntl } from "react-intl";
8
14
  import { connect } from "react-redux";
15
+ import { DomainMenuSelector } from "@truedat/bg/taxonomy/components/DomainMenuSelector";
16
+ import { getDomainSelectorOptions } from "@truedat/bg/taxonomy/selectors";
9
17
  import { updateStructure } from "../routines";
10
18
 
11
19
  export const EditDomainForm = ({
@@ -13,6 +21,7 @@ export const EditDomainForm = ({
13
21
  title,
14
22
  updateStructure,
15
23
  structure,
24
+ domainOptions,
16
25
  }) => {
17
26
  const { formatMessage } = useIntl();
18
27
  const [domainIds, setDomainIds] = useState(structure?.domain_ids || []);
@@ -32,26 +41,26 @@ export const EditDomainForm = ({
32
41
  return (
33
42
  <Container style={{ width: "400px" }}>
34
43
  <Header as="h2" content={title} />
35
- <DomainDropdownSelector
36
- clearable={false}
37
- multiple={true}
38
- onChange={handleDomainSelected}
39
- name="domain"
40
- value={domainIds}
41
- />
42
-
43
- <Checkbox
44
- label={formatMessage({
45
- id: "structureDomain.actions.update.inherit",
46
- })}
47
- checked={inherit}
48
- onChange={(e) => {
49
- setInherit(!inherit);
50
- e.preventDefault();
51
- }}
52
- />
53
44
 
54
45
  <Form onSubmit={saving ? null : onSubmit}>
46
+ <Segment>
47
+ <DomainMenuSelector
48
+ domainOptions={domainOptions}
49
+ onChange={handleDomainSelected}
50
+ value={domainIds}
51
+ />
52
+ </Segment>
53
+
54
+ <Checkbox
55
+ label={formatMessage({
56
+ id: "structureDomain.actions.update.inherit",
57
+ })}
58
+ checked={inherit}
59
+ onChange={(e) => {
60
+ setInherit(!inherit);
61
+ e.preventDefault();
62
+ }}
63
+ />
55
64
  <div className="actions">
56
65
  <Button
57
66
  type="submit"
@@ -71,15 +80,19 @@ export const EditDomainForm = ({
71
80
  };
72
81
 
73
82
  EditDomainForm.propTypes = {
83
+ domainOptions: PropTypes.array,
74
84
  structure: PropTypes.object,
75
85
  updateStructure: PropTypes.func,
76
86
  title: PropTypes.string,
77
87
  saving: PropTypes.bool,
78
88
  };
79
89
 
80
- const mapStateToProps = (state) => ({
81
- structure: _.pick(["id", "domain_ids"])(state.structure),
82
- saving: state.structureUpdating,
83
- });
90
+ const mapStateToProps = (state) => {
91
+ return {
92
+ structure: _.pick(["id", "domain_ids"])(state.structure),
93
+ saving: state.structureUpdating,
94
+ domainOptions: getDomainSelectorOptions(state),
95
+ };
96
+ };
84
97
 
85
98
  export default connect(mapStateToProps, { updateStructure })(EditDomainForm);
@@ -0,0 +1,54 @@
1
+ import React from "react";
2
+ import PropTypes from "prop-types";
3
+ import { connect } from "react-redux";
4
+ import { Dropdown } from "semantic-ui-react";
5
+ import { useIntl } from "react-intl";
6
+ import { UploadModal } from "@truedat/core/components";
7
+ import { uploadStructuresDomains } from "../routines";
8
+
9
+ export const StructuresDomainsUploadOption = ({
10
+ uploadStructuresDomains,
11
+ loading,
12
+ }) => {
13
+ const { formatMessage } = useIntl();
14
+
15
+ return (
16
+ <UploadModal
17
+ icon="upload"
18
+ trigger={
19
+ <Dropdown.Item
20
+ icon="upload"
21
+ text={formatMessage({
22
+ id: "structures.actions.uploadDomains.tooltip",
23
+ })}
24
+ disabled={loading}
25
+ />
26
+ }
27
+ header={formatMessage({
28
+ id: "structures.actions.uploadDomains.confirmation.header",
29
+ })}
30
+ content={formatMessage({
31
+ id: "structures.actions.uploadDomains.confirmation.content",
32
+ })}
33
+ param={"structures_domains"}
34
+ handleSubmit={(data) =>
35
+ uploadStructuresDomains({
36
+ data,
37
+ })
38
+ }
39
+ />
40
+ );
41
+ };
42
+
43
+ StructuresDomainsUploadOption.propTypes = {
44
+ uploadStructuresDomains: PropTypes.func,
45
+ loading: PropTypes.bool,
46
+ };
47
+
48
+ const mapStateToProps = ({ uploadingStructuresDomainsFile }) => ({
49
+ loading: uploadingStructuresDomainsFile,
50
+ });
51
+
52
+ export default connect(mapStateToProps, { uploadStructuresDomains })(
53
+ StructuresDomainsUploadOption
54
+ );
@@ -6,10 +6,12 @@ import StructuresDownloadOption from "./StructuresDownloadOption";
6
6
  import StructuresEditableDownloadOption from "./StructuresEditableDownloadOption";
7
7
  import StructuresUpdateOption from "./StructuresUpdateOption";
8
8
  import StructuresUploadOption from "./StructuresUploadOption";
9
+ import StructuresDomainsUploadOption from "./StructuresDomainsUploadOption";
9
10
 
10
11
  export const StructuresOptions = ({
11
12
  autoPublish,
12
13
  bulkUpload,
14
+ bulkUploadDomains,
13
15
  hasFilterApplied,
14
16
  }) => (
15
17
  <Dropdown
@@ -26,6 +28,7 @@ export const StructuresOptions = ({
26
28
  {bulkUpload ? (
27
29
  <StructuresUploadOption canAutoPublish={!!autoPublish} />
28
30
  ) : null}
31
+ {bulkUploadDomains ? <StructuresDomainsUploadOption /> : null}
29
32
  <Dropdown.Divider />
30
33
  <StructuresUpdateOption />
31
34
  </Dropdown.Menu>
@@ -35,6 +38,7 @@ export const StructuresOptions = ({
35
38
  StructuresOptions.propTypes = {
36
39
  autoPublish: PropTypes.object,
37
40
  bulkUpload: PropTypes.object,
41
+ bulkUploadDomains: PropTypes.object,
38
42
  hasFilterApplied: PropTypes.bool,
39
43
  };
40
44
 
@@ -20,6 +20,7 @@ describe("<EditDomainForm />", () => {
20
20
  title: "title",
21
21
  updateStructure,
22
22
  structure,
23
+ domainOptions: domains,
23
24
  };
24
25
 
25
26
  const messages = {
@@ -29,6 +30,7 @@ describe("<EditDomainForm />", () => {
29
30
  "structureDomain.actions.update.inherit": "Apply on children",
30
31
  "domain.selector.label": "Domain",
31
32
  "domain.selector.placeholder": "Select a domain...",
33
+ "domain.multiple.placeholder": "Select domains ...",
32
34
  },
33
35
  };
34
36
 
@@ -13,6 +13,7 @@ const renderOpts = {
13
13
  "pendingStructureNotes.props.domains": "Domains",
14
14
  "structure.notes.status.draft": "Draft",
15
15
  "structure.notes.status.rejected": "Rejected",
16
+ "pendingStructureNotes.props.path": "Path",
16
17
  },
17
18
  },
18
19
  };
@@ -10,7 +10,12 @@ const renderOpts = {
10
10
  };
11
11
 
12
12
  describe("<StructuresOptions />", () => {
13
- const props = { bulkUpload: {}, bulkUpdate: {}, autoPublish: {} };
13
+ const props = {
14
+ bulkUpload: {},
15
+ bulkUpdate: {},
16
+ bulkUploadDomains: {},
17
+ autoPublish: {},
18
+ };
14
19
 
15
20
  it("matches the latest snapshot", async () => {
16
21
  const { container } = render(<StructuresOptions {...props} />, renderOpts);
@@ -11,107 +11,96 @@ exports[`<EditDomainForm /> matches the latest snapshot 1`] = `
11
11
  >
12
12
  title
13
13
  </h2>
14
- <div
15
- class="required field"
14
+ <form
15
+ class="ui form"
16
16
  >
17
- <label>
18
- Domain
19
- </label>
20
17
  <div
21
- class="field"
18
+ class="ui segment"
22
19
  >
23
20
  <div
24
- aria-expanded="false"
25
- class="ui fluid multiple search selection dropdown"
26
- name="domain"
27
- role="combobox"
21
+ class="field"
28
22
  >
29
- <input
30
- aria-autocomplete="list"
31
- autocomplete="off"
32
- class="search"
33
- tabindex="0"
34
- type="text"
35
- value=""
36
- />
37
- <span
38
- class="sizer"
39
- />
40
- <div
41
- aria-atomic="true"
42
- aria-live="polite"
43
- class="divider default text"
44
- role="alert"
45
- >
46
- Select a domain...
47
- </div>
48
- <i
49
- aria-hidden="true"
50
- class="dropdown icon"
51
- />
52
23
  <div
24
+ aria-expanded="false"
53
25
  aria-multiselectable="true"
54
- class="menu transition"
26
+ class="ui floating multiple dropdown"
55
27
  role="listbox"
28
+ tabindex="0"
56
29
  >
30
+ <label>
31
+ Select domains ...
32
+ </label>
33
+ <i
34
+ aria-hidden="true"
35
+ class="dropdown icon"
36
+ />
57
37
  <div
58
- aria-checked="false"
59
- aria-selected="true"
60
- class="selected item"
61
- role="option"
62
- style="pointer-events: all;"
38
+ class="menu transition"
63
39
  >
64
40
  <div
65
- class="text"
66
- style="margin-left: 0px;"
41
+ class="ui left icon input search"
67
42
  >
43
+ <input
44
+ type="text"
45
+ />
68
46
  <i
69
47
  aria-hidden="true"
70
- class="icon"
48
+ class="search icon"
71
49
  />
72
- Domain 1
73
50
  </div>
74
- </div>
75
- <div
76
- aria-checked="false"
77
- aria-selected="false"
78
- class="item"
79
- role="option"
80
- style="pointer-events: all;"
81
- >
82
51
  <div
83
- class="text"
84
- style="margin-left: 0px;"
52
+ class="scrolling menu transition"
85
53
  >
86
- <i
87
- aria-hidden="true"
88
- class="icon"
89
- />
90
- Domain 2
54
+ <div
55
+ aria-selected="false"
56
+ class="item"
57
+ role="option"
58
+ >
59
+ <div
60
+ style="margin-left: 0px;"
61
+ >
62
+ <i
63
+ aria-hidden="true"
64
+ class="icon"
65
+ />
66
+ Domain 1
67
+ </div>
68
+ </div>
69
+ <div
70
+ aria-selected="false"
71
+ class="item"
72
+ role="option"
73
+ >
74
+ <div
75
+ style="margin-left: 0px;"
76
+ >
77
+ <i
78
+ aria-hidden="true"
79
+ class="icon"
80
+ />
81
+ Domain 2
82
+ </div>
83
+ </div>
91
84
  </div>
92
85
  </div>
93
86
  </div>
94
87
  </div>
95
88
  </div>
96
- </div>
97
- <div
98
- class="ui checked checkbox"
99
- >
100
- <input
101
- checked=""
102
- class="hidden"
103
- readonly=""
104
- tabindex="0"
105
- type="checkbox"
106
- value=""
107
- />
108
- <label>
109
- Apply on children
110
- </label>
111
- </div>
112
- <form
113
- class="ui form"
114
- >
89
+ <div
90
+ class="ui checked checkbox"
91
+ >
92
+ <input
93
+ checked=""
94
+ class="hidden"
95
+ readonly=""
96
+ tabindex="0"
97
+ type="checkbox"
98
+ value=""
99
+ />
100
+ <label>
101
+ Apply on children
102
+ </label>
103
+ </div>
115
104
  <div
116
105
  class="actions"
117
106
  >
@@ -34,7 +34,7 @@ exports[`<PendingStructureNotesTable /> matches the latest snapshot 1`] = `
34
34
  <th
35
35
  class=""
36
36
  >
37
- pendingStructureNotes.props.path
37
+ Path
38
38
  </th>
39
39
  </tr>
40
40
  </thead>
@@ -68,6 +68,20 @@ exports[`<StructuresOptions /> matches the latest snapshot 1`] = `
68
68
  Upload structure notes
69
69
  </span>
70
70
  </div>
71
+ <div
72
+ class="item"
73
+ role="option"
74
+ >
75
+ <i
76
+ aria-hidden="true"
77
+ class="upload icon"
78
+ />
79
+ <span
80
+ class="text"
81
+ >
82
+ Update structure domains
83
+ </span>
84
+ </div>
71
85
  <div
72
86
  class="divider"
73
87
  />
@@ -371,9 +371,13 @@ export default {
371
371
  "structures.actions.new_system": "New system",
372
372
  "structures.actions.upload": "Upload file",
373
373
  "structures.actions.upload.confirmation.content": "Notes",
374
+ "structures.actions.uploadDomains.confirmation.content": "Structure domains",
374
375
  "structures.actions.upload.confirmation.header":
375
376
  "Upload notes file of structures",
377
+ "structures.actions.uploadDomains.confirmation.header":
378
+ "Upload structures domains file",
376
379
  "structures.actions.upload.tooltip": "Upload structure notes",
380
+ "structures.actions.uploadDomains.tooltip": "Update structure domains",
377
381
  "structures.bulkUpdate.crumbs.top": "Bulk update",
378
382
  "structures.bulkUpdate.success.content":
379
383
  "Updated {count} structures successfully",
@@ -391,12 +395,25 @@ export default {
391
395
  "Bulk update completed. Loaded {count_ids} errors {count_errors}",
392
396
  "structures.bulkUpdateStructures.success.errors":
393
397
  "Error in external id {externalId}, message: {message}",
398
+ "structures.bulkUploadStructuresDomains.success.errors.domain: not_exist":
399
+ "Error in line {row}, in external id {externalId}, domain does not exist",
400
+ "structures.bulkUploadStructuresDomains.success.errors.update_domain: forbbiden":
401
+ "Error in line {row}, in external id {externalId}, update forbbiden",
402
+ "structures.bulkUploadStructuresDomains.success.errors.domain_ids: must be a non-empty list":
403
+ "Error in line {row}, in external id {externalId}, domain_ids must be a non-empty list",
404
+ "structures.bulkUploadStructuresDomains.success.errors.structure: not_exist":
405
+ "Error in line {row}, in external id {externalId}, data structure does not exist",
394
406
  "structures.bulkUploadStructures.success.errors":
395
407
  "Error in line {row}, in external id {externalId}, message: {message}",
408
+ "structures.bulkUploadStructuresDomains.success.errors":
409
+ "Error in line {row}, in external id {externalId}, message: {message}",
396
410
  "structures.bulkUpdateStructures.success.unchanged":
397
411
  "{unchanged} structures remained unchanged.",
398
412
  "structures.bulkUpdateStructures.success.updated":
399
413
  "Updated {changed} structures successfully.",
414
+ "structures.bulkUploadStructuresDomains.success.header":
415
+ "Bulk update completed. Loaded {count_ids} errors {count_errors}",
416
+
400
417
  "structures.crumbs.top": "Structures",
401
418
  "structures.edit.bulk.confirmation.content":
402
419
  "{structures_count} structures will be updated. Are you sure?",
@@ -445,6 +462,7 @@ export default {
445
462
  "structures.uploadStructures.failed.external_id_not_exist":
446
463
  "Invalid External Id",
447
464
  "structures.uploadStructures.failed.header": "Upload Failed!",
465
+ "structures.uploadStructuresDomains.failed.header": "Upload Failed!",
448
466
  "structures.uploadStructures.failed.invalid_file_format":
449
467
  "Invalid file format",
450
468
  "structures.uploadStructures.failed.note.df_content.default":
@@ -375,9 +375,15 @@ export default {
375
375
  "structures.actions.new_system": "Nuevo sistema",
376
376
  "structures.actions.upload": "Subir fichero",
377
377
  "structures.actions.upload.confirmation.content": "Notas",
378
+ "structures.actions.uploadDomains.confirmation.content":
379
+ "Dominios de estructuras",
378
380
  "structures.actions.upload.confirmation.header":
379
381
  "Subir fichero de notas de estructuras",
382
+ "structures.actions.uploadDomains.confirmation.header":
383
+ "Subir fichero de dominios de estructuras",
380
384
  "structures.actions.upload.tooltip": "Subir notas de estructuras",
385
+ "structures.actions.uploadDomains.tooltip":
386
+ "Actualizar dominios de estructuras",
381
387
  "structures.bulkUpdate.crumbs.top": "Actualización masiva",
382
388
  "structures.bulkUpdate.success.content":
383
389
  "Se han actualizado {count} estructuras",
@@ -395,12 +401,24 @@ export default {
395
401
  "¡Actualización finalizada! subidos {count_ids} errores {count_errors}",
396
402
  "structures.bulkUpdateStructures.success.errors":
397
403
  "Error en external id {externalId}, mensaje: {message}",
404
+ "structures.bulkUploadStructuresDomains.success.errors.domain: not_exist":
405
+ "Error en linea {row}, en external id {externalId}, el dominio no existe",
406
+ "structures.bulkUploadStructuresDomains.success.errors.structure: not_exist":
407
+ "Error en linea {row}, en external id {externalId}, la estructura no existe",
408
+ "structures.bulkUploadStructuresDomains.success.errors.update_domain: forbbiden":
409
+ "Error en linea {row}, en external id {externalId}, no tiene permisos para actualizar",
410
+ "structures.bulkUploadStructuresDomains.success.errors.domain_ids: must be a non-empty list":
411
+ "Error en linea {row}, en external id {externalId}, domain_ids no puede ser una lista vacia",
398
412
  "structures.bulkUploadStructures.success.errors":
399
413
  "Error en linea {row}, en external id {externalId} mensaje: {message}",
414
+ "structures.bulkUploadStructuresDomains.success.errors":
415
+ "Error en linea {row}, en external id {externalId} mensaje: {message}",
400
416
  "structures.bulkUpdateStructures.success.unchanged":
401
417
  "{unchanged} se han mantenido sin cambios.",
402
418
  "structures.bulkUpdateStructures.success.updated":
403
419
  "Se han actualizado {changed} estructuras.",
420
+ "structures.bulkUploadStructuresDomains.success.header":
421
+ "¡Actualización finalizada! subidos {count_ids} errores {count_errors}",
404
422
  "structures.crumbs.top": "Estructuras",
405
423
  "structures.edit.bulk.confirmation.content":
406
424
  "Se van a actualizar {structures_count} estructuras. ¿Estás seguro?",
@@ -453,6 +471,8 @@ export default {
453
471
  "External Id Inválido",
454
472
  "structures.uploadStructures.failed.header":
455
473
  "Error al subir el fichero. El contenido no se ha actualizado",
474
+ "structures.uploadStructuresDomains.failed.header":
475
+ "Error al subir el fichero. El contenido no se ha actualizado",
456
476
  "structures.uploadStructures.failed.invalid_file_format":
457
477
  "Formato de fichero inválido",
458
478
  "structures.uploadStructures.failed.note.df_content.default":
@@ -0,0 +1,73 @@
1
+ import { uploadStructuresDomains } from "../../routines";
2
+ import { ddMessage } from "..";
3
+
4
+ const fooState = { foo: "bar" };
5
+
6
+ describe("reducers: ddMessage", () => {
7
+ const initialState = null;
8
+
9
+ it("should provide the initial state", () => {
10
+ expect(ddMessage(undefined, {})).toEqual(initialState);
11
+ });
12
+
13
+ it("should handle the uploadStructuresDomains.SUCCESS action returning some errored rows", () => {
14
+ const ddMsg = ddMessage(fooState, {
15
+ type: uploadStructuresDomains.SUCCESS,
16
+ payload: {
17
+ data: {
18
+ errors: [
19
+ {
20
+ external_id:
21
+ "postgres://Postgres-Test/postgres/public/city/city_id",
22
+ message: {
23
+ external_domain_ids: ["must exist: UEUEUEU"],
24
+ },
25
+ row: 3,
26
+ },
27
+ {
28
+ external_id:
29
+ "postgres://Postgres-Test/postgres/public/city/city_name",
30
+ message: {
31
+ external_domain_ids: ["must exist: NANDAKA, JAJAJA"],
32
+ },
33
+ row: 4,
34
+ },
35
+ ],
36
+ ids: [5375339],
37
+ },
38
+ },
39
+ });
40
+
41
+ expect(ddMsg).toMatchObject({
42
+ error: false,
43
+ header: "structures.bulkUploadStructuresDomains.success.header",
44
+ icon: "check",
45
+ color: "orange",
46
+ fields: { count_errors: 2, count_ids: 1 },
47
+ });
48
+ const { messages: errors } = ddMsg;
49
+ expect(errors.length).toEqual(2);
50
+ });
51
+
52
+ it("should handle the uploadStructuresDomains.FAILURE action", () => {
53
+ const ddMsg = ddMessage(fooState, {
54
+ type: uploadStructuresDomains.FAILURE,
55
+ payload: {
56
+ status: 422,
57
+ data: {
58
+ error: {
59
+ message:
60
+ "invalid_headers, must be: external_id, domain_external_ids",
61
+ },
62
+ },
63
+ },
64
+ });
65
+
66
+ expect(ddMsg).toMatchObject({
67
+ error: true,
68
+ header: "structures.uploadStructuresDomains.failed.header",
69
+ icon: "attention",
70
+ content: "invalid_headers, must be: external_id, domain_external_ids",
71
+ });
72
+ });
73
+ });
@@ -4,6 +4,7 @@ import {
4
4
  bulkUpdateStructures,
5
5
  uploadStructures,
6
6
  updateStructureType,
7
+ uploadStructuresDomains,
7
8
  } from "../routines";
8
9
 
9
10
  const initialState = null;
@@ -18,10 +19,7 @@ const ddMessage = (state = initialState, { type, payload }) => {
18
19
  ? _.flow(
19
20
  _.propOr([], "errors"),
20
21
  _.map((error) => ({
21
- id:
22
- type == bulkUpdateStructures.SUCCESS
23
- ? `structures.bulkUpdateStructures.failed.note.${error.message}`
24
- : `structures.uploadStructures.failed.note.${error.message}`,
22
+ id: `structures.bulkUpdateStructures.failed.note.${error.message}`,
25
23
  fields: {
26
24
  row: error.row,
27
25
  id: error.id,
@@ -29,10 +27,7 @@ const ddMessage = (state = initialState, { type, payload }) => {
29
27
  externalId: error.external_id,
30
28
  message: error.message,
31
29
  },
32
- defaultMessage:
33
- type == bulkUpdateStructures.SUCCESS
34
- ? "structures.bulkUpdateStructures.success.errors"
35
- : "structures.bulkUploadStructures.success.errors",
30
+ defaultMessage: "structures.bulkUploadStructures.success.errors",
36
31
  }))
37
32
  )(payload.data)
38
33
  : "";
@@ -51,6 +46,55 @@ const ddMessage = (state = initialState, { type, payload }) => {
51
46
  initialMaxListItems: 5,
52
47
  };
53
48
  }
49
+
50
+ case uploadStructuresDomains.SUCCESS: {
51
+ const messagesImp = _.has("errors")(payload.data)
52
+ ? _.flow(
53
+ _.propOr([], "errors"),
54
+ _.map((error) => {
55
+ const errorMessage = Object.entries(error.message)
56
+ .map(([field, messages]) => `${field}: ${messages.join(", ")}`)
57
+ .join(" | ");
58
+ return {
59
+ id: `structures.bulkUploadStructuresDomains.success.errors.${errorMessage}`,
60
+ fields: {
61
+ row: error.row,
62
+ id: error.id,
63
+ field: error.field,
64
+ externalId: error.external_id,
65
+ message: errorMessage,
66
+ },
67
+ };
68
+ })
69
+ )(payload.data)
70
+ : "";
71
+ return {
72
+ error: false,
73
+ header: "structures.bulkUploadStructuresDomains.success.header",
74
+ icon: "check",
75
+ color: payload.data.errors.length >= 1 ? "orange" : "green",
76
+ text: "",
77
+ messages: messagesImp,
78
+ fields: {
79
+ count_ids: payload.data.ids.length,
80
+ count_errors: payload.data.errors.length,
81
+ },
82
+ downloadable: true,
83
+ initialMaxListItems: 5,
84
+ };
85
+ }
86
+
87
+ case uploadStructuresDomains.FAILURE:
88
+ return payload.status !== 422
89
+ ? state
90
+ : {
91
+ error: true,
92
+ header: "structures.uploadStructuresDomains.failed.header",
93
+ icon: "attention",
94
+ content: payload?.data?.error?.message,
95
+ initialMaxListItems: 5,
96
+ };
97
+
54
98
  case updateStructureType.SUCCESS:
55
99
  return {
56
100
  error: false,
package/src/routines.js CHANGED
@@ -132,6 +132,9 @@ export const updateStructureTag = createRoutine("UPDATE_STRUCTURE_TAG");
132
132
  export const updateStructureType = createRoutine("UPDATE_STRUCTURE_TYPE");
133
133
  export const updateSystem = createRoutine("UPDATE_SYSTEM");
134
134
  export const uploadStructures = createRoutine("UPLOAD_STRUCTURES");
135
+ export const uploadStructuresDomains = createRoutine(
136
+ "UPLOAD_STRUCTURES_DOMAINS"
137
+ );
135
138
  export const addGrantRequestToCart = createRoutine("ADD_GRANT_REQUEST_TO_CART");
136
139
  export const removeGrantRequestFromCart = createRoutine(
137
140
  "REMOVE_GRANT_REQUEST_FROM_CART"
@@ -0,0 +1,81 @@
1
+ import { testSaga } from "redux-saga-test-plan";
2
+ import { apiJsonPost, UPLOAD_JSON_OPTS } from "@truedat/core/services/api";
3
+ import {
4
+ uploadStructuresDomainsRequestSaga,
5
+ uploadStructuresDomainsSaga,
6
+ } from "../uploadStructuresDomains";
7
+ import { uploadStructuresDomains } from "../../routines";
8
+ import { API_STRUCTURES_DOMAINS_UPLOAD } from "../../api";
9
+
10
+ describe("sagas: uploadStructuresDomainsRequestSaga", () => {
11
+ it("should invoke uploadStructuresDomainsSaga on trigger", () => {
12
+ expect(() => {
13
+ testSaga(uploadStructuresDomainsRequestSaga)
14
+ .next()
15
+ .takeLatest(
16
+ uploadStructuresDomains.TRIGGER,
17
+ uploadStructuresDomainsSaga
18
+ )
19
+ .finish()
20
+ .isDone();
21
+ }).not.toThrow();
22
+ });
23
+
24
+ it("should throw exception if an unhandled action is received", () => {
25
+ expect(() => {
26
+ testSaga(uploadStructuresDomainsRequestSaga)
27
+ .next()
28
+ .takeLatest("FOO", uploadStructuresDomainsSaga);
29
+ }).toThrow();
30
+ });
31
+ });
32
+
33
+ describe("sagas: uploadStructuresDomainsSaga", () => {
34
+ const body = { structures_domains: "content" };
35
+ const payload = { data: body };
36
+ const data = { message: [1, 2] };
37
+ it("should put a success action when a response is returned", () => {
38
+ expect(() => {
39
+ testSaga(uploadStructuresDomainsSaga, { payload })
40
+ .next()
41
+ .put(uploadStructuresDomains.request())
42
+ .next()
43
+ .call(
44
+ apiJsonPost,
45
+ API_STRUCTURES_DOMAINS_UPLOAD,
46
+ body,
47
+ UPLOAD_JSON_OPTS
48
+ )
49
+ .next({ data })
50
+ .put(uploadStructuresDomains.success({ data }))
51
+ .next()
52
+ .put(uploadStructuresDomains.fulfill())
53
+ .next()
54
+ .isDone();
55
+ }).not.toThrow();
56
+ });
57
+
58
+ it("should put a failure action when the call returns an error", () => {
59
+ const message = "Request failed";
60
+ const error = { message };
61
+
62
+ expect(() => {
63
+ testSaga(uploadStructuresDomainsSaga, { payload })
64
+ .next()
65
+ .put(uploadStructuresDomains.request())
66
+ .next()
67
+ .call(
68
+ apiJsonPost,
69
+ API_STRUCTURES_DOMAINS_UPLOAD,
70
+ body,
71
+ UPLOAD_JSON_OPTS
72
+ )
73
+ .throw(error)
74
+ .put(uploadStructuresDomains.failure(message))
75
+ .next()
76
+ .put(uploadStructuresDomains.fulfill())
77
+ .next()
78
+ .isDone();
79
+ }).not.toThrow();
80
+ });
81
+ });
@@ -46,6 +46,7 @@ import { updateStructureTagRequestSaga } from "./updateStructureTag";
46
46
  import { updateStructureTypeRequestSaga } from "./updateStructureType";
47
47
  import { updateSystemRequestSaga } from "./updateSystem";
48
48
  import { uploadStructuresRequestSaga } from "./uploadStructures";
49
+ import { uploadStructuresDomainsRequestSaga } from "./uploadStructuresDomains";
49
50
  import { operateGrantRequestToCartRequestSaga } from "./operateGrantRequestCart";
50
51
 
51
52
  export {
@@ -97,6 +98,7 @@ export {
97
98
  updateStructureTypeRequestSaga,
98
99
  updateSystemRequestSaga,
99
100
  uploadStructuresRequestSaga,
101
+ uploadStructuresDomainsRequestSaga,
100
102
  operateGrantRequestToCartRequestSaga,
101
103
  };
102
104
 
@@ -149,5 +151,6 @@ export default [
149
151
  updateStructureTypeRequestSaga(),
150
152
  updateSystemRequestSaga(),
151
153
  uploadStructuresRequestSaga(),
154
+ uploadStructuresDomainsRequestSaga(),
152
155
  operateGrantRequestToCartRequestSaga(),
153
156
  ];
@@ -0,0 +1,35 @@
1
+ import { call, put, takeLatest } from "redux-saga/effects";
2
+ import { apiJsonPost, UPLOAD_JSON_OPTS } from "@truedat/core/services/api";
3
+ import { uploadStructuresDomains } from "../routines";
4
+ import { API_STRUCTURES_DOMAINS_UPLOAD } from "../api";
5
+
6
+ export function* uploadStructuresDomainsSaga({ payload }) {
7
+ try {
8
+ const { data: body } = payload;
9
+ yield put(uploadStructuresDomains.request());
10
+ const { data } = yield call(
11
+ apiJsonPost,
12
+ API_STRUCTURES_DOMAINS_UPLOAD,
13
+ body,
14
+ UPLOAD_JSON_OPTS
15
+ );
16
+
17
+ yield put(uploadStructuresDomains.success({ data }));
18
+ } catch (error) {
19
+ if (error.response) {
20
+ const { status, data } = error.response;
21
+ yield put(uploadStructuresDomains.failure({ status, data }));
22
+ } else {
23
+ yield put(uploadStructuresDomains.failure(error.message));
24
+ }
25
+ } finally {
26
+ yield put(uploadStructuresDomains.fulfill());
27
+ }
28
+ }
29
+
30
+ export function* uploadStructuresDomainsRequestSaga() {
31
+ yield takeLatest(
32
+ uploadStructuresDomains.TRIGGER,
33
+ uploadStructuresDomainsSaga
34
+ );
35
+ }