@truedat/auth 8.5.9 → 8.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -5
- package/src/groups/components/Group.js +4 -1
- package/src/groups/components/GroupCard.js +3 -3
- package/src/groups/components/GroupForm.js +16 -0
- package/src/groups/components/__tests__/Group.spec.js +68 -6
- package/src/groups/components/__tests__/GroupCard.spec.js +15 -1
- package/src/groups/components/__tests__/GroupForm.spec.js +80 -0
- package/src/groups/reducers/__tests__/groupRedirect.spec.js +4 -8
- package/src/groups/reducers/__tests__/groupUsers.spec.js +23 -5
- package/src/groups/reducers/group.js +1 -1
- package/src/groups/reducers/groupRedirect.js +2 -8
- package/src/groups/reducers/groupUsers.js +2 -1
- package/src/groups/sagas/__tests__/deleteGroup.spec.js +93 -0
- package/src/groups/sagas/deleteGroup.js +2 -1
- package/src/messages/en.js +2 -0
- package/src/messages/es.js +2 -0
- package/src/users/components/GroupUserCrumbs.js +2 -2
- package/src/users/components/UserForm.js +26 -14
- package/src/users/components/__tests__/GroupUserCrumbs.spec.js +73 -0
- package/src/users/components/__tests__/UserForm.spec.js +82 -0
- package/src/users/components/__tests__/UserGroupAcls.spec.js +21 -0
- package/src/users/components/__tests__/__snapshots__/EditUser.spec.js.snap +2 -8
- package/src/users/components/__tests__/__snapshots__/InitialUser.spec.js.snap +2 -8
- package/src/users/components/__tests__/__snapshots__/NewUser.spec.js.snap +2 -8
- package/src/users/components/__tests__/__snapshots__/UserForm.spec.js.snap +2 -8
- package/src/users/selectors/__tests__/getUserGroupAcls.spec.js +26 -23
- package/src/users/selectors/getUserGroupAcls.js +4 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/auth",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.6.1",
|
|
4
4
|
"description": "Truedat Web Auth",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"jsnext:main": "src/index.js",
|
|
@@ -51,15 +51,15 @@
|
|
|
51
51
|
"@testing-library/jest-dom": "^6.6.3",
|
|
52
52
|
"@testing-library/react": "^16.3.0",
|
|
53
53
|
"@testing-library/user-event": "^14.6.1",
|
|
54
|
-
"@truedat/test": "8.
|
|
54
|
+
"@truedat/test": "8.6.1",
|
|
55
55
|
"identity-obj-proxy": "^3.0.0",
|
|
56
56
|
"jest": "^29.7.0",
|
|
57
57
|
"redux-saga-test-plan": "^4.0.6"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"@apollo/client": "^3.13.8",
|
|
61
|
-
"@truedat/core": "8.
|
|
62
|
-
"auth0-js": "^
|
|
61
|
+
"@truedat/core": "8.6.1",
|
|
62
|
+
"auth0-js": "^10.0.0",
|
|
63
63
|
"axios": "^1.15.0",
|
|
64
64
|
"graphql": "^16.11.0",
|
|
65
65
|
"is-hotkey": "^0.2.0",
|
|
@@ -93,5 +93,5 @@
|
|
|
93
93
|
"resolutions": {
|
|
94
94
|
"superagent@npm:^7.1.5": "10.2.3"
|
|
95
95
|
},
|
|
96
|
-
"gitHead": "
|
|
96
|
+
"gitHead": "932eb0895ec71961f52a09309b99ae017977a899"
|
|
97
97
|
}
|
|
@@ -62,7 +62,10 @@ export const Group = ({ group, groupLoading, deleteGroup }) => {
|
|
|
62
62
|
})}
|
|
63
63
|
/>
|
|
64
64
|
<Header.Content>
|
|
65
|
-
{group.name}{" "}
|
|
65
|
+
{group.alias ? group.alias : group.name}{" "}
|
|
66
|
+
{group.alias ? (
|
|
67
|
+
<Header.Subheader>{group.name}</Header.Subheader>
|
|
68
|
+
) : null}
|
|
66
69
|
<Header.Subheader>{group.description}</Header.Subheader>
|
|
67
70
|
</Header.Content>
|
|
68
71
|
</Header>
|
|
@@ -8,14 +8,14 @@ import { linkTo } from "@truedat/core/routes";
|
|
|
8
8
|
import { deleteGroup } from "../routines";
|
|
9
9
|
|
|
10
10
|
export const GroupCard = ({
|
|
11
|
-
group: { name, description, id },
|
|
11
|
+
group: { name, description, alias, id },
|
|
12
12
|
deleteGroup,
|
|
13
13
|
}) => (
|
|
14
14
|
<Card key={id}>
|
|
15
15
|
<Card.Content>
|
|
16
16
|
<Card.Header as={Link} to={linkTo.GROUP({ id })}>
|
|
17
17
|
<Icon name="group" />
|
|
18
|
-
{name}
|
|
18
|
+
{alias || name}
|
|
19
19
|
</Card.Header>
|
|
20
20
|
<Card.Description>{description}</Card.Description>
|
|
21
21
|
</Card.Content>
|
|
@@ -43,7 +43,7 @@ export const GroupCard = ({
|
|
|
43
43
|
content={
|
|
44
44
|
<FormattedMessage
|
|
45
45
|
id="group.actions.delete.confirmation.content"
|
|
46
|
-
values={{ name: <i>{name}</i> }}
|
|
46
|
+
values={{ name: <i>{alias || name}</i> }}
|
|
47
47
|
/>
|
|
48
48
|
}
|
|
49
49
|
onConfirm={() => deleteGroup({ id })}
|
|
@@ -80,6 +80,22 @@ export const GroupForm = ({
|
|
|
80
80
|
/>
|
|
81
81
|
)}
|
|
82
82
|
/>
|
|
83
|
+
<Controller
|
|
84
|
+
control={control}
|
|
85
|
+
name="alias"
|
|
86
|
+
render={({ field: { onBlur, onChange, value } }) => (
|
|
87
|
+
<Form.Input
|
|
88
|
+
autoComplete="off"
|
|
89
|
+
label={formatMessage({ id: "group.props.alias" })}
|
|
90
|
+
onBlur={onBlur}
|
|
91
|
+
onChange={(_e, { value }) => onChange(value)}
|
|
92
|
+
placeholder={formatMessage({
|
|
93
|
+
id: "group.form.alias.placeholder",
|
|
94
|
+
})}
|
|
95
|
+
value={value || ""}
|
|
96
|
+
/>
|
|
97
|
+
)}
|
|
98
|
+
/>
|
|
83
99
|
<Controller
|
|
84
100
|
control={control}
|
|
85
101
|
name="user_ids"
|
|
@@ -1,15 +1,77 @@
|
|
|
1
|
+
import { fireEvent, waitFor } from "@testing-library/react";
|
|
2
|
+
import userEvent from "@testing-library/user-event";
|
|
1
3
|
import { render } from "@truedat/test/render";
|
|
2
|
-
import Group from "../Group";
|
|
4
|
+
import ConnectedGroup, { Group } from "../Group";
|
|
3
5
|
|
|
4
6
|
const group = { id: 2, name: "grupo1 ", description: "aaa bbb cc" };
|
|
5
7
|
|
|
6
|
-
const renderOpts = {
|
|
7
|
-
state: { group, deleteGroup: jest.fn() },
|
|
8
|
-
};
|
|
9
|
-
|
|
10
8
|
describe("<Group />", () => {
|
|
11
9
|
it("matches the latest snapshot", () => {
|
|
12
|
-
const { container } = render(
|
|
10
|
+
const { container } = render(
|
|
11
|
+
<Group group={group} deleteGroup={jest.fn()} groupLoading={false} />
|
|
12
|
+
);
|
|
13
13
|
expect(container).toMatchSnapshot();
|
|
14
14
|
});
|
|
15
|
+
|
|
16
|
+
it("renders alias with the group name as a subheader when alias is present", () => {
|
|
17
|
+
const group = {
|
|
18
|
+
id: 2,
|
|
19
|
+
name: "grupo1",
|
|
20
|
+
alias: "Grupo Alias",
|
|
21
|
+
description: "aaa bbb cc",
|
|
22
|
+
};
|
|
23
|
+
const { getByText } = render(
|
|
24
|
+
<Group group={group} deleteGroup={jest.fn()} />
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
expect(getByText("Grupo Alias")).toBeInTheDocument();
|
|
28
|
+
expect(getByText("grupo1")).toBeInTheDocument();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("renders nothing while the group is loading", () => {
|
|
32
|
+
const rendered = render(
|
|
33
|
+
<Group group={group} deleteGroup={jest.fn()} groupLoading={true} />
|
|
34
|
+
);
|
|
35
|
+
expect(rendered.container).toBeEmptyDOMElement();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("renders nothing when the group has no id", () => {
|
|
39
|
+
const rendered = render(
|
|
40
|
+
<Group
|
|
41
|
+
group={{ name: "no id", description: "x" }}
|
|
42
|
+
deleteGroup={jest.fn()}
|
|
43
|
+
groupLoading={false}
|
|
44
|
+
/>
|
|
45
|
+
);
|
|
46
|
+
expect(rendered.container).toBeEmptyDOMElement();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("maps group state from the store for the connected component", () => {
|
|
50
|
+
const rendered = render(<ConnectedGroup />, {
|
|
51
|
+
state: {
|
|
52
|
+
group: { id: 2, name: "from store", description: "stored desc" },
|
|
53
|
+
groupLoading: false,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
expect(rendered.getByText(/from store/i)).toBeInTheDocument();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("calls deleteGroup with the group id when delete is confirmed", async () => {
|
|
60
|
+
const user = userEvent.setup({ delay: null });
|
|
61
|
+
const deleteGroup = jest.fn();
|
|
62
|
+
const rendered = render(
|
|
63
|
+
<Group group={group} deleteGroup={deleteGroup} groupLoading={false} />
|
|
64
|
+
);
|
|
65
|
+
const dropdown = rendered.getByRole("listbox");
|
|
66
|
+
fireEvent.click(dropdown);
|
|
67
|
+
await waitFor(() =>
|
|
68
|
+
expect(rendered.getByText(/actions.delete/i)).toBeInTheDocument()
|
|
69
|
+
);
|
|
70
|
+
await user.click(rendered.getByText(/actions.delete/i));
|
|
71
|
+
await waitFor(() =>
|
|
72
|
+
expect(rendered.getByText(/confirmation.yes/i)).toBeInTheDocument()
|
|
73
|
+
);
|
|
74
|
+
await user.click(rendered.getByText(/confirmation.yes/i));
|
|
75
|
+
expect(deleteGroup).toHaveBeenCalledWith({ id: group.id });
|
|
76
|
+
});
|
|
15
77
|
});
|
|
@@ -17,6 +17,20 @@ describe("<GroupCard />", () => {
|
|
|
17
17
|
expect(rendered.container).toMatchSnapshot();
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
+
it("renders alias instead of name when alias is present", async () => {
|
|
21
|
+
const aliasGroup = {
|
|
22
|
+
...group,
|
|
23
|
+
alias: "Admins",
|
|
24
|
+
};
|
|
25
|
+
const rendered = render(
|
|
26
|
+
<GroupCard group={aliasGroup} deleteGroup={deleteGroup} />,
|
|
27
|
+
);
|
|
28
|
+
await waitForLoad(rendered);
|
|
29
|
+
|
|
30
|
+
expect(rendered.getByText("Admins")).toBeInTheDocument();
|
|
31
|
+
expect(rendered.queryByText("administrators")).not.toBeInTheDocument();
|
|
32
|
+
});
|
|
33
|
+
|
|
20
34
|
it("calls deleteGroup when delete button is clicked", async () => {
|
|
21
35
|
const rendered = render(<GroupCard {...props} />);
|
|
22
36
|
await waitForLoad(rendered);
|
|
@@ -26,7 +40,7 @@ describe("<GroupCard />", () => {
|
|
|
26
40
|
await user.click(rendered.getByRole("button", { name: /delete/i }));
|
|
27
41
|
|
|
28
42
|
await user.click(
|
|
29
|
-
rendered.getByRole("button", { name: /modal-affirmative-action/i })
|
|
43
|
+
rendered.getByRole("button", { name: /modal-affirmative-action/i }),
|
|
30
44
|
);
|
|
31
45
|
|
|
32
46
|
expect(deleteGroup).toHaveBeenCalledTimes(1);
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import userEvent from "@testing-library/user-event";
|
|
2
|
+
import { waitFor } from "@testing-library/react";
|
|
3
|
+
import { render, waitForLoad } from "@truedat/test/render";
|
|
4
|
+
import GroupForm from "../GroupForm";
|
|
5
|
+
|
|
6
|
+
describe("<GroupForm />", () => {
|
|
7
|
+
const renderOpts = {
|
|
8
|
+
state: {
|
|
9
|
+
users: [{ id: 101, full_name: "John Doe", user_name: "jdoe" }],
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
it("renders the alias input with the default group alias", async () => {
|
|
14
|
+
const group = {
|
|
15
|
+
id: 1,
|
|
16
|
+
name: "Administrators",
|
|
17
|
+
alias: "admin-group",
|
|
18
|
+
description: "Group description",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const rendered = render(
|
|
22
|
+
<GroupForm
|
|
23
|
+
group={group}
|
|
24
|
+
groupUsers={[{ id: 101 }]}
|
|
25
|
+
onSubmit={jest.fn()}
|
|
26
|
+
/>,
|
|
27
|
+
renderOpts
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
await waitForLoad(rendered);
|
|
31
|
+
await waitFor(() =>
|
|
32
|
+
expect(
|
|
33
|
+
rendered.getByPlaceholderText(/group.form.alias.placeholder/i)
|
|
34
|
+
).toHaveValue("admin-group")
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("updates name, description, alias and users fields", async () => {
|
|
39
|
+
const onSubmit = jest.fn();
|
|
40
|
+
const rendered = render(
|
|
41
|
+
<GroupForm group={{}} groupUsers={[]} onSubmit={onSubmit} />,
|
|
42
|
+
renderOpts
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
await waitForLoad(rendered);
|
|
46
|
+
|
|
47
|
+
const user = userEvent.setup({ delay: null });
|
|
48
|
+
|
|
49
|
+
await user.type(
|
|
50
|
+
rendered.getByPlaceholderText(/group.form.name.placeholder/i),
|
|
51
|
+
"Team Alpha"
|
|
52
|
+
);
|
|
53
|
+
await user.type(
|
|
54
|
+
rendered.getByPlaceholderText(/group.form.description.placeholder/i),
|
|
55
|
+
"Alpha description"
|
|
56
|
+
);
|
|
57
|
+
await user.type(
|
|
58
|
+
rendered.getByPlaceholderText(/group.form.alias.placeholder/i),
|
|
59
|
+
"alpha-team"
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
await user.click(rendered.getByRole("combobox"));
|
|
63
|
+
await user.click(rendered.getByRole("option", { name: /jdoe/i }));
|
|
64
|
+
|
|
65
|
+
await waitFor(() =>
|
|
66
|
+
expect(
|
|
67
|
+
rendered.getByPlaceholderText(/group.form.name.placeholder/i)
|
|
68
|
+
).toHaveValue("Team Alpha")
|
|
69
|
+
);
|
|
70
|
+
expect(
|
|
71
|
+
rendered.getByPlaceholderText(/group.form.description.placeholder/i)
|
|
72
|
+
).toHaveValue("Alpha description");
|
|
73
|
+
expect(
|
|
74
|
+
rendered.getByPlaceholderText(/group.form.alias.placeholder/i)
|
|
75
|
+
).toHaveValue("alpha-team");
|
|
76
|
+
expect(rendered.getByText(/jdoe/i)).toBeInTheDocument();
|
|
77
|
+
expect(onSubmit).not.toHaveBeenCalled();
|
|
78
|
+
expect(rendered.getByRole("button", { name: /save/i })).not.toBeDisabled();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { linkTo } from "@truedat/core/routes";
|
|
2
1
|
import { clearRedirect } from "@truedat/core/routines";
|
|
3
2
|
import {
|
|
4
3
|
createGroup,
|
|
5
4
|
deleteGroup,
|
|
6
5
|
deleteGroupUser,
|
|
7
|
-
updateGroup
|
|
6
|
+
updateGroup,
|
|
8
7
|
} from "../../routines";
|
|
9
8
|
import { groupRedirect } from "..";
|
|
10
9
|
|
|
@@ -40,12 +39,9 @@ describe("reducers: groupRedirect", () => {
|
|
|
40
39
|
);
|
|
41
40
|
});
|
|
42
41
|
|
|
43
|
-
it("should
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
expect(groupRedirect("foo", { type: deleteGroupUser.SUCCESS, meta })).toBe(
|
|
48
|
-
url
|
|
42
|
+
it("should ignore deleteGroupUser.SUCCESS action", () => {
|
|
43
|
+
expect(groupRedirect("foo", { type: deleteGroupUser.SUCCESS })).toEqual(
|
|
44
|
+
"foo"
|
|
49
45
|
);
|
|
50
46
|
});
|
|
51
47
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { clearGroup, fetchGroup } from "../../routines";
|
|
1
|
+
import { clearGroup, deleteGroupUser, fetchGroup } from "../../routines";
|
|
2
2
|
import { groupUsers, initialState } from "../groupUsers";
|
|
3
3
|
|
|
4
4
|
const fooState = { foo: "bar" };
|
|
@@ -14,20 +14,20 @@ describe("reducers: groupUsers", () => {
|
|
|
14
14
|
id: 1,
|
|
15
15
|
user_name: "user_name1",
|
|
16
16
|
full_name: "user name 1",
|
|
17
|
-
email: "email1@test.com"
|
|
17
|
+
email: "email1@test.com",
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
id: 2,
|
|
21
21
|
user_name: "user_name2",
|
|
22
22
|
full_name: "user name 2",
|
|
23
|
-
email: "email2@test.com"
|
|
24
|
-
}
|
|
23
|
+
email: "email2@test.com",
|
|
24
|
+
},
|
|
25
25
|
];
|
|
26
26
|
|
|
27
27
|
expect(
|
|
28
28
|
groupUsers(fooState, {
|
|
29
29
|
type: fetchGroup.SUCCESS,
|
|
30
|
-
payload: { data: { users } }
|
|
30
|
+
payload: { data: { users } },
|
|
31
31
|
})
|
|
32
32
|
).toBe(users);
|
|
33
33
|
});
|
|
@@ -36,6 +36,24 @@ describe("reducers: groupUsers", () => {
|
|
|
36
36
|
expect(groupUsers(fooState, { type: clearGroup.TRIGGER })).toEqual([]);
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
+
it("should handle the deleteGroupUser action", () => {
|
|
40
|
+
const users = [
|
|
41
|
+
{
|
|
42
|
+
id: 2,
|
|
43
|
+
user_name: "user_name2",
|
|
44
|
+
full_name: "user name 2",
|
|
45
|
+
email: "email2@test.com",
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
expect(
|
|
50
|
+
groupUsers(fooState, {
|
|
51
|
+
type: deleteGroupUser.SUCCESS,
|
|
52
|
+
payload: { data: { users } },
|
|
53
|
+
})
|
|
54
|
+
).toBe(users);
|
|
55
|
+
});
|
|
56
|
+
|
|
39
57
|
it("should ignore unknown actions", () => {
|
|
40
58
|
expect(groupUsers(fooState, { type: "FOO" })).toBe(fooState);
|
|
41
59
|
});
|
|
@@ -3,7 +3,7 @@ import { clearGroup, fetchGroup, updateGroup, createGroup } from "../routines";
|
|
|
3
3
|
|
|
4
4
|
const initialState = {};
|
|
5
5
|
|
|
6
|
-
const pickFields = _.pick(["id", "name", "description"]);
|
|
6
|
+
const pickFields = _.pick(["id", "name", "description", "alias"]);
|
|
7
7
|
|
|
8
8
|
const group = (state = initialState, { type, payload }) => {
|
|
9
9
|
switch (type) {
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import _ from "lodash/fp";
|
|
2
1
|
import { clearRedirect } from "@truedat/core/routines";
|
|
3
|
-
import {
|
|
2
|
+
import { GROUPS } from "@truedat/core/routes";
|
|
4
3
|
import { createGroup, deleteGroup, updateGroup } from "../routines";
|
|
5
|
-
import { deleteGroupUser } from "../../groups/routines";
|
|
6
4
|
|
|
7
5
|
const initialState = "";
|
|
8
6
|
|
|
9
|
-
export const groupRedirect = (state = initialState, { type
|
|
7
|
+
export const groupRedirect = (state = initialState, { type }) => {
|
|
10
8
|
switch (type) {
|
|
11
9
|
case clearRedirect.TRIGGER:
|
|
12
10
|
return initialState;
|
|
@@ -16,10 +14,6 @@ export const groupRedirect = (state = initialState, { type, meta }) => {
|
|
|
16
14
|
return GROUPS;
|
|
17
15
|
case deleteGroup.SUCCESS:
|
|
18
16
|
return GROUPS;
|
|
19
|
-
case deleteGroupUser.SUCCESS: {
|
|
20
|
-
const id = _.prop("group_id")(meta);
|
|
21
|
-
return linkTo.GROUP({ id });
|
|
22
|
-
}
|
|
23
17
|
default:
|
|
24
18
|
return state;
|
|
25
19
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
|
-
import { clearGroup, fetchGroup } from "../routines";
|
|
2
|
+
import { clearGroup, deleteGroupUser, fetchGroup } from "../routines";
|
|
3
3
|
|
|
4
4
|
export const initialState = [];
|
|
5
5
|
|
|
6
6
|
export const groupUsers = (state = initialState, { type, payload }) => {
|
|
7
7
|
switch (type) {
|
|
8
8
|
case fetchGroup.SUCCESS:
|
|
9
|
+
case deleteGroupUser.SUCCESS:
|
|
9
10
|
return _.pathOr([], "data.users")(payload);
|
|
10
11
|
case clearGroup.TRIGGER:
|
|
11
12
|
return initialState;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { compile } from "path-to-regexp";
|
|
2
|
+
import { testSaga } from "redux-saga-test-plan";
|
|
3
|
+
import { apiJsonDelete, JSON_OPTS } from "@truedat/core/services/api";
|
|
4
|
+
import { deleteGroupRequestSaga, deleteGroupSaga } from "../deleteGroup";
|
|
5
|
+
import { deleteGroup, fetchGroups } from "../../routines";
|
|
6
|
+
import { API_GROUP } from "../../api";
|
|
7
|
+
|
|
8
|
+
describe("sagas: deleteGroupRequestSaga", () => {
|
|
9
|
+
it("should invoke deleteGroupSaga on deleteGroup.TRIGGER", () => {
|
|
10
|
+
expect(() => {
|
|
11
|
+
testSaga(deleteGroupRequestSaga)
|
|
12
|
+
.next()
|
|
13
|
+
.takeLatest(deleteGroup.TRIGGER, deleteGroupSaga)
|
|
14
|
+
.finish()
|
|
15
|
+
.isDone();
|
|
16
|
+
}).not.toThrow();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should throw exception if an unhandled action is received", () => {
|
|
20
|
+
expect(() => {
|
|
21
|
+
testSaga(deleteGroupRequestSaga)
|
|
22
|
+
.next()
|
|
23
|
+
.takeLatest("FOO", deleteGroupRequestSaga);
|
|
24
|
+
}).toThrow();
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe("sagas: deleteGroupSaga", () => {
|
|
29
|
+
const payload = { id: "1", name: "group-1" };
|
|
30
|
+
const url = compile(API_GROUP)({ id: `${payload.id}` });
|
|
31
|
+
|
|
32
|
+
it("should put a success action and fetchGroups when a response is returned", () => {
|
|
33
|
+
expect(() => {
|
|
34
|
+
testSaga(deleteGroupSaga, { payload })
|
|
35
|
+
.next()
|
|
36
|
+
.put(deleteGroup.request())
|
|
37
|
+
.next()
|
|
38
|
+
.call(apiJsonDelete, url, JSON_OPTS)
|
|
39
|
+
.next({ data: payload })
|
|
40
|
+
.put(deleteGroup.success(payload))
|
|
41
|
+
.next()
|
|
42
|
+
.put(fetchGroups())
|
|
43
|
+
.next()
|
|
44
|
+
.put(deleteGroup.fulfill())
|
|
45
|
+
.next()
|
|
46
|
+
.isDone();
|
|
47
|
+
}).not.toThrow();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("should put a failure action when the call returns an error response", () => {
|
|
51
|
+
const error = {
|
|
52
|
+
response: { status: 500, data: { message: "Request failed" } },
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
expect(() => {
|
|
56
|
+
testSaga(deleteGroupSaga, { payload })
|
|
57
|
+
.next()
|
|
58
|
+
.put(deleteGroup.request())
|
|
59
|
+
.next()
|
|
60
|
+
.call(apiJsonDelete, url, JSON_OPTS)
|
|
61
|
+
.throw(error)
|
|
62
|
+
.put(
|
|
63
|
+
deleteGroup.failure({
|
|
64
|
+
status: 500,
|
|
65
|
+
data: { message: "Request failed" },
|
|
66
|
+
})
|
|
67
|
+
)
|
|
68
|
+
.next()
|
|
69
|
+
.put(deleteGroup.fulfill())
|
|
70
|
+
.next()
|
|
71
|
+
.isDone();
|
|
72
|
+
}).not.toThrow();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("should put a failure action when the call returns an error message", () => {
|
|
76
|
+
const message = "Request failed";
|
|
77
|
+
const error = { message };
|
|
78
|
+
|
|
79
|
+
expect(() => {
|
|
80
|
+
testSaga(deleteGroupSaga, { payload })
|
|
81
|
+
.next()
|
|
82
|
+
.put(deleteGroup.request())
|
|
83
|
+
.next()
|
|
84
|
+
.call(apiJsonDelete, url, JSON_OPTS)
|
|
85
|
+
.throw(error)
|
|
86
|
+
.put(deleteGroup.failure(message))
|
|
87
|
+
.next()
|
|
88
|
+
.put(deleteGroup.fulfill())
|
|
89
|
+
.next()
|
|
90
|
+
.isDone();
|
|
91
|
+
}).not.toThrow();
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { compile } from "path-to-regexp";
|
|
2
2
|
import { call, put, takeLatest } from "redux-saga/effects";
|
|
3
3
|
import { apiJsonDelete, JSON_OPTS } from "@truedat/core/services/api";
|
|
4
|
-
import { deleteGroup } from "../routines";
|
|
4
|
+
import { deleteGroup, fetchGroups } from "../routines";
|
|
5
5
|
import { API_GROUP } from "../api";
|
|
6
6
|
|
|
7
7
|
const toApiPath = compile(API_GROUP);
|
|
@@ -13,6 +13,7 @@ export function* deleteGroupSaga({ payload }) {
|
|
|
13
13
|
yield put(deleteGroup.request());
|
|
14
14
|
const { data } = yield call(apiJsonDelete, url, JSON_OPTS);
|
|
15
15
|
yield put(deleteGroup.success(data));
|
|
16
|
+
yield put(fetchGroups());
|
|
16
17
|
} catch (error) {
|
|
17
18
|
if (error.response) {
|
|
18
19
|
const { status, data } = error.response;
|
package/src/messages/en.js
CHANGED
|
@@ -13,10 +13,12 @@ export default {
|
|
|
13
13
|
"group.actions.delete.confirmation.header": "Delete Group",
|
|
14
14
|
"group.form.description.placeholder": "Description",
|
|
15
15
|
"group.form.name.placeholder": "Group Name",
|
|
16
|
+
"group.form.alias.placeholder": "Group Alias",
|
|
16
17
|
"group.form.users.placeholder": "Select user",
|
|
17
18
|
"group.props.description": "Description",
|
|
18
19
|
"group.props.members": "{members} users",
|
|
19
20
|
"group.props.name": "Name",
|
|
21
|
+
"group.props.alias": "Alias",
|
|
20
22
|
"group.props.users": "Users",
|
|
21
23
|
"groupUsers.actions.delete.confirmation.content": "Selected user will be deleted from group, ¿are you sure?",
|
|
22
24
|
"groupUsers.actions.delete.confirmation.header": "Remove user from group",
|
package/src/messages/es.js
CHANGED
|
@@ -13,10 +13,12 @@ export default {
|
|
|
13
13
|
"group.actions.delete.confirmation.header": "Eliminar grupo",
|
|
14
14
|
"group.form.description.placeholder": "Description",
|
|
15
15
|
"group.form.name.placeholder": "Nombre del grupo",
|
|
16
|
+
"group.form.alias.placeholder": "Alias del grupo",
|
|
16
17
|
"group.form.users.placeholder": "Seleccionar usuario",
|
|
17
18
|
"group.props.description": "Description",
|
|
18
19
|
"group.props.members": "{members} usuarios",
|
|
19
20
|
"group.props.name": "Nombre",
|
|
21
|
+
"group.props.alias": "Alias",
|
|
20
22
|
"group.props.users": "Usuarios",
|
|
21
23
|
"groupUsers.actions.delete.confirmation.content": "Se va a eliminar al usuario seleccionado del grupo, ¿está seguro?",
|
|
22
24
|
"groupUsers.actions.delete.confirmation.header": "Eliminar usuario de grupo",
|
|
@@ -15,7 +15,7 @@ export const GroupUserCrumbs = ({ group, groupLoading, action }) => {
|
|
|
15
15
|
{action === "show" && (
|
|
16
16
|
<>
|
|
17
17
|
<Breadcrumb.Divider icon="right angle" />
|
|
18
|
-
{group.name}
|
|
18
|
+
{group.alias || group.name}
|
|
19
19
|
</>
|
|
20
20
|
)}
|
|
21
21
|
{action === "add" && (
|
|
@@ -26,7 +26,7 @@ export const GroupUserCrumbs = ({ group, groupLoading, action }) => {
|
|
|
26
26
|
to={linkTo.GROUP({ id: group.id })}
|
|
27
27
|
active={action === "show"}
|
|
28
28
|
>
|
|
29
|
-
{group.name}
|
|
29
|
+
{group.alias || group.name}
|
|
30
30
|
</Breadcrumb.Section>
|
|
31
31
|
<Breadcrumb.Divider icon="right angle" />
|
|
32
32
|
<FormattedMessage id="groups.crumbs.groupUser.add" />
|
|
@@ -6,17 +6,29 @@ import { Button, Form } from "semantic-ui-react";
|
|
|
6
6
|
import { connect } from "react-redux";
|
|
7
7
|
import { HistoryBackButton } from "@truedat/core/components";
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const groupOptions = (groups = [], values) => {
|
|
10
|
+
const groupOpts = _.map.convert({ cap: false })((g, i) => {
|
|
11
|
+
const displayText = typeof g === "string" ? g : g.alias || g.name || "";
|
|
12
|
+
const value = typeof g === "string" ? g : g.name || "";
|
|
13
|
+
return { key: `group_${i}`, text: displayText, value: value };
|
|
14
|
+
})(groups);
|
|
10
15
|
|
|
11
|
-
const
|
|
12
|
-
_.flow(
|
|
13
|
-
_.concat(""),
|
|
14
|
-
_.concat(values),
|
|
16
|
+
const valueOpts = _.flow(
|
|
15
17
|
_.filter(_.isString),
|
|
18
|
+
_.reject((value) =>
|
|
19
|
+
_.some((g) => (typeof g === "string" ? g : g.name) === value, groups),
|
|
20
|
+
),
|
|
16
21
|
_.uniq,
|
|
17
22
|
_.sortBy(_.lowerCase),
|
|
18
|
-
_.map.convert({ cap: false })(
|
|
19
|
-
|
|
23
|
+
_.map.convert({ cap: false })((text, i) => ({
|
|
24
|
+
key: `value_${i}`,
|
|
25
|
+
text,
|
|
26
|
+
value: text,
|
|
27
|
+
})),
|
|
28
|
+
)(values || []);
|
|
29
|
+
|
|
30
|
+
return _.concat(groupOpts, valueOpts);
|
|
31
|
+
};
|
|
20
32
|
|
|
21
33
|
const userTypeOptions = (formatMessage) => [
|
|
22
34
|
{ text: formatMessage({ id: "user.type.user" }), value: "user" },
|
|
@@ -41,13 +53,13 @@ export const PasswordFormFields = ({ control, errors }) => {
|
|
|
41
53
|
rules={{
|
|
42
54
|
required: formatMessage(
|
|
43
55
|
{ id: "form.validation.required" },
|
|
44
|
-
{ prop: formatMessage({ id: "user.form.password" }) }
|
|
56
|
+
{ prop: formatMessage({ id: "user.form.password" }) },
|
|
45
57
|
),
|
|
46
58
|
minLength: {
|
|
47
59
|
value: 6,
|
|
48
60
|
message: formatMessage(
|
|
49
61
|
{ id: "form.validation.minLength" },
|
|
50
|
-
{ prop: formatMessage({ id: "user.form.password" }), value: 6 }
|
|
62
|
+
{ prop: formatMessage({ id: "user.form.password" }), value: 6 },
|
|
51
63
|
),
|
|
52
64
|
},
|
|
53
65
|
}}
|
|
@@ -132,7 +144,7 @@ export const UserForm = ({ isSubmitting, onSubmit, user, groups }) => {
|
|
|
132
144
|
rules={{
|
|
133
145
|
required: formatMessage(
|
|
134
146
|
{ id: "form.validation.required" },
|
|
135
|
-
{ prop: formatMessage({ id: "user.form.user_name" }) }
|
|
147
|
+
{ prop: formatMessage({ id: "user.form.user_name" }) },
|
|
136
148
|
),
|
|
137
149
|
}}
|
|
138
150
|
render={({ field: { onBlur, onChange, value } }) => (
|
|
@@ -216,7 +228,7 @@ export const UserForm = ({ isSubmitting, onSubmit, user, groups }) => {
|
|
|
216
228
|
error
|
|
217
229
|
? formatMessage(
|
|
218
230
|
{ id: "invalid", defaultMessage: "{prop} is required" },
|
|
219
|
-
{ prop: formatMessage({ id: "user.form.full_name" }) }
|
|
231
|
+
{ prop: formatMessage({ id: "user.form.full_name" }) },
|
|
220
232
|
)
|
|
221
233
|
: false
|
|
222
234
|
}
|
|
@@ -265,7 +277,7 @@ export const UserForm = ({ isSubmitting, onSubmit, user, groups }) => {
|
|
|
265
277
|
onBlur={onBlur}
|
|
266
278
|
onChange={(_e, { value }) => onChange(value)}
|
|
267
279
|
placeholder={formatMessage({ id: "user.form.placeholder.groups" })}
|
|
268
|
-
value={value ||
|
|
280
|
+
value={value || []}
|
|
269
281
|
/>
|
|
270
282
|
)}
|
|
271
283
|
/>
|
|
@@ -294,12 +306,12 @@ UserForm.propTypes = {
|
|
|
294
306
|
isSubmitting: PropTypes.bool,
|
|
295
307
|
onSubmit: PropTypes.func,
|
|
296
308
|
user: PropTypes.object,
|
|
297
|
-
groups: PropTypes.
|
|
309
|
+
groups: PropTypes.array,
|
|
298
310
|
};
|
|
299
311
|
|
|
300
312
|
const mapStateToProps = ({ userUpdating, userCreating, groups }) => ({
|
|
301
313
|
isSubmitting: userUpdating || userCreating,
|
|
302
|
-
groups:
|
|
314
|
+
groups: groups || [],
|
|
303
315
|
});
|
|
304
316
|
|
|
305
317
|
export default connect(mapStateToProps)(UserForm);
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { render } from "@truedat/test/render";
|
|
2
|
+
import { GroupUserCrumbs } from "../GroupUserCrumbs";
|
|
3
|
+
|
|
4
|
+
describe("<GroupUserCrumbs />", () => {
|
|
5
|
+
const group = {
|
|
6
|
+
id: "1",
|
|
7
|
+
name: "administrators",
|
|
8
|
+
alias: "Admins",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
it("renders alias when action is show", () => {
|
|
12
|
+
const rendered = render(
|
|
13
|
+
<GroupUserCrumbs group={group} action="show" groupLoading={false} />
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
expect(rendered.getByText("Admins")).toBeInTheDocument();
|
|
17
|
+
expect(rendered.queryByText("administrators")).not.toBeInTheDocument();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("renders alias link when action is add", () => {
|
|
21
|
+
const rendered = render(
|
|
22
|
+
<GroupUserCrumbs group={group} action="add" groupLoading={false} />
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
expect(rendered.getByRole("link", { name: /Admins/i })).toBeInTheDocument();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("renders nothing while group is loading", () => {
|
|
29
|
+
const rendered = render(
|
|
30
|
+
<GroupUserCrumbs group={group} action="show" groupLoading />
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
expect(rendered.container).toBeEmptyDOMElement();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("renders nothing when group has no name", () => {
|
|
37
|
+
const rendered = render(
|
|
38
|
+
<GroupUserCrumbs
|
|
39
|
+
group={{ id: "1", name: "", alias: "Admins" }}
|
|
40
|
+
action="show"
|
|
41
|
+
groupLoading={false}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
expect(rendered.container).toBeEmptyDOMElement();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("renders group name when alias is missing", () => {
|
|
49
|
+
const rendered = render(
|
|
50
|
+
<GroupUserCrumbs
|
|
51
|
+
group={{ id: "1", name: "administrators" }}
|
|
52
|
+
action="add"
|
|
53
|
+
groupLoading={false}
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
expect(
|
|
58
|
+
rendered.getByRole("link", { name: /administrators/i })
|
|
59
|
+
).toBeInTheDocument();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("renders group name on show when alias is missing", () => {
|
|
63
|
+
const rendered = render(
|
|
64
|
+
<GroupUserCrumbs
|
|
65
|
+
group={{ id: "1", name: "administrators" }}
|
|
66
|
+
action="show"
|
|
67
|
+
groupLoading={false}
|
|
68
|
+
/>
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
expect(rendered.getByText(/administrators/i)).toBeInTheDocument();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -98,4 +98,86 @@ describe("<UserForm />", () => {
|
|
|
98
98
|
})
|
|
99
99
|
);
|
|
100
100
|
});
|
|
101
|
+
|
|
102
|
+
it("shows group alias values in the groups dropdown", async () => {
|
|
103
|
+
const groups = [
|
|
104
|
+
{ name: "admins", alias: "Administrators" },
|
|
105
|
+
{ name: "users", alias: "Users" },
|
|
106
|
+
];
|
|
107
|
+
const rendered = render(<UserForm groups={groups} />, {
|
|
108
|
+
...renderOpts,
|
|
109
|
+
state: { ...renderOpts.state, groups },
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
await waitForLoad(rendered);
|
|
113
|
+
|
|
114
|
+
const user = userEvent.setup({ delay: null });
|
|
115
|
+
await user.click(rendered.getByRole("combobox"));
|
|
116
|
+
|
|
117
|
+
await waitFor(() =>
|
|
118
|
+
expect(rendered.getByText("Administrators")).toBeInTheDocument()
|
|
119
|
+
);
|
|
120
|
+
expect(rendered.getByText("Users")).toBeInTheDocument();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("submits external id and custom groups values", async () => {
|
|
124
|
+
const onSubmit = jest.fn();
|
|
125
|
+
const groups = [
|
|
126
|
+
{ name: "admins", alias: "Administrators" },
|
|
127
|
+
{ name: "users", alias: "Users" },
|
|
128
|
+
];
|
|
129
|
+
const rendered = render(
|
|
130
|
+
<UserForm
|
|
131
|
+
onSubmit={(props) => onSubmit(props)}
|
|
132
|
+
groups={groups}
|
|
133
|
+
user={{
|
|
134
|
+
id: 123,
|
|
135
|
+
user_name: "fred",
|
|
136
|
+
full_name: "fredrik foobar",
|
|
137
|
+
groups: ["zzz", "admins", "ZZZ"],
|
|
138
|
+
}}
|
|
139
|
+
/>,
|
|
140
|
+
{
|
|
141
|
+
...renderOpts,
|
|
142
|
+
state: {
|
|
143
|
+
...renderOpts.state,
|
|
144
|
+
groups,
|
|
145
|
+
},
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
await waitForLoad(rendered);
|
|
149
|
+
|
|
150
|
+
const user = userEvent.setup({ delay: null });
|
|
151
|
+
const groupsDropdown = rendered.container.querySelector(
|
|
152
|
+
'.ui.dropdown[name="groups"]'
|
|
153
|
+
);
|
|
154
|
+
const externalIdInput = rendered.getByRole("textbox", {
|
|
155
|
+
name: /external_id/i,
|
|
156
|
+
});
|
|
157
|
+
const selectedGroupDeleteIcon = rendered.container.querySelector(
|
|
158
|
+
'.ui.dropdown[name="groups"] .ui.label[value="zzz"] .delete.icon'
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
await user.click(groupsDropdown);
|
|
162
|
+
await user.click(selectedGroupDeleteIcon);
|
|
163
|
+
await user.clear(externalIdInput);
|
|
164
|
+
await user.type(externalIdInput, "ext-123");
|
|
165
|
+
|
|
166
|
+
await waitFor(() =>
|
|
167
|
+
expect(rendered.getByRole("button", { name: /save/i })).not.toBeDisabled()
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
await user.click(rendered.getByRole("button", { name: /save/i }));
|
|
171
|
+
|
|
172
|
+
await waitFor(() =>
|
|
173
|
+
expect(onSubmit).toHaveBeenCalledWith({
|
|
174
|
+
email: "",
|
|
175
|
+
external_id: "ext-123",
|
|
176
|
+
full_name: "fredrik foobar",
|
|
177
|
+
groups: ["admins", "ZZZ"],
|
|
178
|
+
role: "user",
|
|
179
|
+
user_name: "fred",
|
|
180
|
+
})
|
|
181
|
+
);
|
|
182
|
+
});
|
|
101
183
|
});
|
|
@@ -8,6 +8,8 @@ const renderOpts = {
|
|
|
8
8
|
"user.acl.role": "role",
|
|
9
9
|
"user.acl.domain": "domain",
|
|
10
10
|
"user.acl": "acl",
|
|
11
|
+
"user.acl.group": "group",
|
|
12
|
+
"user.group.acl": "group acl",
|
|
11
13
|
},
|
|
12
14
|
},
|
|
13
15
|
state: {
|
|
@@ -33,4 +35,23 @@ describe("<UserGroupAcls />", () => {
|
|
|
33
35
|
const { container } = render(<UserGroupAcls />, renderOpts);
|
|
34
36
|
await waitFor(() => expect(container).toMatchSnapshot());
|
|
35
37
|
});
|
|
38
|
+
|
|
39
|
+
it("renders group alias when provided by the selector", async () => {
|
|
40
|
+
const rendered = render(
|
|
41
|
+
<UserGroupAcls
|
|
42
|
+
acls={[
|
|
43
|
+
{
|
|
44
|
+
resource: "resource1",
|
|
45
|
+
role: "role1",
|
|
46
|
+
group: "Group One",
|
|
47
|
+
},
|
|
48
|
+
]}
|
|
49
|
+
/>,
|
|
50
|
+
renderOpts
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
await waitFor(() =>
|
|
54
|
+
expect(rendered.queryByText(/group one/i)).toBeInTheDocument()
|
|
55
|
+
);
|
|
56
|
+
});
|
|
36
57
|
});
|
|
@@ -248,15 +248,9 @@ exports[`<EditUser /> matches the latest snapshot 1`] = `
|
|
|
248
248
|
role="listbox"
|
|
249
249
|
>
|
|
250
250
|
<div
|
|
251
|
-
|
|
252
|
-
aria-selected="true"
|
|
253
|
-
class="selected item"
|
|
254
|
-
role="option"
|
|
255
|
-
style="pointer-events: all;"
|
|
251
|
+
class="message"
|
|
256
252
|
>
|
|
257
|
-
|
|
258
|
-
class="text"
|
|
259
|
-
/>
|
|
253
|
+
No results found.
|
|
260
254
|
</div>
|
|
261
255
|
</div>
|
|
262
256
|
</div>
|
|
@@ -228,15 +228,9 @@ exports[`<InitialUser /> matches the latest snapshot 1`] = `
|
|
|
228
228
|
role="listbox"
|
|
229
229
|
>
|
|
230
230
|
<div
|
|
231
|
-
|
|
232
|
-
aria-selected="true"
|
|
233
|
-
class="selected item"
|
|
234
|
-
role="option"
|
|
235
|
-
style="pointer-events: all;"
|
|
231
|
+
class="message"
|
|
236
232
|
>
|
|
237
|
-
|
|
238
|
-
class="text"
|
|
239
|
-
/>
|
|
233
|
+
No results found.
|
|
240
234
|
</div>
|
|
241
235
|
</div>
|
|
242
236
|
</div>
|
|
@@ -248,15 +248,9 @@ exports[`<NewUser /> matches the latest snapshot 1`] = `
|
|
|
248
248
|
role="listbox"
|
|
249
249
|
>
|
|
250
250
|
<div
|
|
251
|
-
|
|
252
|
-
aria-selected="true"
|
|
253
|
-
class="selected item"
|
|
254
|
-
role="option"
|
|
255
|
-
style="pointer-events: all;"
|
|
251
|
+
class="message"
|
|
256
252
|
>
|
|
257
|
-
|
|
258
|
-
class="text"
|
|
259
|
-
/>
|
|
253
|
+
No results found.
|
|
260
254
|
</div>
|
|
261
255
|
</div>
|
|
262
256
|
</div>
|
|
@@ -212,15 +212,9 @@ exports[`<UserForm /> matches the latest snapshot 1`] = `
|
|
|
212
212
|
role="listbox"
|
|
213
213
|
>
|
|
214
214
|
<div
|
|
215
|
-
|
|
216
|
-
aria-selected="true"
|
|
217
|
-
class="selected item"
|
|
218
|
-
role="option"
|
|
219
|
-
style="pointer-events: all;"
|
|
215
|
+
class="message"
|
|
220
216
|
>
|
|
221
|
-
|
|
222
|
-
class="text"
|
|
223
|
-
/>
|
|
217
|
+
No results found.
|
|
224
218
|
</div>
|
|
225
219
|
</div>
|
|
226
220
|
</div>
|
|
@@ -1,40 +1,43 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getUserGroupAcls } from "..";
|
|
2
2
|
|
|
3
|
-
describe("selectors:
|
|
4
|
-
const
|
|
3
|
+
describe("selectors: getUserGroupAcls", () => {
|
|
4
|
+
const aclWithAlias = {
|
|
5
5
|
resource: { id: 1, name: "resource1", type: "domain" },
|
|
6
|
-
role: { id: 2, name: "role1" }
|
|
6
|
+
role: { id: 2, name: "role1" },
|
|
7
|
+
group: { id: 6, name: "group5", alias: "Group Five" },
|
|
7
8
|
};
|
|
8
9
|
|
|
9
|
-
const
|
|
10
|
+
const aclWithoutAlias = {
|
|
10
11
|
resource: { id: 2, name: "resource2", type: "domain" },
|
|
11
|
-
role: { id: 3, name: "role2" }
|
|
12
|
+
role: { id: 3, name: "role2" },
|
|
13
|
+
group: { id: 7, name: "group6" },
|
|
12
14
|
};
|
|
13
15
|
|
|
14
|
-
const
|
|
16
|
+
const aclWithoutGroup = {
|
|
15
17
|
resource: { id: 4, name: "resource3", type: "domain" },
|
|
16
18
|
role: { id: 5, name: "role5" },
|
|
17
|
-
group: { id: 6, name: "group5" }
|
|
18
19
|
};
|
|
19
20
|
|
|
20
|
-
const
|
|
21
|
+
const user = { acls: [aclWithAlias, aclWithoutAlias, aclWithoutGroup] };
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
it("returns only group ACLs and prefers group.alias over group.name", () => {
|
|
24
|
+
const res = getUserGroupAcls({ user });
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
expect(res).toEqual([
|
|
27
|
+
{
|
|
28
|
+
resource: aclWithAlias.resource.name,
|
|
29
|
+
role: aclWithAlias.role.name,
|
|
30
|
+
group: aclWithAlias.group.alias,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
resource: aclWithoutAlias.resource.name,
|
|
34
|
+
role: aclWithoutAlias.role.name,
|
|
35
|
+
group: aclWithoutAlias.group.name,
|
|
36
|
+
},
|
|
37
|
+
]);
|
|
33
38
|
});
|
|
34
39
|
|
|
35
|
-
it("
|
|
36
|
-
|
|
37
|
-
expect(res).toHaveLength(0);
|
|
38
|
-
expect(res).toEqual(expect.arrayContaining([]));
|
|
40
|
+
it("returns an empty array when the user has no ACLs", () => {
|
|
41
|
+
expect(getUserGroupAcls({ user: { acls: [] } })).toEqual([]);
|
|
39
42
|
});
|
|
40
43
|
});
|
|
@@ -2,15 +2,15 @@ import _ from "lodash/fp";
|
|
|
2
2
|
import { createSelector } from "reselect";
|
|
3
3
|
import { getUser } from "./getUser";
|
|
4
4
|
|
|
5
|
-
const isGroupAcl = m => !_.flow(_.prop("group"), _.isEmpty)(m);
|
|
5
|
+
const isGroupAcl = (m) => !_.flow(_.prop("group"), _.isEmpty)(m);
|
|
6
6
|
|
|
7
|
-
const buildGroupAcl = m => ({
|
|
7
|
+
const buildGroupAcl = (m) => ({
|
|
8
8
|
resource: m.resource.name,
|
|
9
9
|
role: m.role.name,
|
|
10
|
-
group: m.group.name
|
|
10
|
+
group: m.group.alias || m.group.name,
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
-
export const getUserGroupAcls = createSelector([getUser], user =>
|
|
13
|
+
export const getUserGroupAcls = createSelector([getUser], (user) =>
|
|
14
14
|
_.flow(
|
|
15
15
|
_.prop("acls"),
|
|
16
16
|
_.filter(isGroupAcl),
|