@truedat/core 4.43.6 → 4.44.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,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.44.1] 2022-05-11
4
+
5
+ ### Changed
6
+
7
+ - [TD-4089] Changes RULE_IMPLEMENTATION routes to drop the "rule" parent
8
+
9
+ ## [4.44.0] 2022-05-10
10
+
11
+ ### Added
12
+
13
+ - [TD-4723] New `DomainSelector` which loads domains using GraphQL query
14
+
3
15
  ## [4.43.6] 2022-05-05
4
16
 
5
17
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/core",
3
- "version": "4.43.6",
3
+ "version": "4.44.2",
4
4
  "description": "Truedat Web Core",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -32,7 +32,7 @@
32
32
  "@babel/plugin-transform-modules-commonjs": "^7.15.0",
33
33
  "@babel/preset-env": "^7.15.0",
34
34
  "@babel/preset-react": "^7.14.5",
35
- "@truedat/test": "4.43.6",
35
+ "@truedat/test": "4.44.2",
36
36
  "babel-jest": "^27.0.6",
37
37
  "babel-plugin-dynamic-import-node": "^2.3.3",
38
38
  "babel-plugin-lodash": "^3.3.4",
@@ -106,5 +106,5 @@
106
106
  "react-dom": ">= 16.8.6 < 17",
107
107
  "semantic-ui-react": ">= 0.88.2 < 2.1"
108
108
  },
109
- "gitHead": "848143d79486499dd9deadea6a545929b5cdb39c"
109
+ "gitHead": "ca0c5fffcba96736f7a2054f3c37789da8c30a9e"
110
110
  }
@@ -0,0 +1,11 @@
1
+ import { gql } from "@apollo/client";
2
+
3
+ export const DOMAINS_QUERY = gql`
4
+ query DomainsQuery($action: String!) {
5
+ domains(action: $action) {
6
+ id
7
+ name
8
+ parentId
9
+ }
10
+ }
11
+ `;
@@ -14,8 +14,8 @@ import Submenu from "./Submenu";
14
14
  const items = [{ name: "structures", routes: [STRUCTURES, SYSTEMS] }];
15
15
 
16
16
  const adminItems = [
17
- { name: "structure_types", routes: [STRUCTURE_TYPES] },
18
- { name: "structure_tags", routes: [STRUCTURE_TAGS] },
17
+ { name: "structureTypes", routes: [STRUCTURE_TYPES] },
18
+ { name: "structureTags", routes: [STRUCTURE_TAGS] },
19
19
  ];
20
20
 
21
21
  const structureNoteItems = [
@@ -0,0 +1,42 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { useIntl } from "react-intl";
5
+ import { useQuery } from "@apollo/client";
6
+ import { accentInsensitivePathOrder } from "../services/sort";
7
+ import { stratify, flatten } from "../services/tree";
8
+ import { DOMAINS_QUERY } from "../api/queries";
9
+ import TreeSelector from "./TreeSelector";
10
+
11
+ export const DomainSelector = ({ action, onLoad, value, ...props }) => {
12
+ const { formatMessage } = useIntl();
13
+ const { loading, error, data } = useQuery(DOMAINS_QUERY, {
14
+ variables: { action },
15
+ onCompleted: onLoad,
16
+ });
17
+ if (error) return null;
18
+ if (loading) return null;
19
+ const options = _.flow(
20
+ _.propOr([], "domains"),
21
+ _.sortBy(accentInsensitivePathOrder("name")),
22
+ stratify({}),
23
+ flatten
24
+ )(data);
25
+
26
+ return (
27
+ <TreeSelector
28
+ options={options}
29
+ placeholder={formatMessage({ id: "domain.multiple.placeholder" })}
30
+ value={_.map(_.toString)(value)}
31
+ {...props}
32
+ />
33
+ );
34
+ };
35
+
36
+ DomainSelector.propTypes = {
37
+ action: PropTypes.string,
38
+ onLoad: PropTypes.func,
39
+ value: PropTypes.array,
40
+ };
41
+
42
+ export default DomainSelector;
@@ -43,7 +43,7 @@ export const DropdownMenuItem = ({
43
43
  };
44
44
 
45
45
  DropdownMenuItem.propTypes = {
46
- id: PropTypes.number,
46
+ id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
47
47
  canOpen: PropTypes.bool,
48
48
  check: PropTypes.bool,
49
49
  handleOpen: PropTypes.func,
@@ -0,0 +1,161 @@
1
+ import _ from "lodash/fp";
2
+ import React, { useState, useEffect } from "react";
3
+ import { Dropdown, Form, Icon, Input, Label } from "semantic-ui-react";
4
+ import PropTypes from "prop-types";
5
+ import { lowerDeburr } from "../services/sort";
6
+ import { recursiveMatch, descendents, childIds } from "../services/tree";
7
+ import DropdownMenuItem from "./DropdownMenuItem";
8
+
9
+ export const match =
10
+ (query) =>
11
+ ({ name }) =>
12
+ _.contains(query)(lowerDeburr(name));
13
+ export const matchAny = recursiveMatch(match);
14
+
15
+ export const TreeSelector = ({
16
+ options,
17
+ error,
18
+ label,
19
+ name,
20
+ onBlur,
21
+ onChange,
22
+ required = false,
23
+ placeholder,
24
+ value: initialValue = [],
25
+ }) => {
26
+ const [value, setValue] = useState(initialValue);
27
+ const [query, setQuery] = useState();
28
+ const [open, setOpen] = useState([]);
29
+ const [displayed, setDisplayed] = useState([]);
30
+
31
+ useEffect(() => {
32
+ const ids = childIds(open)(options);
33
+ setDisplayed((displayed) => _.union(displayed)(ids));
34
+ }, [options, open, value]);
35
+
36
+ const handleOpen = (id) => {
37
+ const option = _.find({ id })(options);
38
+ const isOpen = _.contains(id)(open);
39
+ const childIds = _.map("id")(option.children);
40
+ const descendentIds = descendents(option);
41
+
42
+ if (isOpen) {
43
+ setOpen(_.without([id, ...descendentIds])(open));
44
+ setDisplayed(_.without(descendentIds)(displayed));
45
+ } else {
46
+ setOpen(_.union([id])(open));
47
+ setDisplayed(_.union(childIds)(displayed));
48
+ }
49
+ };
50
+
51
+ const displayAll = () => {
52
+ const ids = _.map("id")(options);
53
+ setOpen(ids);
54
+ setDisplayed(ids);
55
+ };
56
+
57
+ const handleSearch = (e, { value }) => {
58
+ e.preventDefault();
59
+ setQuery(lowerDeburr(value));
60
+ if (!_.isEmpty(value)) {
61
+ displayAll();
62
+ }
63
+ };
64
+
65
+ const handleClick = (e, id) => {
66
+ const ids = _.includes(id)(value)
67
+ ? _.without([id])(value)
68
+ : _.union([id])(value);
69
+ setValue(ids);
70
+ onChange && onChange(e, { value: ids });
71
+ };
72
+
73
+ const filterSearch = query ? _.filter(matchAny(query)) : _.identity;
74
+
75
+ const filterDisplayed = _.filter(
76
+ ({ id, level }) => level === 0 || _.includes(id)(displayed)
77
+ );
78
+
79
+ const trigger = _.isEmpty(value) ? (
80
+ <label>{placeholder}</label>
81
+ ) : (
82
+ _.map((id) => (
83
+ <Label key={id}>
84
+ {_.flow(_.find({ id }), _.prop("name"))(options)}
85
+ <Icon
86
+ name="delete"
87
+ onClick={(e) => {
88
+ e.preventDefault();
89
+ e.stopPropagation();
90
+ handleClick(e, id);
91
+ }}
92
+ />
93
+ </Label>
94
+ ))(value)
95
+ );
96
+
97
+ const items = _.flow(
98
+ filterSearch,
99
+ filterDisplayed,
100
+ _.map((option) => (
101
+ <DropdownMenuItem
102
+ key={option?.id}
103
+ check={false}
104
+ handleOpen={handleOpen}
105
+ handleClick={handleClick}
106
+ open={_.contains(option.id)(open)}
107
+ canOpen={!_.isEmpty(option.children)}
108
+ selected={_.contains(option.id)(value)}
109
+ {...option}
110
+ />
111
+ ))
112
+ )(options);
113
+
114
+ return (
115
+ <Form.Dropdown
116
+ error={error}
117
+ floating
118
+ label={label}
119
+ name={name}
120
+ onBlur={onBlur}
121
+ upward={false}
122
+ required={required}
123
+ trigger={trigger}
124
+ multiple
125
+ value={value}
126
+ >
127
+ <Dropdown.Menu>
128
+ <Input
129
+ icon="search"
130
+ iconPosition="left"
131
+ className="search"
132
+ onKeyDown={(e) => {
133
+ if (e.key === " ") {
134
+ e.stopPropagation();
135
+ }
136
+ }}
137
+ onChange={handleSearch}
138
+ onClick={(e) => {
139
+ e.preventDefault();
140
+ e.stopPropagation();
141
+ }}
142
+ />
143
+ <Dropdown.Menu scrolling>{items}</Dropdown.Menu>
144
+ </Dropdown.Menu>
145
+ </Form.Dropdown>
146
+ );
147
+ };
148
+
149
+ TreeSelector.propTypes = {
150
+ error: PropTypes.bool,
151
+ label: PropTypes.string,
152
+ name: PropTypes.string,
153
+ onBlur: PropTypes.func,
154
+ onChange: PropTypes.func,
155
+ options: PropTypes.array,
156
+ placeholder: PropTypes.string,
157
+ required: PropTypes.bool,
158
+ value: PropTypes.array,
159
+ };
160
+
161
+ export default TreeSelector;
@@ -1,15 +1,21 @@
1
1
  import React from "react";
2
- import { shallow } from "enzyme";
2
+ import { render } from "@truedat/test/render";
3
3
  import { CatalogMenu } from "../CatalogMenu";
4
4
 
5
5
  jest.mock("../../hooks", () => ({
6
- useActiveRoutes: jest.fn(() => true),
6
+ ...jest.requireActual("../../hooks"),
7
7
  useAuthorized: jest.fn(() => true),
8
8
  }));
9
9
 
10
+ const renderOpts = {
11
+ messages: {
12
+ en: {},
13
+ },
14
+ };
15
+
10
16
  describe("<CatalogMenu />", () => {
11
17
  it("matches the latest snapshot", () => {
12
- const wrapper = shallow(<CatalogMenu />);
13
- expect(wrapper).toMatchSnapshot();
18
+ const { container } = render(<CatalogMenu />, renderOpts);
19
+ expect(container).toMatchSnapshot();
14
20
  });
15
21
  });
@@ -0,0 +1,59 @@
1
+ import React from "react";
2
+ import { waitFor } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+ import { render } from "@truedat/test/render";
5
+ import { DOMAINS_QUERY } from "../../api/queries";
6
+ import DomainSelector from "../DomainSelector";
7
+
8
+ const action = "manageStructureTags";
9
+
10
+ const domainsMock = {
11
+ request: { query: DOMAINS_QUERY, variables: { action } },
12
+ result: {
13
+ data: {
14
+ domains: [
15
+ { __typename: "Domain", id: "1", name: "foo", parentId: null },
16
+ { __typename: "Domain", id: "2", name: "bar", parentId: "1" },
17
+ { __typename: "Domain", id: "3", name: "baz", parentId: "2" },
18
+ { __typename: "Domain", id: "4", name: "xyzzy", parentId: "99" },
19
+ ],
20
+ },
21
+ },
22
+ };
23
+ const messages = {
24
+ en: {
25
+ "domain.multiple.placeholder": "Select domains",
26
+ },
27
+ };
28
+
29
+ const renderOpts = { mocks: [domainsMock], messages };
30
+
31
+ describe("<DomainSelector />", () => {
32
+ it("matches latest snapshot", () => {
33
+ const props = { action, onChange: jest.fn() };
34
+ const { container } = render(<DomainSelector {...props} />, renderOpts);
35
+ expect(container).toMatchSnapshot();
36
+ });
37
+
38
+ it("calls onChange with selected values", async () => {
39
+ const props = { action, onChange: jest.fn() };
40
+ const { getByText, getByRole } = render(
41
+ <DomainSelector {...props} />,
42
+ renderOpts
43
+ );
44
+
45
+ await waitFor(() => {
46
+ expect(getByText("Select domains")).toBeTruthy();
47
+ });
48
+
49
+ userEvent.click(getByText("Select domains"));
50
+
51
+ await waitFor(() => {
52
+ expect(getByRole("option", { name: /foo/i })).toBeTruthy();
53
+ });
54
+
55
+ userEvent.click(getByRole("option", { name: /foo/i }));
56
+ expect(props.onChange.mock.calls.length).toBe(1);
57
+ expect(props.onChange.mock.calls[0][1]).toEqual({ value: ["1"] });
58
+ });
59
+ });
@@ -0,0 +1,38 @@
1
+ import React from "react";
2
+ import { waitFor } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+ import { render } from "@truedat/test/render";
5
+ import TreeSelector from "../TreeSelector";
6
+
7
+ const baz = { id: "3", level: 2, name: "baz", children: [] };
8
+ const bar = { id: "2", level: 1, name: "bar", children: [baz] };
9
+ const foo = { id: "1", level: 0, name: "foo", children: [bar] };
10
+ const options = [foo, bar, baz];
11
+
12
+ const renderOpts = {};
13
+ const onChange = jest.fn();
14
+ const props = { options, placeholder: "Select a domain", onChange };
15
+
16
+ describe("<TreeSelector />", () => {
17
+ it("matches latest snapshot", () => {
18
+ const { container } = render(<TreeSelector {...props} />, renderOpts);
19
+ expect(container).toMatchSnapshot();
20
+ });
21
+
22
+ it("calls onChange with selected values", async () => {
23
+ const { getByText, getByRole } = render(
24
+ <TreeSelector {...props} />,
25
+ renderOpts
26
+ );
27
+
28
+ userEvent.click(getByText("Select a domain"));
29
+
30
+ await waitFor(() => {
31
+ expect(getByRole("option", { name: /foo/i })).toBeTruthy();
32
+ });
33
+
34
+ userEvent.click(getByRole("option", { name: /foo/i }));
35
+ expect(onChange.mock.calls.length).toBe(1);
36
+ expect(onChange.mock.calls[0][1]).toEqual({ value: ["1"] });
37
+ });
38
+ });
@@ -1,43 +1,101 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<CatalogMenu /> matches the latest snapshot 1`] = `
4
- <Submenu
5
- icon="block layout"
6
- items={
7
- Array [
8
- Object {
9
- "name": "structures",
10
- "routes": Array [
11
- "/structures",
12
- "/systems",
13
- ],
14
- },
15
- Object {
16
- "name": "structure_types",
17
- "routes": Array [
18
- "/structure_types",
19
- ],
20
- },
21
- Object {
22
- "name": "structure_tags",
23
- "routes": Array [
24
- "/structure_tags",
25
- ],
26
- },
27
- Object {
28
- "name": "pending_structure_notes",
29
- "routes": Array [
30
- "/structure_notes",
31
- ],
32
- },
33
- Object {
34
- "name": "structures_upload_events",
35
- "routes": Array [
36
- "/bulk_update_template_content_events",
37
- ],
38
- },
39
- ]
40
- }
41
- name="catalog"
42
- />
4
+ <div>
5
+ <div>
6
+ <div
7
+ aria-expanded="false"
8
+ class="ui item dropdown"
9
+ role="listbox"
10
+ tabindex="0"
11
+ >
12
+ <a
13
+ class="ui"
14
+ href="/structures"
15
+ >
16
+ <i
17
+ aria-hidden="true"
18
+ class="block layout large icon"
19
+ />
20
+ </a>
21
+ <div
22
+ class="menu transition"
23
+ >
24
+ <div
25
+ class="header selectable"
26
+ >
27
+ catalog
28
+ </div>
29
+ <div
30
+ class="divider"
31
+ />
32
+ <a
33
+ aria-checked="false"
34
+ class="item"
35
+ href="/structures"
36
+ name="structures"
37
+ role="option"
38
+ >
39
+ <span
40
+ class="text"
41
+ >
42
+ structures
43
+ </span>
44
+ </a>
45
+ <a
46
+ aria-checked="false"
47
+ class="item"
48
+ href="/structureTypes"
49
+ name="structureTypes"
50
+ role="option"
51
+ >
52
+ <span
53
+ class="text"
54
+ >
55
+ structureTypes
56
+ </span>
57
+ </a>
58
+ <a
59
+ aria-checked="false"
60
+ class="item"
61
+ href="/structureTags"
62
+ name="structureTags"
63
+ role="option"
64
+ >
65
+ <span
66
+ class="text"
67
+ >
68
+ structureTags
69
+ </span>
70
+ </a>
71
+ <a
72
+ aria-checked="false"
73
+ class="item"
74
+ href="/structureNotes"
75
+ name="pending_structure_notes"
76
+ role="option"
77
+ >
78
+ <span
79
+ class="text"
80
+ >
81
+ pending_structure_notes
82
+ </span>
83
+ </a>
84
+ <a
85
+ aria-checked="false"
86
+ class="item"
87
+ href="/bulk_update_template_content_events"
88
+ name="structures_upload_events"
89
+ role="option"
90
+ >
91
+ <span
92
+ class="text"
93
+ >
94
+ structures_upload_events
95
+ </span>
96
+ </a>
97
+ </div>
98
+ </div>
99
+ </div>
100
+ </div>
43
101
  `;
@@ -0,0 +1,3 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<DomainSelector /> matches latest snapshot 1`] = `<div />`;
@@ -0,0 +1,59 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<TreeSelector /> matches latest snapshot 1`] = `
4
+ <div>
5
+ <div
6
+ class="field"
7
+ >
8
+ <div
9
+ aria-expanded="false"
10
+ aria-multiselectable="true"
11
+ class="ui floating multiple dropdown"
12
+ role="listbox"
13
+ tabindex="0"
14
+ >
15
+ <label>
16
+ Select a domain
17
+ </label>
18
+ <i
19
+ aria-hidden="true"
20
+ class="dropdown icon"
21
+ />
22
+ <div
23
+ class="menu transition"
24
+ >
25
+ <div
26
+ class="ui left icon input search"
27
+ >
28
+ <input
29
+ type="text"
30
+ />
31
+ <i
32
+ aria-hidden="true"
33
+ class="search icon"
34
+ />
35
+ </div>
36
+ <div
37
+ class="scrolling menu transition"
38
+ >
39
+ <div
40
+ aria-selected="false"
41
+ class="item"
42
+ role="option"
43
+ >
44
+ <div
45
+ style="margin-left: 0px;"
46
+ >
47
+ <i
48
+ aria-hidden="true"
49
+ class="plus icon"
50
+ />
51
+ foo
52
+ </div>
53
+ </div>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ `;
@@ -12,6 +12,7 @@ import DashboardMenu from "./DashboardMenu";
12
12
  import DateFilter from "./DateFilter";
13
13
  import DateRangeFilter from "./DateRangeFilter";
14
14
  import DateTime from "./DateTime";
15
+ import DomainSelector from "./DomainSelector";
15
16
  import DropdownMenuItem from "./DropdownMenuItem";
16
17
  import ErrorBoundary from "./ErrorBoundary";
17
18
  import FiltersLoader from "./FiltersLoader";
@@ -36,6 +37,7 @@ import SidebarToggle from "./SidebarToggle";
36
37
  import SideMenu from "./SideMenu";
37
38
  import Submenu from "./Submenu";
38
39
  import TaxonomyMenu from "./TaxonomyMenu";
40
+ import TreeSelector from "./TreeSelector";
39
41
  import Unauthorized from "./Unauthorized";
40
42
  import UploadModal from "./UploadModal";
41
43
 
@@ -54,6 +56,7 @@ export {
54
56
  DateFilter,
55
57
  DateRangeFilter,
56
58
  DateTime,
59
+ DomainSelector,
57
60
  DropdownMenuItem,
58
61
  ErrorBoundary,
59
62
  FiltersLoader,
@@ -78,6 +81,7 @@ export {
78
81
  SideMenu,
79
82
  Submenu,
80
83
  TaxonomyMenu,
84
+ TreeSelector,
81
85
  Unauthorized,
82
- UploadModal
86
+ UploadModal,
83
87
  };
@@ -92,8 +92,8 @@ export default {
92
92
  "sidemenu.rules": "Quality Rules",
93
93
  "sidemenu.search": "Search",
94
94
  "sidemenu.sources": "Sources",
95
- "sidemenu.structure_tags": "Structure Tags",
96
- "sidemenu.structure_types": "Structure Types",
95
+ "sidemenu.structureTags": "Structure Tags",
96
+ "sidemenu.structureTypes": "Structure Types",
97
97
  "sidemenu.structures": "Structures",
98
98
  "sidemenu.structures_upload_events": "My loads",
99
99
  "sidemenu.subscriptions": "Subscriptions",
@@ -95,8 +95,8 @@ export default {
95
95
  "sidemenu.rules": "Reglas",
96
96
  "sidemenu.search": "Búsqueda",
97
97
  "sidemenu.sources": "Orígenes",
98
- "sidemenu.structure_tags": "Etiquetas de estructura",
99
- "sidemenu.structure_types": "Tipos de estructura",
98
+ "sidemenu.structureTags": "Etiquetas de estructura",
99
+ "sidemenu.structureTypes": "Tipos de estructura",
100
100
  "sidemenu.structures": "Estructuras",
101
101
  "sidemenu.structures_upload_events": "Mis cargas",
102
102
  "sidemenu.subscriptions": "Suscripciones",
package/src/routes.js CHANGED
@@ -1,9 +1,7 @@
1
1
  import _ from "lodash/fp";
2
2
  import { compile } from "path-to-regexp";
3
3
 
4
- export const GRANT_REQUEST_APPROVALS = "/grant_request_approvals";
5
4
  export const CALLBACK = "/callback";
6
- export const CONCEPT_VERSION = "/concepts/:business_concept_id/versions/:id";
7
5
  export const CONCEPTS = "/concepts";
8
6
  export const CONCEPTS_BULK_UPDATE = "/concepts/bulk_update";
9
7
  export const CONCEPTS_NEW = "/concepts/new";
@@ -17,17 +15,17 @@ export const CONCEPT_LINKS_CONCEPTS =
17
15
  "/concepts/:business_concept_id/versions/:id/links/concepts";
18
16
  export const CONCEPT_LINKS_CONCEPTS_NEW =
19
17
  "/concepts/:business_concept_id/versions/:id/links/concepts/new";
18
+ export const CONCEPT_LINKS_IMPLEMENTATIONS =
19
+ "/concepts/:business_concept_id/versions/:id/links/implementations";
20
20
  export const CONCEPT_LINKS_STRUCTURES =
21
21
  "/concepts/:business_concept_id/versions/:id/links/structures";
22
22
  export const CONCEPT_LINKS_STRUCTURES_NEW =
23
23
  "/concepts/:business_concept_id/versions/:id/links/structures/new";
24
-
25
- export const CONCEPT_LINKS_IMPLEMENTATIONS =
26
- "/concepts/:business_concept_id/versions/:id/links/implementations";
27
24
  export const CONCEPT_RULES =
28
25
  "/concepts/:business_concept_id/versions/:id/rules";
29
26
  export const CONCEPT_RULES_NEW =
30
27
  "/concepts/:business_concept_id/versions/:id/rules/new";
28
+ export const CONCEPT_VERSION = "/concepts/:business_concept_id/versions/:id";
31
29
  export const CONFIGURATION = "/configurations/:external_id";
32
30
  export const CONFIGURATIONS = "/configurations";
33
31
  export const CONFIGURATION_CREATE = "/configurations/new";
@@ -44,11 +42,11 @@ export const DOMAIN_MEMBERS = "/domains/:id/members";
44
42
  export const DOMAIN_MEMBERS_NEW = "/domains/:id/members/new";
45
43
  export const DOMAIN_NEW = "/domains/:id/new";
46
44
  export const EXECUTION_GROUP = "/executionGroups/:id";
47
- export const GRANT_REQUESTS = "/grant_requests";
48
- export const GRANT_REQUEST = "/grant_requests/:id";
49
-
50
45
  export const GRANTS = "/grants";
51
46
  export const GRANTS_REQUESTS_CHECKOUT = "/grants_requests/checkout";
47
+ export const GRANT_REQUEST = "/grant_requests/:id";
48
+ export const GRANT_REQUESTS = "/grant_requests";
49
+ export const GRANT_REQUEST_APPROVALS = "/grant_request_approvals";
52
50
  export const GRAPH = "/graphs/:id";
53
51
  export const GRAPHS = "/graphs";
54
52
  export const GROUP = "/groups/:id";
@@ -57,13 +55,33 @@ export const GROUP_CREATE = "/groups/new";
57
55
  export const GROUP_EDIT = "/groups/:id/edit";
58
56
  export const IMPLEMENTATIONS = "/implementations";
59
57
  export const IMPLEMENTATION_CONCEPT_LINKS =
60
- "/rules/:id/implementations/:implementation_id/links/concepts";
58
+ "/implementations/:implementation_id/links/concepts";
61
59
  export const IMPLEMENTATION_CONCEPT_LINKS_NEW =
62
- "/rules/:id/implementations/:implementation_id/links/concepts/new";
60
+ "/implementations/:implementation_id/links/concepts/new";
63
61
  export const IMPLEMENTATION_STRUCTURES =
64
- "/rules/:id/implementations/:implementation_id/structures";
62
+ "/implementations/:implementation_id/structures";
65
63
  export const IMPLEMENTATION_STRUCTURES_NEW =
66
- "/rules/:id/implementations/:implementation_id/structures/new";
64
+ "/implementations/:implementation_id/structures/new";
65
+ export const IMPLEMENTATION_NEW = "/implementations/new";
66
+ export const IMPLEMENTATION_NEW_RAW = "/implementations/new_raw";
67
+ export const IMPLEMENTATION = "/implementations/:implementation_id(\\d+)";
68
+ export const IMPLEMENTATION_EVENTS =
69
+ "/implementations/:implementation_id/events";
70
+ export const IMPLEMENTATION_EDIT = "/implementations/:implementation_id/edit";
71
+ export const IMPLEMENTATION_CLONE = "/implementations/:implementation_id/clone";
72
+ export const IMPLEMENTATION_MOVE = "/implementations/:implementation_id/move";
73
+ export const IMPLEMENTATION_RESULT_DETAILS =
74
+ "/implementations/:implementation_id/results/:rule_result_id";
75
+ export const IMPLEMENTATION_RESULT_SEGMENTS_RESULTS =
76
+ "/implementations/:implementation_id/results/:rule_result_id/segment_results";
77
+ export const IMPLEMENTATION_RESULT_REMEDIATION_PLAN =
78
+ "/implementations/:implementation_id/results/:rule_result_id/remediation_plan";
79
+ export const IMPLEMENTATION_RESULT_DETAILS_REMEDIATION_PLAN =
80
+ "/implementations/:implementation_id/results/:rule_result_id/remediation_plan";
81
+ export const IMPLEMENTATION_RESULTS =
82
+ "/implementations/:implementation_id/results";
83
+ export const IMPLEMENTATION_RESULTS_DETAILS =
84
+ "/implementations/:implementation_id/detail";
67
85
  export const INGEST = "/ingests/:id";
68
86
  export const INGESTS = "/ingests";
69
87
  export const INGESTS_NEW = "/ingests/new";
@@ -83,19 +101,19 @@ export const JOB = "/jobs/:id";
83
101
  export const JOBS = "/jobs";
84
102
  export const LINEAGE_EVENTS = "/lineage_events";
85
103
  export const LOGIN = "/login";
86
- export const MY_GRANT_REQUESTS = "/my_grant_requests";
87
104
  export const MY_GRANTS = "/my_grants";
105
+ export const MY_GRANT_REQUESTS = "/my_grant_requests";
88
106
  export const PASSWORD = "/password";
89
- export const PENDING_STRUCTURE_NOTES = "/structure_notes";
107
+ export const PENDING_STRUCTURE_NOTES = "/structureNotes";
90
108
  export const PROFILE_EXECUTION =
91
109
  "/profileGroups/:group_id/profileExecutions/:id";
92
110
  export const PROFILE_GROUP = "/profileGroups/:id";
93
111
  export const QUALITY_DASHBOARD = "/quality_dashboard";
94
- export const REMEDIATION_PLAN = "/rule_results/:rule_result_id/remediation";
95
112
  export const REMEDIATION_EDIT =
96
- "/rules/:id/implementations/:implementation_id/ruleResults/:rule_result_id/remediation/edit";
113
+ "/rules/:id/implementations/:implementation_id/results/:rule_result_id/remediation/edit";
97
114
  export const REMEDIATION_NEW =
98
- "/rules/:id/implementations/:implementation_id/ruleResults/:rule_result_id/remediation/new";
115
+ "/rules/:id/implementations/:implementation_id/results/:rule_result_id/remediation/new";
116
+ export const REMEDIATION_PLAN = "/rule_results/:rule_result_id/remediation";
99
117
  export const ROLE = "/roles/:id";
100
118
  export const ROLES = "/roles";
101
119
  export const ROLES_NEW = "/roles/new";
@@ -103,28 +121,9 @@ export const RULE = "/rules/:id(\\d+)";
103
121
  export const RULES = "/rules";
104
122
  export const RULE_EDIT = "/rules/:id/edit";
105
123
  export const RULE_EVENTS = "/rules/:id/events";
106
- export const RULE_IMPLEMENTATION =
107
- "/rules/:id/implementations/:implementation_id(\\d+)";
108
124
  export const RULE_IMPLEMENTATIONS = "/rules/:id/implementations";
109
- export const RULE_IMPLEMENTATION_EVENTS =
110
- "/rules/:id/implementations/:implementation_id/events";
111
125
  export const RULE_IMPLEMENTATION_NEW = "/rules/:id/implementations/new";
112
- export const RULE_IMPLEMENTATION_EDIT =
113
- "/rules/:id/implementations/:implementation_id/edit";
114
- export const RULE_IMPLEMENTATION_CLONE =
115
- "/rules/:id/implementations/:implementation_id/clone";
116
- export const RULE_IMPLEMENTATION_MOVE =
117
- "/rules/:id/implementations/:implementation_id/move";
118
- export const RULE_IMPLEMENTATION_RESULT_DETAILS =
119
- "/rules/:id/implementations/:implementation_id/results/:rule_result_id";
120
- export const RULE_IMPLEMENTATION_RESULT_SEGMENTS_RESULTS =
121
- "/rules/:id/implementations/:implementation_id/results/:rule_result_id/segment_results";
122
- export const RULE_IMPLEMENTATION_RESULT_REMEDIATION_PLAN =
123
- "/rules/:id/implementations/:implementation_id/results/:rule_result_id/remediation_plan";
124
- export const RULE_IMPLEMENTATION_RESULTS =
125
- "/rules/:id/implementations/:implementation_id/results";
126
- export const RULE_IMPLEMENTATION_RESULTS_DETAILS =
127
- "/rules/:id/implementations/:implementation_id/detail";
126
+ export const RULE_IMPLEMENTATION_NEW_RAW = "/rules/:id/implementations/new_raw";
128
127
  export const RULE_NEW = "/rules/new";
129
128
  export const SAMPLE = "/sample";
130
129
  export const SEARCH = "/search";
@@ -156,11 +155,11 @@ export const STRUCTURE_PARENTS = "/structures/:id/parents";
156
155
  export const STRUCTURE_PROFILE = "/structures/:id/profile";
157
156
  export const STRUCTURE_RULES_IMPLEMENTATIONS =
158
157
  "/structures/:id/rules_implementations";
159
- export const STRUCTURE_TAGS = "/structure_tags";
160
- export const STRUCTURE_TAGS_NEW = "/structure_tags/new";
161
- export const STRUCTURE_TAGS_EDIT = "/structure_tags/:id/edit";
162
- export const STRUCTURE_TYPES = "/structure_types";
163
- export const STRUCTURE_TYPES_EDIT = "/structure_types/:id/edit";
158
+ export const STRUCTURE_TAGS = "/structureTags";
159
+ export const STRUCTURE_TAGS_EDIT = "/structureTags/:id/edit";
160
+ export const STRUCTURE_TAGS_NEW = "/structureTags/new";
161
+ export const STRUCTURE_TYPES = "/structureTypes";
162
+ export const STRUCTURE_TYPES_EDIT = "/structureTypes/:id/edit";
164
163
  export const STRUCTURE_VERSION = "/structures/:id/versions/:version";
165
164
  export const STRUCTURE_VERSIONS = "/structures/:id/versions";
166
165
  export const STRUCTURE_VERSION_VERSIONS =
@@ -168,8 +167,8 @@ export const STRUCTURE_VERSION_VERSIONS =
168
167
  export const STRUCTURES_UPLOAD_EVENTS = "/bulk_update_template_content_events";
169
168
  export const SUBSCRIPTION = "/subscriptions/:id";
170
169
  export const SUBSCRIPTIONS = "/subscriptions";
171
- export const SUBSCRIPTION_NEW = "/subscriptions/new";
172
170
  export const SUBSCRIPTION_EDIT = "/subscriptions/:id/edit";
171
+ export const SUBSCRIPTION_NEW = "/subscriptions/new";
173
172
  export const SYSTEMS = "/systems";
174
173
  export const SYSTEM_EDIT = "/systems/:id/edit";
175
174
  export const SYSTEM_NEW = "/systems/new";
@@ -189,133 +188,139 @@ export const USER_EDIT = "/users/:id/edit";
189
188
  export const USER_EDIT_PASSWORD = "/users/:id/password";
190
189
 
191
190
  const routes = {
192
- GRANT_REQUEST_APPROVALS,
193
191
  CALLBACK,
192
+ CONCEPTS,
193
+ CONCEPTS_BULK_UPDATE,
194
+ CONCEPTS_NEW,
195
+ CONCEPTS_PENDING,
194
196
  CONCEPT_ARCHIVE,
195
197
  CONCEPT_EDIT,
196
198
  CONCEPT_EVENTS,
197
- CONCEPT_LINKS_CONCEPTS_NEW,
198
199
  CONCEPT_LINKS_CONCEPTS,
199
- CONCEPT_LINKS_STRUCTURES_NEW,
200
- CONCEPT_LINKS_STRUCTURES,
200
+ CONCEPT_LINKS_CONCEPTS_NEW,
201
201
  CONCEPT_LINKS_IMPLEMENTATIONS,
202
- CONCEPT_RULES_NEW,
202
+ CONCEPT_LINKS_STRUCTURES,
203
+ CONCEPT_LINKS_STRUCTURES_NEW,
203
204
  CONCEPT_RULES,
205
+ CONCEPT_RULES_NEW,
204
206
  CONCEPT_VERSION,
205
- CONCEPTS_BULK_UPDATE,
206
- CONCEPTS_NEW,
207
- CONCEPTS_PENDING,
208
- CONCEPTS,
209
- CONFIGURATION_EDIT,
210
207
  CONFIGURATION,
208
+ CONFIGURATION_EDIT,
211
209
  DASHBOARD,
212
- DOMAIN_ACTION,
213
- DOMAIN_EDIT,
214
- DOMAIN_MEMBERS_NEW,
215
- DOMAIN_MEMBERS,
216
- DOMAIN_NEW,
217
210
  DOMAIN,
211
+ DOMAINS,
218
212
  DOMAINS_ACTIONS,
219
213
  DOMAINS_NEW,
220
214
  DOMAINS_SEARCH,
221
- DOMAINS,
215
+ DOMAIN_ACTION,
216
+ DOMAIN_EDIT,
217
+ DOMAIN_MEMBERS,
218
+ DOMAIN_MEMBERS_NEW,
219
+ DOMAIN_NEW,
222
220
  EXECUTION_GROUP,
223
- GRANT_REQUEST,
224
- GRANT_REQUESTS,
225
221
  GRANTS,
226
222
  GRANTS_REQUESTS_CHECKOUT,
223
+ GRANT_REQUEST,
224
+ GRANT_REQUESTS,
225
+ GRANT_REQUEST_APPROVALS,
227
226
  GRAPH,
228
227
  GRAPHS,
229
- GROUP_CREATE,
230
- GROUP_EDIT,
231
228
  GROUP,
232
229
  GROUPS,
230
+ GROUP_CREATE,
231
+ GROUP_EDIT,
233
232
  IMPLEMENTATIONS,
233
+ IMPLEMENTATION_NEW,
234
+ IMPLEMENTATION_NEW_RAW,
235
+ IMPLEMENTATION,
236
+ IMPLEMENTATION_EDIT,
237
+ IMPLEMENTATION_EVENTS,
238
+ IMPLEMENTATION_CLONE,
239
+ IMPLEMENTATION_MOVE,
240
+ IMPLEMENTATION_RESULT_DETAILS,
241
+ IMPLEMENTATION_RESULT_SEGMENTS_RESULTS,
242
+ IMPLEMENTATION_RESULT_REMEDIATION_PLAN,
243
+ IMPLEMENTATION_RESULTS,
244
+ IMPLEMENTATION_RESULTS_DETAILS,
245
+ INGEST,
246
+ INGESTS,
247
+ INGESTS_NEW,
248
+ INGESTS_PENDING,
234
249
  INGEST_ARCHIVE,
235
250
  INGEST_DUPLICATE,
236
251
  INGEST_EDIT,
237
252
  INGEST_EVENTS,
238
253
  INGEST_EXECUTIONS,
239
- INGEST_RELATIONS_INGESTS_NEW,
240
254
  INGEST_RELATIONS_INGESTS,
241
- INGEST_RELATIONS_STRUCTURES_NEW,
255
+ INGEST_RELATIONS_INGESTS_NEW,
242
256
  INGEST_RELATIONS_STRUCTURES,
243
- INGEST,
244
- INGESTS_NEW,
245
- INGESTS_PENDING,
246
- INGESTS,
257
+ INGEST_RELATIONS_STRUCTURES_NEW,
247
258
  JOB,
248
259
  JOBS,
249
260
  LINEAGE_EVENTS,
250
261
  LOGIN,
251
- MY_GRANT_REQUESTS,
252
262
  MY_GRANTS,
263
+ MY_GRANT_REQUESTS,
253
264
  PASSWORD,
254
265
  PENDING_STRUCTURE_NOTES,
255
266
  PROFILE_EXECUTION,
256
267
  PROFILE_GROUP,
257
268
  QUALITY_DASHBOARD,
258
- REMEDIATION_PLAN,
259
269
  REMEDIATION_EDIT,
260
270
  REMEDIATION_NEW,
271
+ REMEDIATION_PLAN,
261
272
  ROLE,
262
- ROLES_NEW,
263
273
  ROLES,
274
+ ROLES_NEW,
275
+ RULE,
276
+ RULES,
264
277
  RULE_EDIT,
265
278
  RULE_EVENTS,
266
- RULE_IMPLEMENTATION_CLONE,
267
- RULE_IMPLEMENTATION_EDIT,
268
- RULE_IMPLEMENTATION_EVENTS,
269
279
  IMPLEMENTATION_CONCEPT_LINKS,
270
280
  IMPLEMENTATION_CONCEPT_LINKS_NEW,
271
281
  IMPLEMENTATION_STRUCTURES,
272
282
  IMPLEMENTATION_STRUCTURES_NEW,
273
- RULE_IMPLEMENTATION_MOVE,
274
283
  RULE_IMPLEMENTATION_NEW,
275
- RULE_IMPLEMENTATION_RESULT_DETAILS,
276
- RULE_IMPLEMENTATION_RESULT_SEGMENTS_RESULTS,
277
- RULE_IMPLEMENTATION_RESULT_REMEDIATION_PLAN,
278
- RULE_IMPLEMENTATION_RESULTS_DETAILS,
279
- RULE_IMPLEMENTATION_RESULTS,
280
- RULE_IMPLEMENTATION,
284
+ RULE_IMPLEMENTATION_NEW_RAW,
281
285
  RULE_IMPLEMENTATIONS,
282
286
  RULE_NEW,
283
- RULE,
284
- RULES,
285
287
  SAMPLE,
288
+ SEARCH,
286
289
  SEARCH_CONCEPTS,
287
290
  SEARCH_INGESTS,
288
291
  SEARCH_RESULTS,
289
292
  SEARCH_STRUCTURES,
290
- SEARCH,
293
+ SOURCE,
294
+ SOURCES,
291
295
  SOURCES_NEW,
292
296
  SOURCE_EDIT,
293
297
  SOURCE_JOB,
294
- SOURCE_JOBS_NEW,
295
298
  SOURCE_JOBS,
296
- SOURCE,
297
- SOURCES,
299
+ SOURCE_JOBS_NEW,
300
+ STRUCTURE,
301
+ STRUCTURES,
302
+ STRUCTURES_BULK_UPDATE,
298
303
  STRUCTURE_CHILDREN,
299
304
  STRUCTURE_EVENTS,
300
305
  STRUCTURE_GRANTS,
301
306
  STRUCTURE_IMPACT,
302
307
  STRUCTURE_LINEAGE,
303
- STRUCTURE_LINKS_NEW,
304
308
  STRUCTURE_LINKS,
309
+ STRUCTURE_LINKS_NEW,
305
310
  STRUCTURE_METADATA,
306
- STRUCTURE_NOTES_EDIT,
307
311
  STRUCTURE_NOTES,
312
+ STRUCTURE_NOTES_EDIT,
308
313
  STRUCTURE_PARENTS,
309
314
  STRUCTURE_PROFILE,
310
315
  STRUCTURE_RULES_IMPLEMENTATIONS,
316
+ STRUCTURE_TAGS,
311
317
  STRUCTURE_TAGS_EDIT,
312
318
  STRUCTURE_TAGS_NEW,
313
- STRUCTURE_TAGS,
314
- STRUCTURE_TYPES_EDIT,
315
319
  STRUCTURE_TYPES,
316
- STRUCTURE_VERSION_VERSIONS,
320
+ STRUCTURE_TYPES_EDIT,
317
321
  STRUCTURE_VERSION,
318
322
  STRUCTURE_VERSIONS,
323
+ STRUCTURE_VERSION_VERSIONS,
319
324
  STRUCTURE,
320
325
  STRUCTURES_BULK_UPDATE,
321
326
  STRUCTURES,
@@ -324,23 +329,25 @@ const routes = {
324
329
  SUBSCRIPTION_NEW,
325
330
  SUBSCRIPTION,
326
331
  SUBSCRIPTIONS,
332
+ SUBSCRIPTION_EDIT,
333
+ SUBSCRIPTION_NEW,
334
+ SYSTEMS,
327
335
  SYSTEM_EDIT,
328
336
  SYSTEM_NEW,
329
337
  SYSTEM_STRUCTURES,
330
- SYSTEMS,
331
- TAGS_NEW,
332
338
  TAGS,
333
- TEMPLATE_EDIT,
334
- TEMPLATE_SCOPE,
339
+ TAGS_NEW,
335
340
  TEMPLATE,
336
- TEMPLATES_NEW,
337
341
  TEMPLATES,
342
+ TEMPLATES_NEW,
343
+ TEMPLATE_EDIT,
344
+ TEMPLATE_SCOPE,
338
345
  UNAUTHORIZED,
339
- USER_EDIT_PASSWORD,
340
- USER_EDIT,
341
346
  USER,
342
- USERS_NEW,
343
347
  USERS,
348
+ USERS_NEW,
349
+ USER_EDIT,
350
+ USER_EDIT_PASSWORD,
344
351
  };
345
352
 
346
353
  export const linkTo = _.mapValues(compile)(routes);
@@ -0,0 +1,68 @@
1
+ import _ from "lodash/fp";
2
+ import {
3
+ flatten,
4
+ childIds,
5
+ descendents,
6
+ recursiveMatch,
7
+ stratify,
8
+ } from "../tree";
9
+
10
+ const baz = { id: "3", name: "baz", children: [] };
11
+ const bar = { id: "2", name: "bar", children: [baz] };
12
+ const foo = { id: "1", name: "foo", children: [bar] };
13
+ const options = [foo];
14
+
15
+ describe("stratify", () => {
16
+ it("stratifies options", () => {
17
+ const foo = { id: 1, name: "foo" };
18
+ const bar = { id: 2, name: "bar", parentId: 1 };
19
+ const baz = { id: 3, name: "baz", parentId: 2 };
20
+ const xyzzy = { id: 4, name: "xyzzy", parentId: 99 };
21
+ const options = [foo, bar, baz, xyzzy];
22
+ const res = stratify({})(options);
23
+ expect(res).toEqual([
24
+ {
25
+ ...foo,
26
+ level: 0,
27
+ children: [{ ...bar, children: [{ ...baz, children: [] }] }],
28
+ },
29
+ { ...xyzzy, level: 0, children: [] },
30
+ ]);
31
+ });
32
+ });
33
+
34
+ describe("flatten", () => {
35
+ it("flattens stratified options and assigns level", () => {
36
+ const res = flatten(options);
37
+ expect(res).toEqual([
38
+ { ...foo, level: 0 },
39
+ { ...bar, level: 1 },
40
+ { ...baz, level: 2 },
41
+ ]);
42
+ });
43
+ });
44
+
45
+ describe("childIds", () => {
46
+ it("returns ids of children", () => {
47
+ const res = childIds("1")(options);
48
+ expect(res).toEqual(["2"]);
49
+ });
50
+ });
51
+
52
+ describe("descendents", () => {
53
+ it("returns descendent ids", () => {
54
+ const res = _.flatMap(descendents)(options);
55
+ expect(res).toEqual(["1", "2", "3"]);
56
+ });
57
+ });
58
+
59
+ describe("recursiveMatch", () => {
60
+ it("matches recursively", () => {
61
+ const match =
62
+ (query) =>
63
+ ({ name }) =>
64
+ _.contains(query)(name);
65
+ const res = _.filter(recursiveMatch(match)("baz"))(options);
66
+ expect(res).toEqual([foo]);
67
+ });
68
+ });
@@ -0,0 +1,44 @@
1
+ import _ from "lodash/fp";
2
+
3
+ const idFn = (d) => d.id;
4
+ const parentFn = (d) => d.parentId;
5
+ const nodeFn = ({ id, name, ...props }) => ({
6
+ id,
7
+ name,
8
+ children: [],
9
+ ...props,
10
+ });
11
+
12
+ export const stratify = (options) => (data) => {
13
+ const { node = nodeFn, id = idFn, parent = parentFn } = options || {};
14
+ const root = { children: [] };
15
+ const nodes = data.reduce((acc, d) => ({ ...acc, [id(d)]: node(d) }), {
16
+ root,
17
+ });
18
+ for (const d of data) {
19
+ const n = nodes[id(d)];
20
+ const p = nodes[parent(d)] || root;
21
+ p.children.push(n);
22
+ }
23
+ return root.children.map((c) => ({ ...c, level: 0 }));
24
+ };
25
+
26
+ export const flatten = (data, level = 0) =>
27
+ data.flatMap((d) =>
28
+ d.children?.length
29
+ ? [{ ...d, level }, ...flatten(d.children, level + 1)]
30
+ : [{ ...d, level }]
31
+ );
32
+
33
+ export const recursiveMatch = (match) => (query) => (d) =>
34
+ match(query)(d) || _.some(recursiveMatch(match)(query))(d?.children);
35
+
36
+ export const descendents = ({ id, children }) =>
37
+ _.isArray(children) ? [id, ...children.flatMap(descendents)] : [id];
38
+
39
+ export const childIds = (ids) =>
40
+ _.flow(
41
+ _.filter(({ id }) => _.contains(id)(ids)),
42
+ _.flatMap("children"),
43
+ _.map("id")
44
+ );