@truedat/dd 4.35.4 → 4.35.8

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,10 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.35.8] 2022-01-07
4
+
5
+ ### Added
6
+
7
+ - [TD-4390] Support for alias on Implementation
8
+
9
+ ## [4.35.7] 2021-01-07
10
+
11
+ ### Fixed
12
+
13
+ - [TD-4413]:
14
+ - Fix impact tab not displaying data, affecting lineage tab
15
+ - Fix LineageContextMenu inside impact and lineage tabs not redirecting to /graphs
16
+
3
17
  ## [4.35.4] 2021-12-20
4
18
 
5
19
  ### Fixed
6
20
 
7
- - # [TD-4356] Improve structure types view performance by doing optional the metadata fields column
21
+ - [TD-4356] Improve structure types view performance by doing optional the metadata fields column
8
22
 
9
23
  ## [4.35.3] 2021-12-20
10
24
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/dd",
3
- "version": "4.35.4",
3
+ "version": "4.35.8",
4
4
  "description": "Truedat Web Data Dictionary",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -84,9 +84,9 @@
84
84
  ]
85
85
  },
86
86
  "dependencies": {
87
- "@truedat/auth": "4.35.4",
88
- "@truedat/core": "4.35.4",
89
- "@truedat/df": "4.35.4",
87
+ "@truedat/auth": "4.35.8",
88
+ "@truedat/core": "4.35.8",
89
+ "@truedat/df": "4.35.8",
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": "b03fba26635e365bbab64a6de1dd9a155925b5d1"
110
+ "gitHead": "35a51ffb36f357fe814258d51420d5ace5b3cd63"
111
111
  }
@@ -26,7 +26,7 @@ export const GraphLoader = ({ clearGraph, fetchGraph, graph, cancelPoll }) => {
26
26
  fetchGraph({ id });
27
27
  }
28
28
  }
29
- }, [/*graph, */ /*fetchGraph,*/ id]);
29
+ }, [graph, fetchGraph, id]);
30
30
 
31
31
  useEffect(() => {
32
32
  return () => {
@@ -5,23 +5,31 @@ import { connect } from "react-redux";
5
5
  import { useHistory, useLocation } from "react-router-dom";
6
6
  import { clearRedirect } from "@truedat/core/routines";
7
7
 
8
- export const GraphRedirector = ({ graphRedirect, clearRedirect }) => {
8
+ export const GraphRedirector = ({ graph, graphRedirect, clearRedirect }) => {
9
9
  const { pathname } = useLocation();
10
10
  const history = useHistory();
11
11
  useEffect(() => {
12
- if (graphRedirect && !_.isEqual(pathname, graphRedirect)) {
12
+ if (
13
+ graphRedirect &&
14
+ graph?.fromLineageContextMenu &&
15
+ !_.isEqual(pathname, graphRedirect)
16
+ ) {
13
17
  history.push(graphRedirect);
14
18
  clearRedirect();
15
19
  }
16
- }, [pathname, graphRedirect]);
20
+ }, [history, pathname, graphRedirect, graph, clearRedirect]);
17
21
  return null;
18
22
  };
19
23
 
20
24
  GraphRedirector.propTypes = {
25
+ graph: PropTypes.object,
21
26
  clearRedirect: PropTypes.func.isRequired,
22
27
  graphRedirect: PropTypes.string,
23
28
  };
24
29
 
25
- const mapStateToProps = ({ graphRedirect }) => ({ graphRedirect });
30
+ const mapStateToProps = ({ graph, graphRedirect }) => ({
31
+ graph,
32
+ graphRedirect,
33
+ });
26
34
 
27
35
  export default connect(mapStateToProps, { clearRedirect })(GraphRedirector);
@@ -23,7 +23,12 @@ export const LineageContextMenu = ({
23
23
  <>
24
24
  <Menu.Item
25
25
  onClick={() =>
26
- createGraph({ ids: [id], levels: levels, type: "lineage" })
26
+ createGraph({
27
+ ids: [id],
28
+ levels: levels,
29
+ type: "lineage",
30
+ fromLineageContextMenu: true,
31
+ })
27
32
  }
28
33
  >
29
34
  <Icon name="random" flipped="horizontally" />
@@ -34,7 +39,12 @@ export const LineageContextMenu = ({
34
39
  </Menu.Item>
35
40
  <Menu.Item
36
41
  onClick={() =>
37
- createGraph({ ids: [id], levels: levels, type: "impact" })
42
+ createGraph({
43
+ ids: [id],
44
+ levels: levels,
45
+ type: "impact",
46
+ fromLineageContextMenu: true,
47
+ })
38
48
  }
39
49
  >
40
50
  <Icon name="random" />
@@ -43,7 +53,9 @@ export const LineageContextMenu = ({
43
53
  defaultMessage="Impact"
44
54
  />
45
55
  </Menu.Item>
46
- <Menu.Item onClick={() => excludeNode({ id })}>
56
+ <Menu.Item
57
+ onClick={() => excludeNode({ id, fromLineageContextMenu: true })}
58
+ >
47
59
  <Icon name="eye slash outline" />
48
60
  <FormattedMessage
49
61
  id="navigation.lineage.exclude"
@@ -7,7 +7,7 @@ import GraphRedirector from "./GraphRedirector";
7
7
  export const LineageSample = ({ createGraph }) => {
8
8
  useEffect(() => {
9
9
  createGraph({ type: "sample" });
10
- }, []);
10
+ }, [createGraph]);
11
11
  return <GraphRedirector />;
12
12
  };
13
13
 
@@ -8,6 +8,7 @@ import { getStructuresFields } from "../selectors/getStructuresFields";
8
8
 
9
9
  export const StructureFieldsDropdown = ({
10
10
  label,
11
+ fluid,
11
12
  inline,
12
13
  onSelectField,
13
14
  structureFields,
@@ -17,22 +18,33 @@ export const StructureFieldsDropdown = ({
17
18
  }) => {
18
19
  const { formatMessage } = useIntl();
19
20
  const onClick = ({ value, modifier }) => {
20
- const field = _.find(_.propEq("data_structure_id", value))(structureFields);
21
+ const [data_structure_id, parent_index] = _.isString(value)
22
+ ? value.split("/").map(Number)
23
+ : [value, undefined];
24
+ const field = _.isNil(parent_index)
25
+ ? _.find({ data_structure_id: data_structure_id })(structureFields)
26
+ : _.find({
27
+ data_structure_id: data_structure_id,
28
+ parent_index: parent_index,
29
+ })(structureFields);
21
30
  onSelectField(field, modifier);
22
31
  };
23
32
 
24
33
  const formatOption = (field) => {
34
+ const value = _.isNil(field?.parent_index)
35
+ ? field.data_structure_id
36
+ : `${field.data_structure_id}/${field.parent_index}`;
25
37
  const modifier = _.find({ sourceType: field.field_type })(
26
38
  typeCastModifiers
27
39
  );
28
40
  if (modifier) {
29
41
  return {
30
- key: field.data_structure_id,
42
+ key: value,
31
43
  text: `${field.name} (${formatMessage({
32
44
  id: `filtersGrid.field.modifier.${modifier.name}`,
33
45
  })})`,
34
46
  modifier,
35
- value: field.data_structure_id,
47
+ value: value,
36
48
  icon: formatMessage({
37
49
  id: `field_type.icon.${field.field_type}`,
38
50
  defaultMessage: "hashtag",
@@ -41,9 +53,9 @@ export const StructureFieldsDropdown = ({
41
53
  }
42
54
 
43
55
  return {
44
- key: field.data_structure_id,
56
+ key: value,
45
57
  text: field.name,
46
- value: field.data_structure_id,
58
+ value: value,
47
59
  icon: formatMessage({
48
60
  id: `field_type.icon.${field.field_type}`,
49
61
  defaultMessage: "hashtag",
@@ -88,6 +100,7 @@ export const StructureFieldsDropdown = ({
88
100
 
89
101
  return (
90
102
  <OptionGroup
103
+ fluid={fluid}
91
104
  inline={inline}
92
105
  label={label}
93
106
  value={value}
@@ -102,6 +115,7 @@ export const StructureFieldsDropdown = ({
102
115
 
103
116
  StructureFieldsDropdown.propTypes = {
104
117
  label: PropTypes.string,
118
+ fluid: PropTypes.bool,
105
119
  inline: PropTypes.bool,
106
120
  value: PropTypes.number,
107
121
  structureFields: PropTypes.array,
@@ -3,6 +3,7 @@ import React, { useEffect } from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { connect } from "react-redux";
5
5
  import { createGraph, fetchStructureGraph } from "../routines";
6
+ import GraphRedirector from "./GraphRedirector";
6
7
  import LineageGraph from "./LineageGraph";
7
8
 
8
9
  export const StructureGraph = ({
@@ -16,11 +17,16 @@ export const StructureGraph = ({
16
17
  }) => {
17
18
  useEffect(() => {
18
19
  degree && _.isEmpty(graph) && createGraph({ ids: [id], type: type });
19
- }, [type]);
20
+ }, [degree, graph, id, type, createGraph]);
20
21
  useEffect(() => {
21
22
  graphId && _.isEmpty(graph) && fetchStructureGraph({ id: graphId });
22
- }, [graphId]);
23
- return degree ? <LineageGraph height="50vh" graph={graph} /> : null;
23
+ }, [graph, graphId, fetchStructureGraph]);
24
+ return degree ? (
25
+ <>
26
+ <GraphRedirector />
27
+ <LineageGraph height="50vh" graph={graph} />
28
+ </>
29
+ ) : null;
24
30
  };
25
31
 
26
32
  StructureGraph.propTypes = {
@@ -18,7 +18,10 @@ export const StructureLineage = (props) => (
18
18
  </>
19
19
  );
20
20
 
21
- const mapStateToProps = ({ structureLineageId: graphId, graph: graph }) => ({
21
+ const mapStateToProps = ({
22
+ structureLineageId: graphId,
23
+ structureLineageGraph: graph,
24
+ }) => ({
22
25
  graph,
23
26
  graphId,
24
27
  type: "lineage",
@@ -34,6 +34,12 @@ export const StructureSelectorInputField = ({
34
34
  onChangeField({ ...selectedStructure, clauses });
35
35
  };
36
36
 
37
+ const aliasIsNeeded =
38
+ _.flow(
39
+ _.map(_.path("structure.id")),
40
+ _.filter((s) => s === selectedStructure?.structure?.id)
41
+ )(structures).length > 1;
42
+
37
43
  return (
38
44
  <>
39
45
  <Form.Field>
@@ -74,6 +80,30 @@ export const StructureSelectorInputField = ({
74
80
  />
75
81
  )}
76
82
  </Form.Field>
83
+ {aliasIsNeeded && (
84
+ <Container style={{ paddingLeft: "8em" }}>
85
+ <Form.Field>
86
+ <label>
87
+ {formatMessage({
88
+ id: `ruleImplementation.props.structure.alias`,
89
+ })}
90
+ </label>
91
+ <Form.Input
92
+ width={fieldWidth ? fieldWidth : 7}
93
+ value={selectedStructure?.alias?.text || ""}
94
+ onChange={(e) =>
95
+ onChangeField({
96
+ ...selectedStructure,
97
+ alias: {
98
+ ...(selectedStructure?.alias || {}),
99
+ text: e.target.value,
100
+ },
101
+ })
102
+ }
103
+ />
104
+ </Form.Field>
105
+ </Container>
106
+ )}
77
107
  {joined && _.prop("structure")(selectedStructure) && (
78
108
  <Container style={{ paddingLeft: "8em" }}>
79
109
  <Form.Field>
@@ -2,10 +2,10 @@ import _ from "lodash/fp";
2
2
  import React from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { useIntl } from "react-intl";
5
- import { Form, Icon, Popup } from "semantic-ui-react";
5
+ import { Grid, Icon, Popup } from "semantic-ui-react";
6
6
  import StructureFieldsDropdown from "./StructureFieldsDropdown";
7
7
 
8
- const getClauses = selectedStructure => {
8
+ const getClauses = (selectedStructure) => {
9
9
  const clauses = _.getOr([], "clauses")(selectedStructure);
10
10
  return _.isEmpty(clauses) ? [{}] : clauses;
11
11
  };
@@ -14,15 +14,21 @@ const getParentStructures = (structures, selectedStructure) => {
14
14
  return structures && selectedStructure
15
15
  ? _.flow(
16
16
  _.filter(
17
- s =>
18
- _.path("structure.id")(s) !==
19
- _.path("structure.id")(selectedStructure)
17
+ (s) =>
18
+ _.path("alias.index")(s) !==
19
+ _.path("alias.index")(selectedStructure)
20
20
  ),
21
21
  _.defaultTo([])
22
22
  )(structures)
23
23
  : [];
24
24
  };
25
25
 
26
+ const structureToFieldDropdown = (structures) =>
27
+ _.map((s) => ({
28
+ ..._.prop("structure")(s),
29
+ alias: _.prop("alias")(s),
30
+ }))(structures);
31
+
26
32
  const JoinClause = ({
27
33
  createClause,
28
34
  deleteClause,
@@ -31,33 +37,43 @@ const JoinClause = ({
31
37
  left,
32
38
  parentStructures,
33
39
  right,
34
- selectedStructure
40
+ selectedStructure,
35
41
  }) => {
36
42
  const addActionDisabled = _.isEmpty(left) || _.isEmpty(right);
37
43
  const { formatMessage } = useIntl();
38
44
  return (
39
- <Form.Group inline>
40
- <Form.Field width={4}>
45
+ <Grid>
46
+ <Grid.Column width={4}>
41
47
  <StructureFieldsDropdown
42
- parentStructures={[_.prop("structure")(selectedStructure)]}
48
+ fluid
49
+ parentStructures={structureToFieldDropdown([selectedStructure])}
43
50
  onSelectField={handleChange("left", idx)}
44
- value={_.prop("id")(left)}
51
+ value={
52
+ _.isNil(_.prop("parent_index")(left))
53
+ ? _.prop("id")(left)
54
+ : `${_.prop("id")(left)}/${_.prop("parent_index")(left)}`
55
+ }
45
56
  />
46
- </Form.Field>
47
- <Form.Field width={1}>{"="}</Form.Field>
48
- <Form.Field width={4}>
57
+ </Grid.Column>
58
+ <Grid.Column width={1}>{"="}</Grid.Column>
59
+ <Grid.Column width={4}>
49
60
  <StructureFieldsDropdown
50
- parentStructures={_.map(_.prop("structure"))(parentStructures)}
61
+ fluid
62
+ parentStructures={structureToFieldDropdown(parentStructures)}
51
63
  onSelectField={handleChange("right", idx)}
52
- value={_.prop("id")(right)}
64
+ value={
65
+ _.isNil(_.prop("parent_index")(right))
66
+ ? _.prop("id")(right)
67
+ : `${_.prop("id")(right)}/${_.prop("parent_index")(right)}`
68
+ }
53
69
  />
54
- </Form.Field>
55
- <Form.Field width={1}>
70
+ </Grid.Column>
71
+ <Grid.Column width={1}>
56
72
  {
57
73
  <Popup
58
74
  disabled={!addActionDisabled}
59
75
  content={formatMessage({
60
- id: "dataset.form.button.add_join.popup"
76
+ id: "dataset.form.button.add_join.popup",
61
77
  })}
62
78
  trigger={
63
79
  <Icon
@@ -69,8 +85,8 @@ const JoinClause = ({
69
85
  }
70
86
  />
71
87
  }
72
- </Form.Field>
73
- <Form.Field width={1}>
88
+ </Grid.Column>
89
+ <Grid.Column width={1}>
74
90
  {idx > 0 && (
75
91
  <Icon
76
92
  name="trash"
@@ -78,8 +94,8 @@ const JoinClause = ({
78
94
  onClick={() => deleteClause(idx)}
79
95
  />
80
96
  )}
81
- </Form.Field>
82
- </Form.Group>
97
+ </Grid.Column>
98
+ </Grid>
83
99
  );
84
100
  };
85
101
 
@@ -91,24 +107,27 @@ JoinClause.propTypes = {
91
107
  selectedStructure: PropTypes.object,
92
108
  createClause: PropTypes.func,
93
109
  deleteClause: PropTypes.func,
94
- handleChange: PropTypes.func
110
+ handleChange: PropTypes.func,
95
111
  };
96
112
 
97
113
  export const StructureSelectorInputFieldJoin = ({
98
114
  structures,
99
115
  selectedStructure,
100
- handleSelectField
116
+ handleSelectField,
101
117
  }) => {
102
118
  const clauses = getClauses(selectedStructure);
103
119
  const parentStructures = getParentStructures(structures, selectedStructure);
104
120
  const createClause = () => handleSelectField([...clauses, {}]);
105
121
  const removeAt = (arr, idx) => [
106
122
  ..._.slice(0, idx)(arr),
107
- ..._.slice(idx + 1, arr.length)(arr)
123
+ ..._.slice(idx + 1, arr.length)(arr),
108
124
  ];
109
- const deleteClause = idx => handleSelectField(removeAt(clauses, idx));
110
- const handleChange = (prop, idx) => ({ data_structure_id: id }) => {
111
- const newClause = { ...clauses[idx], [prop]: { id } };
125
+ const deleteClause = (idx) => handleSelectField(removeAt(clauses, idx));
126
+ const handleChange = (prop, idx) => (data) => {
127
+ const { data_structure_id: id, parent_index: parent_index } = data;
128
+ const newClause = _.isNil(parent_index)
129
+ ? { ...clauses[idx], [prop]: { id } }
130
+ : { ...clauses[idx], [prop]: { id, parent_index } };
112
131
  const newClauses = _.set(`[${idx}]`, newClause)(clauses);
113
132
  handleSelectField(newClauses);
114
133
  };
@@ -134,7 +153,7 @@ export const StructureSelectorInputFieldJoin = ({
134
153
  StructureSelectorInputFieldJoin.propTypes = {
135
154
  structures: PropTypes.array,
136
155
  selectedStructure: PropTypes.object,
137
- handleSelectField: PropTypes.func
156
+ handleSelectField: PropTypes.func,
138
157
  };
139
158
 
140
159
  export default StructureSelectorInputFieldJoin;
@@ -378,7 +378,7 @@ exports[`<GrantRequest /> matches the latest snapshot with approve actions 1`] =
378
378
  role="listitem"
379
379
  >
380
380
  <div
381
- class="header"
381
+ class="header dynamic-field-header"
382
382
  >
383
383
  bar
384
384
  </div>
@@ -386,7 +386,7 @@ exports[`<GrantRequest /> matches the latest snapshot with approve actions 1`] =
386
386
  class="description"
387
387
  >
388
388
  <div
389
- style="white-space: pre-wrap;"
389
+ class="default-value"
390
390
  >
391
391
  baz
392
392
  </div>
@@ -644,7 +644,7 @@ exports[`<GrantRequest /> matches the latest snapshot with failed status 1`] = `
644
644
  role="listitem"
645
645
  >
646
646
  <div
647
- class="header"
647
+ class="header dynamic-field-header"
648
648
  >
649
649
  bar
650
650
  </div>
@@ -652,7 +652,7 @@ exports[`<GrantRequest /> matches the latest snapshot with failed status 1`] = `
652
652
  class="description"
653
653
  >
654
654
  <div
655
- style="white-space: pre-wrap;"
655
+ class="default-value"
656
656
  >
657
657
  baz
658
658
  </div>
@@ -1,37 +1,39 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<StructureSelectorInputFieldJoin /> matches the latest JoinClause snapshot 1`] = `
4
- <FormGroup
5
- inline={true}
6
- >
7
- <FormField
4
+ <Grid>
5
+ <GridColumn
8
6
  width={4}
9
7
  >
10
8
  <Connect(StructureFieldsDropdown)
9
+ fluid={true}
11
10
  onSelectField={[Function]}
12
11
  parentStructures={
13
12
  Array [
14
- undefined,
13
+ Object {
14
+ "alias": undefined,
15
+ },
15
16
  ]
16
17
  }
17
18
  value="7"
18
19
  />
19
- </FormField>
20
- <FormField
20
+ </GridColumn>
21
+ <GridColumn
21
22
  width={1}
22
23
  >
23
24
  =
24
- </FormField>
25
- <FormField
25
+ </GridColumn>
26
+ <GridColumn
26
27
  width={4}
27
28
  >
28
29
  <Connect(StructureFieldsDropdown)
30
+ fluid={true}
29
31
  onSelectField={[Function]}
30
32
  parentStructures={Array []}
31
33
  value="8"
32
34
  />
33
- </FormField>
34
- <FormField
35
+ </GridColumn>
36
+ <GridColumn
35
37
  width={1}
36
38
  >
37
39
  <Popup
@@ -57,8 +59,8 @@ exports[`<StructureSelectorInputFieldJoin /> matches the latest JoinClause snaps
57
59
  />
58
60
  }
59
61
  />
60
- </FormField>
61
- <FormField
62
+ </GridColumn>
63
+ <GridColumn
62
64
  width={1}
63
65
  >
64
66
  <Icon
@@ -67,8 +69,8 @@ exports[`<StructureSelectorInputFieldJoin /> matches the latest JoinClause snaps
67
69
  name="trash"
68
70
  onClick={[Function]}
69
71
  />
70
- </FormField>
71
- </FormGroup>
72
+ </GridColumn>
73
+ </Grid>
72
74
  `;
73
75
 
74
76
  exports[`<StructureSelectorInputFieldJoin /> matches the latest snapshot 1`] = `
@@ -17,12 +17,6 @@ describe("reducers: graph", () => {
17
17
  expect(graph(fooState, { type: clearGraph.TRIGGER })).toEqual(initialState);
18
18
  });
19
19
 
20
- it("should be the initial state after receiving the clearStructure.TRIGGER action", () => {
21
- expect(graph(fooState, { type: clearStructure.TRIGGER })).toEqual(
22
- initialState
23
- );
24
- });
25
-
26
20
  it("should be the initial state after receiving the fetchStructure.TRIGGER action", () => {
27
21
  expect(graph(fooState, { type: fetchStructure.TRIGGER })).toEqual(
28
22
  initialState
@@ -3,7 +3,7 @@ import {
3
3
  createGraph,
4
4
  excludeNode,
5
5
  fetchGraph,
6
- selectGroup
6
+ selectGroup,
7
7
  } from "../../routines";
8
8
 
9
9
  const fooState = { foo: "bar" };
@@ -21,6 +21,17 @@ describe("reducers: lineageQuery", () => {
21
21
  ).toMatchObject({ excludes: ["bar", "foo"] });
22
22
  });
23
23
 
24
+ it("should append a default {fromLineageContextMenu: false} it it was not specified in the excludeNode.TRIGGER action payload", () => {
25
+ const state = { excludes: ["foo"] };
26
+ const payload = { id: "bar" };
27
+ expect(
28
+ lineageQuery(state, { type: excludeNode.TRIGGER, payload })
29
+ ).toMatchObject({
30
+ excludes: ["bar", "foo"],
31
+ fromLineageContextMenu: false,
32
+ });
33
+ });
34
+
24
35
  it("should reset the exclusions after receiving the selectGroup.TRIGGER action", () => {
25
36
  const payload = {};
26
37
  expect(
@@ -39,6 +50,23 @@ describe("reducers: lineageQuery", () => {
39
50
  ).toMatchObject({ excludes: [], ids, type, levels });
40
51
  });
41
52
 
53
+ it("should append a default {fromLineageContextMenu: false} it it was not specified in the createGraph.TRIGGER action payload", () => {
54
+ const ids = ["foo", "bar"];
55
+ const type = "type";
56
+ const levels = 3;
57
+ const state = { excludes: ["foo"] };
58
+ const payload = { ids, type, levels };
59
+ expect(
60
+ lineageQuery(state, { type: createGraph.TRIGGER, payload })
61
+ ).toMatchObject({
62
+ excludes: [],
63
+ ids,
64
+ type,
65
+ levels,
66
+ fromLineageContextMenu: false,
67
+ });
68
+ });
69
+
42
70
  it("should extract the data from the payload of the fetchGraph.SUCCESS action", () => {
43
71
  const ids = ["foo", "bar"];
44
72
  const type = "type";
@@ -2,7 +2,8 @@ import { initialState, structureImpactGraph } from "../structureImpactGraph";
2
2
  import {
3
3
  clearStructure,
4
4
  fetchStructure,
5
- fetchStructureGraph
5
+ fetchStructureGraph,
6
+ createGraph
6
7
  } from "../../routines";
7
8
 
8
9
  const fooState = { foo: "bar" };
@@ -35,6 +36,28 @@ describe("reducers: structureImpactGraph", () => {
35
36
  ).toBe(data);
36
37
  });
37
38
 
39
+ it("should extract the data from the payload after receiving the createGraph.SUCCESS action", () => {
40
+ const data = { opts: { type: "impact" } };
41
+ const payload = { data };
42
+ expect(
43
+ structureImpactGraph(fooState, {
44
+ type: createGraph.SUCCESS,
45
+ payload
46
+ })
47
+ ).toBe(data);
48
+ });
49
+
50
+ it("should leave the state unchanged if createGraph succeeds and was called from LineageContextMenu", () => {
51
+ const data = { opts: { type: "impact" }, fromLineageContextMenu: true };
52
+ const payload = { data };
53
+ expect(
54
+ structureImpactGraph(fooState, {
55
+ type: createGraph.SUCCESS,
56
+ payload
57
+ })
58
+ ).toBe(fooState);
59
+ });
60
+
38
61
  it("should leave thes tate unchanged if the type of the fetchStructureGraph.SUCCESS action is not impact", () => {
39
62
  const data = { opts: { type: "foo" } };
40
63
  const payload = { data };
@@ -2,7 +2,8 @@ import { initialState, structureLineageGraph } from "../structureLineageGraph";
2
2
  import {
3
3
  clearStructure,
4
4
  fetchStructure,
5
- fetchStructureGraph
5
+ fetchStructureGraph,
6
+ createGraph,
6
7
  } from "../../routines";
7
8
 
8
9
  const fooState = { foo: "bar" };
@@ -35,6 +36,39 @@ describe("reducers: structureLineageGraph", () => {
35
36
  ).toBe(data);
36
37
  });
37
38
 
39
+ it("should extract the data from the payload after receiving the createGraph.SUCCESS action", () => {
40
+ const data = { opts: { type: "lineage" } };
41
+ const payload = { data };
42
+ expect(
43
+ structureLineageGraph(fooState, {
44
+ type: createGraph.SUCCESS,
45
+ payload
46
+ })
47
+ ).toBe(data);
48
+ });
49
+
50
+ it("should leave the state unchanged if the type of the fetchStructureGraph.SUCCESS action is not lineage", () => {
51
+ const data = { opts: { type: "foo" } };
52
+ const payload = { data };
53
+ expect(
54
+ structureLineageGraph(fooState, {
55
+ type: fetchStructureGraph.SUCCESS,
56
+ payload
57
+ })
58
+ ).toBe(fooState);
59
+ });
60
+
61
+ it("should leave the state unchanged if createGraph succeeds and was called from LineageContextMenu", () => {
62
+ const data = { opts: { type: "impact" }, fromLineageContextMenu: true };
63
+ const payload = { data };
64
+ expect(
65
+ structureLineageGraph(fooState, {
66
+ type: createGraph.SUCCESS,
67
+ payload
68
+ })
69
+ ).toBe(fooState);
70
+ });
71
+
38
72
  it("should leave thes tate unchanged if the type of the fetchStructureGraph.SUCCESS action is not lineage", () => {
39
73
  const data = { opts: { type: "foo" } };
40
74
  const payload = { data };
@@ -11,8 +11,6 @@ export const initialState = {};
11
11
 
12
12
  export const graph = (state = initialState, { type, payload }) => {
13
13
  switch (type) {
14
- case clearStructure.TRIGGER:
15
- return initialState;
16
14
  case fetchStructure.TRIGGER:
17
15
  return initialState;
18
16
  case clearGraph.TRIGGER:
@@ -6,21 +6,37 @@ export const initialState = { ids: [], type: "lineage", excludes: [] };
6
6
  export const lineageQuery = (state = initialState, { type, payload }) => {
7
7
  switch (type) {
8
8
  case excludeNode.TRIGGER: {
9
- const { id } = payload;
9
+ const { id, fromLineageContextMenu } = payload;
10
10
  const { excludes } = state;
11
- return { ...state, excludes: _.concat([id])(excludes) };
11
+ return {
12
+ ...state,
13
+ excludes: _.concat([id])(excludes),
14
+ fromLineageContextMenu: fromLineageContextMenu || false,
15
+ };
12
16
  }
13
17
  case createGraph.TRIGGER: {
14
- const { ids = [], type = "lineage", levels } = payload;
15
- return { ...state, ids, type, excludes: [], levels };
18
+ const {
19
+ ids = [],
20
+ type = "lineage",
21
+ levels,
22
+ fromLineageContextMenu,
23
+ } = payload;
24
+ return {
25
+ ...state,
26
+ ids,
27
+ type,
28
+ excludes: [],
29
+ levels,
30
+ fromLineageContextMenu: fromLineageContextMenu || false,
31
+ };
16
32
  }
17
33
  case fetchGraph.SUCCESS: {
18
34
  const {
19
35
  data: {
20
36
  ids,
21
37
  opts: { type: type },
22
- excludes: excludes
23
- }
38
+ excludes: excludes,
39
+ },
24
40
  } = payload;
25
41
  return { ids, type, excludes, levels: state.levels };
26
42
  }
@@ -2,7 +2,8 @@ import _ from "lodash/fp";
2
2
  import {
3
3
  clearStructure,
4
4
  fetchStructure,
5
- fetchStructureGraph
5
+ fetchStructureGraph,
6
+ createGraph,
6
7
  } from "../routines";
7
8
 
8
9
  export const initialState = {};
@@ -16,8 +17,10 @@ export const structureImpactGraph = (
16
17
  return initialState;
17
18
  case fetchStructure.TRIGGER:
18
19
  return initialState;
20
+ case createGraph.SUCCESS:
19
21
  case fetchStructureGraph.SUCCESS:
20
- return _.pathEq("data.opts.type", "impact")(payload)
22
+ return !payload?.data?.fromLineageContextMenu &&
23
+ _.pathEq("data.opts.type", "impact")(payload)
21
24
  ? _.prop("data")(payload)
22
25
  : state;
23
26
  default:
@@ -2,7 +2,8 @@ import _ from "lodash/fp";
2
2
  import {
3
3
  clearStructure,
4
4
  fetchStructure,
5
- fetchStructureGraph
5
+ fetchStructureGraph,
6
+ createGraph,
6
7
  } from "../routines";
7
8
 
8
9
  export const initialState = {};
@@ -16,8 +17,10 @@ export const structureLineageGraph = (
16
17
  return initialState;
17
18
  case fetchStructure.TRIGGER:
18
19
  return initialState;
20
+ case createGraph.SUCCESS:
19
21
  case fetchStructureGraph.SUCCESS:
20
- return _.pathEq("data.opts.type", "lineage")(payload)
22
+ return !payload?.data?.fromLineageContextMenu &&
23
+ _.pathEq("data.opts.type", "lineage")(payload)
21
24
  ? _.prop("data")(payload)
22
25
  : state;
23
26
  default:
@@ -68,10 +68,13 @@ describe("sagas: createGraphRequestSaga", () => {
68
68
  });
69
69
 
70
70
  describe("sagas: createGraphSaga", () => {
71
+ const fromLineageContextMenu = false;
72
+
71
73
  const query = {
72
74
  ids: ["BASILEAII_HIST_PROA_MOROSIDAD [ 2 ] [ 1 ].SOBREGIROCAPITAL"],
73
75
  type: "lineage",
74
76
  exclude: [],
77
+ fromLineageContextMenu,
75
78
  };
76
79
 
77
80
  const graphAccepted = {
@@ -80,7 +83,7 @@ describe("sagas: createGraphSaga", () => {
80
83
  };
81
84
 
82
85
  const graphCreated = {
83
- data: graphData,
86
+ data: { ...graphData, fromLineageContextMenu },
84
87
  status: 201,
85
88
  };
86
89
 
@@ -114,7 +117,7 @@ describe("sagas: createGraphSaga", () => {
114
117
  .next(graphAccepted)
115
118
  .put(fetchGraph.request(graphAccepted))
116
119
  .next()
117
- .fork(polling, graphAccepted)
120
+ .fork(polling, graphAccepted, fromLineageContextMenu)
118
121
  .next()
119
122
  .take(cancelPoll.TRIGGER)
120
123
  .next()
@@ -149,6 +152,8 @@ describe("sagas: createGraphSaga", () => {
149
152
  });
150
153
 
151
154
  describe("generator: polling", () => {
155
+ const fromLineageContextMenu = false;
156
+
152
157
  const graphAccepted = {
153
158
  data: graphAcceptedData,
154
159
  status: 202,
@@ -160,13 +165,13 @@ describe("generator: polling", () => {
160
165
  };
161
166
 
162
167
  const graphCompleted = {
163
- data: graphData,
168
+ data: { ...graphData, fromLineageContextMenu },
164
169
  status: 200,
165
170
  };
166
171
 
167
172
  it("polling should keep requesting by graph hash until graph is received", () => {
168
173
  expect(() => {
169
- testSaga(polling, graphAccepted)
174
+ testSaga(polling, graphAccepted, fromLineageContextMenu)
170
175
  .next()
171
176
  .delay(1000)
172
177
  .next()
@@ -202,7 +207,7 @@ describe("generator: polling", () => {
202
207
 
203
208
  it("cancel should stop polling and return createGraph failure", () => {
204
209
  expect(() => {
205
- testSaga(polling, graphAccepted)
210
+ testSaga(polling, graphAccepted, fromLineageContextMenu)
206
211
  .next()
207
212
  .delay(1000)
208
213
  .next()
@@ -48,7 +48,11 @@ export function* createGraphSaga() {
48
48
  if (graphAcceptedOrCreated.status == 202) {
49
49
  yield put(fetchGraph.request(graphAcceptedOrCreated));
50
50
  // starts the task in the background
51
- const pollingTask = yield fork(polling, graphAcceptedOrCreated);
51
+ const pollingTask = yield fork(
52
+ polling,
53
+ graphAcceptedOrCreated,
54
+ body.fromLineageContextMenu
55
+ );
52
56
 
53
57
  // wait for the user stop action
54
58
  yield take(cancelPoll.TRIGGER);
@@ -56,7 +60,15 @@ export function* createGraphSaga() {
56
60
  // this will cause the forked bgSync task to jump into its finally block
57
61
  yield cancel(pollingTask);
58
62
  } else if (graphAcceptedOrCreated.status == 201) {
59
- yield put(createGraph.success(graphAcceptedOrCreated));
63
+ yield put(
64
+ createGraph.success({
65
+ ...graphAcceptedOrCreated,
66
+ data: {
67
+ ...graphAcceptedOrCreated.data,
68
+ fromLineageContextMenu: body.fromLineageContextMenu,
69
+ },
70
+ })
71
+ );
60
72
  }
61
73
  } catch (error) {
62
74
  if (error.response) {
@@ -70,7 +82,7 @@ export function* createGraphSaga() {
70
82
  }
71
83
  }
72
84
 
73
- export function* polling(graphAccepted) {
85
+ export function* polling(graphAccepted, fromLineageContextMenu) {
74
86
  const backoff = exponentialBackoff(1.2, 0);
75
87
  let backoffTime,
76
88
  retry = 0,
@@ -113,7 +125,12 @@ export function* polling(graphAccepted) {
113
125
  } while (!isCancelled && graphResp?.status == 202);
114
126
 
115
127
  graphResp?.status == 200 && !isCancelled
116
- ? yield put(createGraph.success(graphResp))
128
+ ? yield put(
129
+ createGraph.success({
130
+ ...graphResp,
131
+ data: { ...graphResp.data, fromLineageContextMenu },
132
+ })
133
+ )
117
134
  : yield put(createGraph.failure({}));
118
135
  }
119
136
 
@@ -12,7 +12,8 @@ const getFields = (parent, structuresFields) =>
12
12
  _.propOr([], _.path("id")(parent)),
13
13
  _.map((sf) => ({
14
14
  ...sf,
15
- label: _.prop("name")(parent),
15
+ label: _.prop("alias.text")(parent) || _.prop("name")(parent),
16
+ parent_index: _.prop("alias.index")(parent),
16
17
  }))
17
18
  )(structuresFields);
18
19
 
@@ -20,7 +21,7 @@ const getFieldsFromStructures = (structuresFields, parentStructures) =>
20
21
  _.flow(
21
22
  _.map((parent) => getFields(parent, structuresFields)),
22
23
  _.flatten,
23
- _.uniqBy("data_structure_id"),
24
+ _.uniqBy((s) => `${s.data_structure_id}/${s.parent_index}`),
24
25
  _.map(withFieldType)
25
26
  )(parentStructures);
26
27