@webitel/ui-sdk 24.12.95 → 24.12.97

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.
Files changed (36) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/ui-sdk.css +1 -1
  3. package/dist/ui-sdk.js +3179 -3219
  4. package/dist/ui-sdk.umd.cjs +16 -16
  5. package/package.json +14 -8
  6. package/src/api/defaults/getDefaultInstance/getDefaultInstance.js +5 -2
  7. package/src/api/defaults/index.js +1 -1
  8. package/src/components/wt-dual-panel/wt-dual-panel.vue +1 -2
  9. package/src/components/wt-icon-action/iconMappings.js +1 -1
  10. package/src/composables/useAccessControl/v2/createUserAccessControl.ts +66 -0
  11. package/src/composables/useAccessControl/v2/types/CreateUserAccessControl.d.ts +21 -0
  12. package/src/enums/CrudAction/CrudAction.js +6 -0
  13. package/src/enums/CrudAction/CrudAction.ts +8 -0
  14. package/src/enums/WtObject/WtObject.js +51 -1
  15. package/src/enums/WtObject/WtObject.ts +51 -1
  16. package/src/enums/index.js +6 -19
  17. package/src/enums/index.ts +33 -0
  18. package/src/locale/en/en.js +1 -1
  19. package/src/locale/ru/ru.js +1 -1
  20. package/src/locale/ua/ua.js +1 -1
  21. package/src/modules/Filters/v2/headers/createTableHeadersStore.ts +127 -78
  22. package/src/modules/Filters/v2/persist/usePersistedStorage.ts +0 -5
  23. package/src/modules/Filters/v2/table/createTableStore.store.ts +3 -2
  24. package/src/modules/Userinfo/api/userinfo.js +21 -34
  25. package/src/modules/Userinfo/store/UserinfoStoreModule.js +1 -1
  26. package/src/modules/Userinfo/v2/api/UserinfoAPI.ts +62 -0
  27. package/src/modules/Userinfo/v2/enums/GlobalActions/GlobalActions.ts +36 -0
  28. package/src/modules/Userinfo/v2/enums/ScopeClass/ScopeClass.ts +47 -0
  29. package/src/modules/Userinfo/v2/enums/index.ts +7 -0
  30. package/src/modules/Userinfo/v2/index.ts +0 -0
  31. package/src/modules/Userinfo/v2/mappings/mappings.ts +161 -0
  32. package/src/modules/Userinfo/v2/scripts/utils.ts +123 -0
  33. package/src/modules/Userinfo/v2/stores/__tests__/accessStore.spec.ts +136 -0
  34. package/src/modules/Userinfo/v2/stores/accessStore.ts +131 -0
  35. package/src/modules/Userinfo/v2/stores/userinfoStore.ts +56 -0
  36. package/src/modules/Userinfo/v2/types/UserAccess.d.ts +118 -0
@@ -32,7 +32,7 @@ export const createTableStore = <Entity extends { id: string; etag?: string }>(
32
32
 
33
33
  const headersStore = useHeadersStore();
34
34
  const { headers, shownHeaders, fields, sort } = storeToRefs(headersStore);
35
- const { updateSort, updateShownHeaders } = headersStore;
35
+ const { updateSort, updateShownHeaders, setupPersistence: setupHeadersPersistence } = headersStore;
36
36
 
37
37
  const filtersStore = useFiltersStore();
38
38
  const { filtersManager, isRestoring: isFiltersRestoring } =
@@ -116,12 +116,13 @@ export const createTableStore = <Entity extends { id: string; etag?: string }>(
116
116
  await Promise.allSettled([
117
117
  setupPaginationPersistence(),
118
118
  setupFiltersPersistence(),
119
+ setupHeadersPersistence(),
119
120
  ]);
120
121
 
121
122
  let loadingAfterFiltersChange = false;
122
123
 
123
124
  watch(
124
- [() => filtersManager.value.getAllValues(), sort, size],
125
+ [() => filtersManager.value.getAllValues(), sort, fields, size],
125
126
  async () => {
126
127
  loadingAfterFiltersChange = true;
127
128
  updatePage(1);
@@ -1,38 +1,25 @@
1
- import applyTransform, {
2
- notify,
3
- snakeToCamel,
4
- } from '../../../api/transformers/index.js';
1
+ import {
2
+ getSession,
3
+ getUiVisibilityAccess,
4
+ logout,
5
+ setInstance,
6
+ } from '../v2/api/UserinfoAPI';
5
7
 
6
- const userinfo = (instance) => ({
7
- async getSession() {
8
- const url = '/userinfo';
9
- try {
10
- const response = await instance.get(url);
11
- return applyTransform(response.data, [snakeToCamel()]);
12
- } catch (err) {
13
- throw applyTransform(err, [notify]);
14
- }
15
- },
8
+ /**
9
+ * @deprecated remove after v25.06 release
10
+ * @description
11
+ * backward compat
12
+ * */
13
+ const userinfo = (instance) => {
14
+ if (instance) {
15
+ setInstance(instance);
16
+ }
16
17
 
17
- async getApplicationsAccess() {
18
- const url = 'role/metadata/access';
19
- try {
20
- const response = await instance.get(url);
21
- return applyTransform(response.data, [snakeToCamel()]);
22
- } catch (err) {
23
- throw applyTransform(err, [notify]);
24
- }
25
- },
26
-
27
- async logout() {
28
- const url = '/logout';
29
-
30
- try {
31
- return await instance.post(url, {});
32
- } catch (err) {
33
- throw applyTransform(err, [notify]);
34
- }
35
- },
36
- });
18
+ return {
19
+ getSession,
20
+ logout,
21
+ getUiVisibilityAccess,
22
+ };
23
+ };
37
24
 
38
25
  export default userinfo;
@@ -117,7 +117,7 @@ export default class UserinfoStoreModule extends BaseStoreModule {
117
117
  }
118
118
 
119
119
  await context.dispatch('SET_SESSION', session);
120
- const access = await userinfo.getApplicationsAccess();
120
+ const access = await userinfo.getUiVisibilityAccess();
121
121
  await context.dispatch(
122
122
  'SET_APPLICATIONS_ACCESS',
123
123
  new ApplicationsAccess({ access }).getAccess(),
@@ -0,0 +1,62 @@
1
+ import type { AxiosInstance } from 'axios';
2
+
3
+ import getDefaultInstance from '../../../../api/defaults/getDefaultInstance/getDefaultInstance';
4
+ import applyTransform, {
5
+ merge,
6
+ notify,
7
+ snakeToCamel,
8
+ } from '../../../../api/transformers/index.js';
9
+ import type {
10
+ GlobalAccessApiResponseItem,
11
+ ScopeAccessApiResponseItem,
12
+ VisibilityAccess,
13
+ } from '../types/UserAccess';
14
+
15
+ let instance = getDefaultInstance();
16
+
17
+ const setInstance = (newInstance: AxiosInstance) => {
18
+ instance = newInstance;
19
+ };
20
+
21
+ const getSession = async (): Promise<{
22
+ scope: ScopeAccessApiResponseItem[];
23
+ permissions: GlobalAccessApiResponseItem[];
24
+ userId: string;
25
+ }> => {
26
+ const url = '/userinfo';
27
+ try {
28
+ const defaultObject = () => ({
29
+ scope: [],
30
+ permissions: [],
31
+ });
32
+
33
+ const response = await instance.get(url);
34
+ return applyTransform(response.data, [
35
+ merge(defaultObject()),
36
+ snakeToCamel(),
37
+ ]);
38
+ } catch (err) {
39
+ throw applyTransform(err, [notify]);
40
+ }
41
+ };
42
+
43
+ const getUiVisibilityAccess = async (): Promise<VisibilityAccess> => {
44
+ const url = 'role/metadata/access';
45
+ try {
46
+ const response = await instance.get(url);
47
+ return applyTransform(response.data, [snakeToCamel()]);
48
+ } catch (err) {
49
+ throw applyTransform(err, [notify]);
50
+ }
51
+ };
52
+
53
+ const logout = async () => {
54
+ const url = '/logout';
55
+ try {
56
+ return await instance.post(url, {});
57
+ } catch (err) {
58
+ throw applyTransform(err, [notify]);
59
+ }
60
+ };
61
+
62
+ export { getSession, getUiVisibilityAccess, logout, setInstance };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @internal
3
+ * @description
4
+ * Represents backend response
5
+ * format for Global Access to Crud Actions.
6
+ * Should be converted to {@link CrudAction}
7
+ * */
8
+ export const CrudGlobalAction = {
9
+ Read: 'read',
10
+ Write: 'write',
11
+ Delete: 'delete',
12
+ Add: 'add',
13
+ } as const;
14
+
15
+ export type CrudGlobalAction =
16
+ (typeof CrudGlobalAction)[keyof typeof CrudGlobalAction];
17
+
18
+ /**
19
+ * @description
20
+ * Represents access to global specific actions
21
+ * like downloading files or exporting data
22
+ * */
23
+ export const SpecialGlobalAction = {
24
+ PlaybackRecordFile: 'playbackRecordFile',
25
+ ManageUserLicense: 'manageUserLicense',
26
+ SchemeVariables: 'schemeVariables',
27
+ SystemSetting: 'systemSetting',
28
+ ManageUserRoles: 'manageUserRoles',
29
+ ExportDataGrid: 'exportDataGrid',
30
+ ViewCdrPhoneNumbers: 'viewCdrPhoneNumbers',
31
+ ChangeUserPassword: 'changeUserPassword',
32
+ EavesdropCall: 'eavesdropCall',
33
+ } as const;
34
+
35
+ export type SpecialGlobalAction =
36
+ (typeof SpecialGlobalAction)[keyof typeof SpecialGlobalAction];
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @description
3
+ * backend `scope` field userinfo response classes
4
+ * `class` represents a group of entities, managed by this one class
5
+ */
6
+ export const ScopeClass = {
7
+ /**
8
+ * Includes:
9
+ * - {@link WtObject.Agent}
10
+ * - {@link }
11
+ */
12
+ Agent: 'cc_agent',
13
+ Queue: 'cc_queue',
14
+ Dictionaries: 'dictionaries',
15
+ EmailProfile: 'email_profile',
16
+
17
+ Skills: 'cc_skill',
18
+ Users: 'users',
19
+ Devices: 'devices',
20
+ AcrRouting: 'acr_routing',
21
+ Gateways: 'gateways',
22
+ AcrChatPlan: 'acr_chat_plan',
23
+ Chats: 'chats',
24
+ List: 'cc_list',
25
+ Calendars: 'calendars',
26
+ Team: 'cc_team',
27
+ ResourceGroup: 'cc_resource_group',
28
+ Resource: 'cc_resource',
29
+ StorageProfile: 'storage_profile',
30
+ CognitiveProfile: 'cognitive_profile',
31
+ SingleSignOn: 'single_sign_on',
32
+ ImportTemplate: 'import_template',
33
+ Trigger: 'trigger',
34
+ Schema: 'schema',
35
+ Role: 'roles',
36
+ Contacts: 'contacts',
37
+ MediaFile: 'media_file',
38
+ Logger: 'logger',
39
+ Calls: 'calls',
40
+ RecordFile: 'record_file',
41
+ ContactGroups: 'contact_groups',
42
+ ChatBots: 'chat_bots',
43
+ Cases: 'cases',
44
+ CaseComments: 'case_comments',
45
+ } as const;
46
+
47
+ export type ScopeClass = (typeof ScopeClass)[keyof typeof ScopeClass];
@@ -0,0 +1,7 @@
1
+ import {
2
+ CrudGlobalAction,
3
+ SpecialGlobalAction,
4
+ } from './GlobalActions/GlobalActions';
5
+ import { ScopeClass } from './ScopeClass/ScopeClass';
6
+
7
+ export { CrudGlobalAction, ScopeClass, SpecialGlobalAction };
File without changes
@@ -0,0 +1,161 @@
1
+ import invert from 'lodash/fp/invert';
2
+
3
+ import {
4
+ AdminSections,
5
+ AuditorSections,
6
+ CrmSections,
7
+ CrudAction,
8
+ SupervisorSections,
9
+ WtApplication,
10
+ WtObject,
11
+ } from '../../../../enums';
12
+ import { CrudGlobalAction, ScopeClass } from '../enums';
13
+
14
+ export const mapGlobalActionToCrudAction = {
15
+ [CrudGlobalAction.Add]: CrudAction.Create,
16
+ [CrudGlobalAction.Read]: CrudAction.Read,
17
+ [CrudGlobalAction.Write]: CrudAction.Update,
18
+ [CrudGlobalAction.Delete]: CrudAction.Delete,
19
+ };
20
+
21
+ export const mapCrudActionToGlobalAction = invert(mapGlobalActionToCrudAction);
22
+
23
+ /* one-to-many */
24
+ export const mapScopeClassToWtObjects: Record<ScopeClass, WtObject[]> = {
25
+ [ScopeClass.Users]: [WtObject.User],
26
+ [ScopeClass.Devices]: [WtObject.Device],
27
+ [ScopeClass.Schema]: [WtObject.Flow],
28
+ [ScopeClass.AcrRouting]: [WtObject.Dialplan],
29
+ [ScopeClass.Gateways]: [WtObject.Gateway],
30
+ [ScopeClass.AcrChatPlan]: [WtObject.Chatplan],
31
+ [ScopeClass.Chats]: [WtObject.ChatGateway],
32
+ [ScopeClass.Dictionaries]: [
33
+ WtObject.Region,
34
+ WtObject.Bucket,
35
+ WtObject.Communication,
36
+ WtObject.PauseCause,
37
+ ],
38
+ [ScopeClass.List]: [WtObject.Blacklist],
39
+ [ScopeClass.Skills]: [WtObject.Skill],
40
+ [ScopeClass.Calendars]: [WtObject.Calendar],
41
+ [ScopeClass.MediaFile]: [WtObject.Media],
42
+ [ScopeClass.Agent]: [WtObject.Agent],
43
+ [ScopeClass.Queue]: [WtObject.Queue, WtObject.Member],
44
+ [ScopeClass.ResourceGroup]: [WtObject.ResourceGroup],
45
+ [ScopeClass.Resource]: [WtObject.Resource],
46
+ [ScopeClass.Team]: [WtObject.Team],
47
+ [ScopeClass.StorageProfile]: [WtObject.Storage],
48
+ [ScopeClass.CognitiveProfile]: [WtObject.CognitiveProfile],
49
+ [ScopeClass.EmailProfile]: [WtObject.EmailProfile],
50
+ [ScopeClass.SingleSignOn]: [WtObject.SingleSignOn],
51
+ [ScopeClass.ImportTemplate]: [WtObject.ImportCsv],
52
+ [ScopeClass.Trigger]: [WtObject.Trigger],
53
+ [ScopeClass.Role]: [WtObject.Role],
54
+ [ScopeClass.Contacts]: [WtObject.Contact],
55
+ [ScopeClass.Logger]: [WtObject.Logger], // Change log in Admin
56
+ [ScopeClass.Calls]: [WtObject.Call], // Call history
57
+ [ScopeClass.RecordFile]: [WtObject.RecordFile], // Call history
58
+ [ScopeClass.ContactGroups]: [WtObject.ContactGroup], //CRM
59
+ [ScopeClass.ChatBots]: [WtObject.ChatBot], // routing cht_gateway
60
+ [ScopeClass.Cases]: [WtObject.Cases], // CRM
61
+ [ScopeClass.CaseComments]: [WtObject.CaseComment],
62
+ };
63
+
64
+ export const mapScopeClassAccessTokenToCrudAction = {
65
+ r: CrudAction.Read,
66
+ w: CrudAction.Update,
67
+ d: CrudAction.Delete,
68
+ x: CrudAction.Create,
69
+ };
70
+
71
+ export const mapCrudActionToScopeClassAccessToken = invert(
72
+ mapScopeClassAccessTokenToCrudAction,
73
+ );
74
+
75
+ type UiSection =
76
+ | AdminSections
77
+ | AuditorSections
78
+ | CrmSections
79
+ | SupervisorSections;
80
+ export const mapWtObjectToUiSection: Record<
81
+ WtApplication,
82
+ Partial<Record<WtObject, UiSection>>
83
+ > = {
84
+ // if we don`t need empty objects we can use Partial<Record<WtApplication, Partial<Record<WtObject, UiSection>>>>
85
+ [WtApplication.Agent]: {},
86
+ [WtApplication.History]: {},
87
+ [WtApplication.Analytics]: {},
88
+
89
+ // Admin sections
90
+ [WtApplication.Admin]: {
91
+ [WtObject.User]: AdminSections.Users,
92
+ [WtObject.Agent]: AdminSections.Agents,
93
+ [WtObject.License]: AdminSections.License,
94
+ [WtObject.Device]: AdminSections.Devices,
95
+ [WtObject.Flow]: AdminSections.Flow,
96
+ [WtObject.Dialplan]: AdminSections.Dialplan,
97
+ [WtObject.Gateway]: AdminSections.Gateways,
98
+ [WtObject.Chatplan]: AdminSections.Chatplan,
99
+ [WtObject.ChatGateway]: AdminSections.ChatGateways,
100
+ [WtObject.Skill]: AdminSections.Skills,
101
+ [WtObject.Bucket]: AdminSections.Buckets,
102
+ [WtObject.Blacklist]: AdminSections.Blacklist,
103
+ [WtObject.Region]: AdminSections.Regions,
104
+ [WtObject.Calendar]: AdminSections.Calendars,
105
+ [WtObject.Communication]: AdminSections.Communications,
106
+ [WtObject.PauseCause]: AdminSections.PauseCause,
107
+ [WtObject.Media]: AdminSections.Media,
108
+ [WtObject.Team]: AdminSections.Teams,
109
+ [WtObject.Resource]: AdminSections.Resources,
110
+ [WtObject.ResourceGroup]: AdminSections.ResourceGroups,
111
+ [WtObject.Queue]: AdminSections.Queues,
112
+ [WtObject.Storage]: AdminSections.Storage,
113
+ [WtObject.CognitiveProfile]: AdminSections.CognitiveProfiles,
114
+ [WtObject.EmailProfile]: AdminSections.EmailProfiles,
115
+ [WtObject.ImportCsv]: AdminSections.ImportCsv,
116
+ [WtObject.Trigger]: AdminSections.Triggers,
117
+ [WtObject.Role]: AdminSections.Roles,
118
+ [WtObject.Object]: AdminSections.Objects,
119
+ [WtObject.ChangeLog]: AdminSections.Changelogs,
120
+ [WtObject.Configuration]: AdminSections.Configuration,
121
+ [WtObject.GlobalVariable]: AdminSections.GlobalVariables,
122
+ [WtObject.ShiftTemplate]: AdminSections.ShiftTemplates,
123
+ [WtObject.PauseTemplate]: AdminSections.PauseTemplates,
124
+ [WtObject.WorkingCondition]: AdminSections.WorkingConditions,
125
+ [WtObject.Member]: AdminSections.Members,
126
+ },
127
+
128
+ // Auditor sections
129
+ [WtApplication.Audit]: {
130
+ [WtObject.Scorecard]: AuditorSections.Scorecards,
131
+ },
132
+
133
+ // Crm sections
134
+ [WtApplication.Crm]: {
135
+ [WtObject.Contact]: CrmSections.Contacts,
136
+ [WtObject.Cases]: CrmSections.Cases,
137
+ [WtObject.Slas]: CrmSections.Slas,
138
+ [WtObject.ServiceCatalog]: CrmSections.ServiceCatalogs,
139
+ [WtObject.CaseSource]: CrmSections.CaseSources,
140
+ [WtObject.CloseReasonGroup]: CrmSections.CloseReasonGroups,
141
+ [WtObject.Priorities]: CrmSections.Priorities,
142
+ [WtObject.Status]: CrmSections.Statuses,
143
+ [WtObject.Source]: CrmSections.Sources,
144
+ [WtObject.ContactGroup]: CrmSections.ContactGroups,
145
+ [WtObject.CustomLookup]: CrmSections.CustomLookups,
146
+ },
147
+
148
+ // Supervisor sections
149
+ [WtApplication.Supervisor]: {
150
+ [WtObject.Queue]: SupervisorSections.Queues,
151
+ [WtObject.Agent]: SupervisorSections.Agents,
152
+ [WtObject.Communication]: SupervisorSections.ActiveCalls,
153
+ },
154
+ };
155
+
156
+ export const mapUiSectionToWtObject = invert(mapWtObjectToUiSection);
157
+
158
+ export const AdminSectionsValues = invert(AdminSections);
159
+ export const AuditorSectionsValues = invert(AuditorSections);
160
+ export const CrmSectionsValues = invert(CrmSections);
161
+ export const SupervisorSectionsValues = invert(SupervisorSections);
@@ -0,0 +1,123 @@
1
+ import { CrudAction, WtApplication, WtObject } from '../../../../enums';
2
+ import { _wtUiLog as wtlog } from '../../../../scripts/logger';
3
+ import {
4
+ AdminSectionsValues,
5
+ AuditorSectionsValues,
6
+ CrmSectionsValues,
7
+ mapGlobalActionToCrudAction,
8
+ mapScopeClassAccessTokenToCrudAction,
9
+ mapScopeClassToWtObjects,
10
+ mapUiSectionToWtObject,
11
+ mapWtObjectToUiSection,
12
+ SupervisorSectionsValues,
13
+ } from '../mappings/mappings';
14
+ import type {
15
+ AppVisibilityMap,
16
+ GlobalAccessApiResponseItem,
17
+ GlobalAction,
18
+ GlobalActionAccessMap,
19
+ ScopeAccessApiResponseItem,
20
+ ScopeAccessMap,
21
+ SectionVisibilityMap,
22
+ UiSection,
23
+ VisibilityAccess,
24
+ } from '../types/UserAccess.d.ts';
25
+
26
+ /**
27
+ * @internal
28
+ * @description
29
+ * backend -> frontend
30
+ * */
31
+ const castGlobalActionToCrudAction = (
32
+ globalAction: GlobalAction,
33
+ ): CrudAction | null => {
34
+ return mapGlobalActionToCrudAction[globalAction] || null;
35
+ };
36
+
37
+ export const makeGlobalAccessMap = (
38
+ rawGlobalAccess: GlobalAccessApiResponseItem[],
39
+ ): GlobalActionAccessMap => {
40
+ return rawGlobalAccess.reduce((map, { id }) => {
41
+ const key = castGlobalActionToCrudAction(id) || id;
42
+ return map.set(key, true);
43
+ }, new Map());
44
+ };
45
+
46
+ export const makeScopeAccessMap = (
47
+ rawScope: ScopeAccessApiResponseItem[],
48
+ ): ScopeAccessMap => {
49
+ const map = new Map();
50
+
51
+ rawScope.forEach(({ class: scopeClass, access: scopeAccess }) => {
52
+ const access = scopeAccess.split('').reduce((accessMap, token) => {
53
+ accessMap.set(mapScopeClassAccessTokenToCrudAction[token], true);
54
+ return accessMap;
55
+ }, new Map());
56
+
57
+ const scopeClassObjects = mapScopeClassToWtObjects[scopeClass];
58
+
59
+ if (!scopeClassObjects) {
60
+ wtlog.error({ module: 'modules/userinfo' })(
61
+ 'Unknown scope class to convert to WtObject:',
62
+ scopeClass,
63
+ );
64
+ map.set(scopeClass, access);
65
+ } else {
66
+ scopeClassObjects.forEach((object) => {
67
+ map.set(object, access);
68
+ });
69
+ }
70
+ });
71
+
72
+ return map;
73
+ };
74
+
75
+ export const makeAppVisibilityMap = (
76
+ rawVisibility: VisibilityAccess,
77
+ ): AppVisibilityMap => {
78
+ const map = new Map();
79
+ Object.entries(rawVisibility).forEach(([app, visibility]) => {
80
+ map.set(app, visibility._enabled);
81
+ });
82
+ return map;
83
+ };
84
+
85
+ export const makeSectionVisibilityMap = (
86
+ rawVisibility: VisibilityAccess,
87
+ ): SectionVisibilityMap => {
88
+ const map = new Map();
89
+
90
+ Object.values(rawVisibility).forEach((appSectionsVisibility) => {
91
+ Object.entries(appSectionsVisibility).forEach(([section, visibility]) => {
92
+ if (section.startsWith('_')) return map; // skip private fields
93
+ map.set(section, visibility._enabled);
94
+ });
95
+ });
96
+
97
+ return map;
98
+ };
99
+
100
+ export const castUiSectionToWtObject = (section: UiSection): WtObject => {
101
+ return mapUiSectionToWtObject[section];
102
+ };
103
+
104
+ export const castWtObjectToUiSection = (object: WtObject): UiSection => {
105
+ return mapWtObjectToUiSection[object];
106
+ };
107
+
108
+ export const getWtAppByUiSection = (section: UiSection): WtApplication => {
109
+ /* use inverted maps because UiSection is the enum value, not key */
110
+ if (AdminSectionsValues[section]) {
111
+ return WtApplication.Admin;
112
+ }
113
+ if (AuditorSectionsValues[section]) {
114
+ return WtApplication.Audit;
115
+ }
116
+ if (CrmSectionsValues[section]) {
117
+ return WtApplication.Crm;
118
+ }
119
+ if (SupervisorSectionsValues[section]) {
120
+ return WtApplication.Supervisor;
121
+ }
122
+ wtlog.error({ module: 'modules/userinfo' })('Unknown section:', section);
123
+ };
@@ -0,0 +1,136 @@
1
+ import {createPinia, setActivePinia, type StoreDefinition} from 'pinia';
2
+ import { beforeEach, describe, expect,it } from 'vitest';
3
+ import {createApp, h} from "vue";
4
+ import {createRouter, createWebHistory, type Router} from "vue-router";
5
+
6
+ import {AdminSections, WtApplication, WtObject} from "../../../../../enums";
7
+ import {CrudGlobalAction} from "../../enums";
8
+ import { createUserAccessStore } from '../accessStore';
9
+
10
+ describe('AccessStore', () => {
11
+ let router: Router;
12
+ let useAccessStore: StoreDefinition;
13
+
14
+ beforeEach(async () => {
15
+ router = createRouter({
16
+ history: createWebHistory(),
17
+ routes: [
18
+ {
19
+ path: '/',
20
+ name: 'home',
21
+ component: () => h('div', 'home'),
22
+ },
23
+ {
24
+ path: '/users',
25
+ name: 'users',
26
+ component: () => h('div', 'users'),
27
+ meta: {
28
+ WtObject: WtObject.User,
29
+ UiSection: AdminSections.Users,
30
+ },
31
+ },
32
+ ],
33
+ });
34
+
35
+ const pinia = createPinia();
36
+ const app = createApp({});
37
+ app.use(router);
38
+ app.use(pinia)
39
+ setActivePinia(pinia);
40
+ useAccessStore = createUserAccessStore();
41
+ });
42
+
43
+ it('restricts route if no access', async () => {
44
+ const { initialize, routeAccessGuard } = useAccessStore();
45
+ router.beforeEach(routeAccessGuard);
46
+
47
+ initialize({
48
+ permissions: [],
49
+ scope: [],
50
+ access: {},
51
+ });
52
+
53
+ await router.push({ name: 'users' });
54
+
55
+ /* because guard should not allow to navigate
56
+ there since we pass empty permissions */
57
+ expect(router.currentRoute.value.name).not.toBe('users');
58
+ });
59
+
60
+ it('allows route access if has global permission', async () => {
61
+ const { initialize, routeAccessGuard } = useAccessStore();
62
+ router.beforeEach(routeAccessGuard);
63
+
64
+ initialize({
65
+ permissions: [{ id: CrudGlobalAction.Read }],
66
+ scope: [],
67
+ access: {},
68
+ });
69
+
70
+ await router.push({ name: 'users' });
71
+ expect(router.currentRoute.value.name).toBe('users');
72
+ });
73
+
74
+ it('allows route access if has scope permission, app visibility and section visibility', async () => {
75
+ const { initialize, routeAccessGuard } = useAccessStore();
76
+ router.beforeEach(routeAccessGuard);
77
+
78
+ initialize({
79
+ permissions: [],
80
+ scope: [{ class: 'users', access: 'r' }],
81
+ access: {
82
+ [WtApplication.Admin]: {
83
+ _enabled: true,
84
+ [AdminSections.Users]: {
85
+ _enabled: true,
86
+ },
87
+ },
88
+ },
89
+ });
90
+
91
+ await router.push({ name: 'users' });
92
+ expect(router.currentRoute.value.name).toBe('users');
93
+ });
94
+
95
+ it('restricts route access if has scope permission, app visibility, but no section visibility', async () => {
96
+ const { initialize, routeAccessGuard } = useAccessStore();
97
+ router.beforeEach(routeAccessGuard);
98
+
99
+ initialize({
100
+ permissions: [],
101
+ scope: [{ class: 'users', access: 'r' }],
102
+ access: {
103
+ [WtApplication.Admin]: {
104
+ _enabled: true,
105
+ [AdminSections.Users]: {
106
+ _enabled: false,
107
+ },
108
+ },
109
+ },
110
+ });
111
+
112
+ await router.push({ name: 'users' });
113
+ expect(router.currentRoute.value.name).not.toBe('users');
114
+ });
115
+
116
+ it('restricts route access if has scope permission, section visibility but no app visibility', async () => {
117
+ const { initialize, routeAccessGuard } = useAccessStore();
118
+ router.beforeEach(routeAccessGuard);
119
+
120
+ initialize({
121
+ permissions: [],
122
+ scope: [{ class: 'users', access: 'r' }],
123
+ access: {
124
+ [WtApplication.Admin]: {
125
+ _enabled: false,
126
+ [AdminSections.Users]: {
127
+ _enabled: true,
128
+ },
129
+ },
130
+ },
131
+ });
132
+
133
+ await router.push({ name: 'users' });
134
+ expect(router.currentRoute.value.name).not.toBe('users');
135
+ });
136
+ });