@davra/ui-core 1.0.0-alpha.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/README.md +183 -0
- package/dist/style.css +1 -0
- package/dist/types/components/ComponentA.vue.d.ts +12 -0
- package/dist/types/components/ComponentB.vue.d.ts +2 -0
- package/dist/types/components/index.d.ts +3 -0
- package/dist/types/constants/MyConstants.d.ts +1 -0
- package/dist/types/constants/index.d.ts +2 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/services/davraApi.d.ts +5 -0
- package/dist/types/services/devicesCountersService.d.ts +10 -0
- package/dist/types/services/devicesCountersService.test.d.ts +1 -0
- package/dist/types/services/devicesService.d.ts +18 -0
- package/dist/types/services/devicesService.test.d.ts +1 -0
- package/dist/types/services/index.d.ts +10 -0
- package/dist/types/services/labelsService.d.ts +8 -0
- package/dist/types/services/labelsService.test.d.ts +1 -0
- package/dist/types/services/metricsCountersService.d.ts +5 -0
- package/dist/types/services/metricsCountersService.test.d.ts +1 -0
- package/dist/types/services/metricsService.d.ts +6 -0
- package/dist/types/services/metricsService.test.d.ts +1 -0
- package/dist/types/services/timeseriesService.d.ts +5 -0
- package/dist/types/services/timeseriesService.test.d.ts +1 -0
- package/dist/types/services/twinTypesService.d.ts +5 -0
- package/dist/types/services/twinTypesService.test.d.ts +1 -0
- package/dist/types/services/twinsCountersService.d.ts +6 -0
- package/dist/types/services/twinsCountersService.test.d.ts +1 -0
- package/dist/types/services/twinsService.d.ts +22 -0
- package/dist/types/services/twinsService.test.d.ts +1 -0
- package/dist/types/services/userSessionService.d.ts +12 -0
- package/dist/types/services/userSessionService.test.d.ts +1 -0
- package/dist/types/stores/alertMessages.d.ts +19 -0
- package/dist/types/stores/alertMessages.test.d.ts +1 -0
- package/dist/types/stores/devices.d.ts +42 -0
- package/dist/types/stores/devices.test.d.ts +1 -0
- package/dist/types/stores/index.d.ts +7 -0
- package/dist/types/stores/labels.d.ts +20 -0
- package/dist/types/stores/labels.test.d.ts +1 -0
- package/dist/types/stores/metrics.d.ts +39 -0
- package/dist/types/stores/metrics.test.d.ts +1 -0
- package/dist/types/stores/twinTypes.d.ts +27 -0
- package/dist/types/stores/twinTypes.test.d.ts +1 -0
- package/dist/types/stores/twins.d.ts +42 -0
- package/dist/types/stores/twins.test.d.ts +1 -0
- package/dist/types/stores/userSession.d.ts +24 -0
- package/dist/types/stores/userSession.test.d.ts +1 -0
- package/dist/types/types.d.ts +175 -0
- package/dist/types/utils/MyUtil.d.ts +5 -0
- package/dist/types/utils/index.d.ts +2 -0
- package/dist/ui-core.es.js +1982 -0
- package/package.json +46 -0
- package/src/assets/fonts/myfont.woff +0 -0
- package/src/assets/main.scss +17 -0
- package/src/auto-imports.d.ts +200 -0
- package/src/components/ComponentA.vue +13 -0
- package/src/components/ComponentB.vue +26 -0
- package/src/components/index.ts +7 -0
- package/src/constants/MyConstants.ts +1 -0
- package/src/constants/index.ts +5 -0
- package/src/env.d.ts +8 -0
- package/src/index.ts +23 -0
- package/src/services/davraApi.ts +18 -0
- package/src/services/devicesCountersService.test.ts +209 -0
- package/src/services/devicesCountersService.ts +117 -0
- package/src/services/devicesService.test.ts +207 -0
- package/src/services/devicesService.ts +110 -0
- package/src/services/index.ts +22 -0
- package/src/services/labelsService.test.ts +124 -0
- package/src/services/labelsService.ts +71 -0
- package/src/services/metricsCountersService.test.ts +44 -0
- package/src/services/metricsCountersService.ts +24 -0
- package/src/services/metricsService.test.ts +97 -0
- package/src/services/metricsService.ts +54 -0
- package/src/services/timeseriesService.test.ts +86 -0
- package/src/services/timeseriesService.ts +24 -0
- package/src/services/twinTypesService.test.ts +74 -0
- package/src/services/twinTypesService.ts +24 -0
- package/src/services/twinsCountersService.test.ts +72 -0
- package/src/services/twinsCountersService.ts +40 -0
- package/src/services/twinsService.test.ts +228 -0
- package/src/services/twinsService.ts +137 -0
- package/src/services/userSessionService.test.ts +74 -0
- package/src/services/userSessionService.ts +82 -0
- package/src/stores/alertMessages.test.ts +27 -0
- package/src/stores/alertMessages.ts +26 -0
- package/src/stores/devices.test.ts +149 -0
- package/src/stores/devices.ts +78 -0
- package/src/stores/index.ts +12 -0
- package/src/stores/labels.test.ts +72 -0
- package/src/stores/labels.ts +39 -0
- package/src/stores/metrics.test.ts +116 -0
- package/src/stores/metrics.ts +71 -0
- package/src/stores/twinTypes.test.ts +71 -0
- package/src/stores/twinTypes.ts +36 -0
- package/src/stores/twins.test.ts +148 -0
- package/src/stores/twins.ts +78 -0
- package/src/stores/userSession.test.ts +107 -0
- package/src/stores/userSession.ts +57 -0
- package/src/types.ts +173 -0
- package/src/utils/MyUtil.ts +7 -0
- package/src/utils/index.ts +5 -0
@@ -0,0 +1,148 @@
|
|
1
|
+
import { createPinia, setActivePinia } from 'pinia'
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
3
|
+
import { useTwinsStore } from './twins'
|
4
|
+
import twinsService from '~/services/twinsService'
|
5
|
+
import type { Twin } from '~/types'
|
6
|
+
vi.mock('~/services/davraApi')
|
7
|
+
|
8
|
+
describe('Twins Store', () => {
|
9
|
+
beforeEach(() => {
|
10
|
+
// creates a fresh pinia and make it active so it's automatically picked
|
11
|
+
// up by any useStore() call without having to pass it to it:
|
12
|
+
// `useStore(pinia)`
|
13
|
+
setActivePinia(createPinia())
|
14
|
+
})
|
15
|
+
|
16
|
+
it('pullTwinsByUUIDs should fetch twins by UUIDs and update the collection', async () => {
|
17
|
+
const store = useTwinsStore()
|
18
|
+
|
19
|
+
// Mock the TwinsService
|
20
|
+
const mockedTwin = { UUID: '123', name: 'Mocked twin' } as Twin
|
21
|
+
const mockedTwinsRequest = { totalRecords: 1, records: [mockedTwin] }
|
22
|
+
const mockedGetTwinByUUIDs = vi.spyOn(twinsService, 'getTwinByUUIDs').mockResolvedValue(mockedTwinsRequest)
|
23
|
+
|
24
|
+
// Call the store method
|
25
|
+
await store.pullTwinsByUUIDs(['123'], false)
|
26
|
+
|
27
|
+
// Check the result
|
28
|
+
expect(mockedGetTwinByUUIDs).toHaveBeenCalledWith(['123'])
|
29
|
+
expect(store.twinsCollection).toEqual({ 123: mockedTwin })
|
30
|
+
})
|
31
|
+
|
32
|
+
it('pullTwinsByUUIDs should fetch twins by UUIDs and update the collection and purge the previous records', async () => {
|
33
|
+
const store = useTwinsStore()
|
34
|
+
store.twinsCollection = { qwe124: { UUID: 'qwe124', name: 'Mocked twin' } as Twin }
|
35
|
+
// Mock the TwinsService
|
36
|
+
const mockedTwin = { UUID: '123', name: 'Mocked twin' } as Twin
|
37
|
+
const mockedTwinsRequest = { totalRecords: 1, records: [mockedTwin] }
|
38
|
+
const mockedGetTwinByUUIDs = vi.spyOn(twinsService, 'getTwinByUUIDs').mockResolvedValue(mockedTwinsRequest)
|
39
|
+
|
40
|
+
// Call the store method
|
41
|
+
await store.pullTwinsByUUIDs(['qwe124', '123'], true)
|
42
|
+
|
43
|
+
// Check the result
|
44
|
+
expect(mockedGetTwinByUUIDs).toHaveBeenCalledWith(['qwe124', '123'])
|
45
|
+
expect(store.twinsCollection).toEqual({ 123: mockedTwin })
|
46
|
+
})
|
47
|
+
|
48
|
+
it('pullTwinsByUUIDs should return empty records when the api throw an error', async () => {
|
49
|
+
const store = useTwinsStore()
|
50
|
+
store.twinsCollection = { qwe124: { UUID: 'qwe124', name: 'Mocked twin' } as Twin }
|
51
|
+
// Mock the TwinsService
|
52
|
+
const mockedGetTwinByUUIDs = vi.spyOn(twinsService, 'getTwinByUUIDs').mockRejectedValue(new Error('ERROR'))
|
53
|
+
|
54
|
+
// Call the store method
|
55
|
+
await store.pullTwinsByUUIDs(['qwe124', '123'], true)
|
56
|
+
|
57
|
+
// Check the result
|
58
|
+
expect(mockedGetTwinByUUIDs).toHaveBeenCalledWith(['qwe124', '123'])
|
59
|
+
expect(store.twinsCollection).toEqual({})
|
60
|
+
})
|
61
|
+
|
62
|
+
it('pullTwins should fetch twins and update the collection', async () => {
|
63
|
+
const store = useTwinsStore()
|
64
|
+
|
65
|
+
// Mock the TwinsService
|
66
|
+
const mockedTwin = { UUID: '123', name: 'Mocked twin' } as Twin
|
67
|
+
const mockedTwinsRequest = { totalRecords: 1, records: [mockedTwin] }
|
68
|
+
const mockedGetTwins = vi.spyOn(twinsService, 'getTwins').mockResolvedValue(mockedTwinsRequest)
|
69
|
+
|
70
|
+
// Call the store method
|
71
|
+
const result = await store.pullTwins(0, 10, '', '')
|
72
|
+
|
73
|
+
// Check the result
|
74
|
+
expect(mockedGetTwins).toHaveBeenCalledWith(0, 10, '', '')
|
75
|
+
expect(store.twinsCollection).toEqual({ 123: mockedTwin })
|
76
|
+
expect(result).toEqual(mockedTwinsRequest)
|
77
|
+
})
|
78
|
+
|
79
|
+
it('pullTwins should return empty records when the api throw an error', async () => {
|
80
|
+
const store = useTwinsStore()
|
81
|
+
|
82
|
+
// Mock the TwinsService
|
83
|
+
const mockedGetTwins = vi.spyOn(twinsService, 'getTwins').mockRejectedValue(new Error('ERROR'))
|
84
|
+
|
85
|
+
// Call the store method
|
86
|
+
const result = await store.pullTwins(0, 10, '', '')
|
87
|
+
|
88
|
+
// Check the result
|
89
|
+
expect(mockedGetTwins).toHaveBeenCalledWith(0, 10, '', '')
|
90
|
+
expect(store.twinsCollection).toEqual({})
|
91
|
+
expect(result).toEqual({
|
92
|
+
totalRecords: 0,
|
93
|
+
records: [],
|
94
|
+
})
|
95
|
+
})
|
96
|
+
|
97
|
+
it('getTwinFromUUID should fetch twins and update the collection', async () => {
|
98
|
+
const store = useTwinsStore()
|
99
|
+
|
100
|
+
// Mock the TwinsService
|
101
|
+
const mockedTwin = { UUID: '123', name: 'Mocked twin' } as Twin
|
102
|
+
const mockedGetTwins = vi.spyOn(twinsService, 'getTwinByUUID').mockResolvedValue(mockedTwin)
|
103
|
+
|
104
|
+
// Call the store method
|
105
|
+
const result = await store.getTwinFromUUID('123')
|
106
|
+
|
107
|
+
// Check the result
|
108
|
+
expect(mockedGetTwins).toHaveBeenCalledWith('123')
|
109
|
+
expect(store.twinsCollection).toEqual({ 123: mockedTwin })
|
110
|
+
expect(result).toEqual(mockedTwin)
|
111
|
+
})
|
112
|
+
|
113
|
+
it('getTwinFromUUID should return the twin if it is already in the cache', async () => {
|
114
|
+
const store = useTwinsStore()
|
115
|
+
store.twinsCollection = { 123: { UUID: '123', name: 'Mocked twin' } as Twin }
|
116
|
+
|
117
|
+
// Mock the TwinsService
|
118
|
+
const mockedTwin = { UUID: '123', name: 'Mocked twin' } as Twin
|
119
|
+
const mockedGetTwins = vi.spyOn(twinsService, 'getTwinByUUID').mockResolvedValue(mockedTwin)
|
120
|
+
|
121
|
+
// Call the store method
|
122
|
+
const result = await store.getTwinFromUUID('123')
|
123
|
+
|
124
|
+
// Check the result
|
125
|
+
expect(mockedGetTwins).not.toHaveBeenCalled()
|
126
|
+
expect(store.twinsCollection).toEqual({ 123: mockedTwin })
|
127
|
+
expect(result).toEqual(mockedTwin)
|
128
|
+
})
|
129
|
+
|
130
|
+
it('getTwinFromUUID should return empty records when the api throw an error', async () => {
|
131
|
+
const store = useTwinsStore()
|
132
|
+
|
133
|
+
// Mock the TwinsService
|
134
|
+
const mockedGetTwins = vi.spyOn(twinsService, 'getTwinByUUID').mockRejectedValue(new Error('ERROR'))
|
135
|
+
|
136
|
+
// Call the store method
|
137
|
+
const result = await store.getTwinFromUUID('123')
|
138
|
+
|
139
|
+
// Check the result
|
140
|
+
expect(mockedGetTwins).toHaveBeenCalledWith('123')
|
141
|
+
expect(store.twinsCollection).toEqual({ })
|
142
|
+
expect(result).toEqual(null)
|
143
|
+
})
|
144
|
+
|
145
|
+
afterEach(() => {
|
146
|
+
vi.restoreAllMocks()
|
147
|
+
})
|
148
|
+
})
|
@@ -0,0 +1,78 @@
|
|
1
|
+
import { acceptHMRUpdate, defineStore } from 'pinia'
|
2
|
+
import type { Ref } from 'vue'
|
3
|
+
import TwinsService from '~/services/twinsService'
|
4
|
+
import type { Twin } from '~/types'
|
5
|
+
|
6
|
+
export const useTwinsStore = defineStore('twins', () => {
|
7
|
+
const twinsCollection: Ref<{ [UUID: string]: Twin }> = ref({})
|
8
|
+
|
9
|
+
const pullTwinsByUUIDs = async (uuids: string[], forceClear = false) => {
|
10
|
+
if (forceClear) {
|
11
|
+
uuids.forEach((uuid) => {
|
12
|
+
delete twinsCollection.value[uuid]
|
13
|
+
})
|
14
|
+
}
|
15
|
+
try {
|
16
|
+
const twinsRequest = await TwinsService.getTwinByUUIDs(uuids)
|
17
|
+
twinsRequest.records.forEach((twin: Twin) => {
|
18
|
+
// may need to sanitize the twins
|
19
|
+
twinsCollection.value[twin.UUID] = twin
|
20
|
+
})
|
21
|
+
return twinsRequest
|
22
|
+
}
|
23
|
+
catch {
|
24
|
+
return {
|
25
|
+
totalRecords: 0,
|
26
|
+
records: [],
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
const pullTwins = async (start: number, limit: number, sort: string, filters: string) => {
|
32
|
+
try {
|
33
|
+
const twinsRequest = await TwinsService.getTwins(start, limit, sort, filters)
|
34
|
+
twinsRequest.records.forEach((twin: Twin) => {
|
35
|
+
// may need to sanitize the twins
|
36
|
+
twinsCollection.value[twin.UUID] = twin
|
37
|
+
})
|
38
|
+
return twinsRequest
|
39
|
+
}
|
40
|
+
catch {
|
41
|
+
return {
|
42
|
+
totalRecords: 0,
|
43
|
+
records: [],
|
44
|
+
}
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
const getTwinFromUUID = async (uuid: string, fromCache?: boolean) => {
|
49
|
+
const twinCache = twinsCollection.value[uuid]
|
50
|
+
if (twinCache) {
|
51
|
+
return twinCache
|
52
|
+
}
|
53
|
+
else if (!fromCache) {
|
54
|
+
try {
|
55
|
+
const twin = await TwinsService.getTwinByUUID(uuid)
|
56
|
+
if (twin) {
|
57
|
+
twinsCollection.value[twin.UUID] = twin
|
58
|
+
return twin
|
59
|
+
}
|
60
|
+
}
|
61
|
+
catch {
|
62
|
+
return null
|
63
|
+
}
|
64
|
+
}
|
65
|
+
else {
|
66
|
+
return null
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
return {
|
71
|
+
pullTwinsByUUIDs,
|
72
|
+
pullTwins,
|
73
|
+
twinsCollection,
|
74
|
+
getTwinFromUUID,
|
75
|
+
}
|
76
|
+
})
|
77
|
+
if (import.meta.hot)
|
78
|
+
import.meta.hot.accept(acceptHMRUpdate(useTwinsStore, import.meta.hot))
|
@@ -0,0 +1,107 @@
|
|
1
|
+
import { createPinia, setActivePinia } from 'pinia'
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
3
|
+
|
4
|
+
import { useUserSessionStore } from '../../src/stores/userSession'
|
5
|
+
import UserSessionService from '~/services/userSessionService'
|
6
|
+
|
7
|
+
vi.mock('~/services/davraApi')
|
8
|
+
|
9
|
+
const assignMock = vi.fn()
|
10
|
+
|
11
|
+
describe('Tenant Settings Store', () => {
|
12
|
+
beforeEach(() => {
|
13
|
+
// creates a fresh pinia and make it active so it's automatically picked
|
14
|
+
// up by any useStore() call without having to pass it to it:
|
15
|
+
// `useStore(pinia)`
|
16
|
+
setActivePinia(createPinia())
|
17
|
+
|
18
|
+
window.location = { assign: assignMock } as any
|
19
|
+
vi.spyOn(UserSessionService, 'getUser').mockResolvedValue(null)
|
20
|
+
})
|
21
|
+
|
22
|
+
afterEach(() => {
|
23
|
+
assignMock.mockClear()
|
24
|
+
|
25
|
+
vi.restoreAllMocks()
|
26
|
+
})
|
27
|
+
|
28
|
+
it('init store with empty setting', () => {
|
29
|
+
const userSession = useUserSessionStore()
|
30
|
+
expect(userSession.user).toBeNull()
|
31
|
+
expect(userSession.userPermissions).toEqual([])
|
32
|
+
})
|
33
|
+
|
34
|
+
it('init store with settings in local storage', () => {
|
35
|
+
const mockUser = { _id: '631b1b74abf76669a84d0d54', name: 'admin', UUID: '451e5a4868b3fd5bda206fc82e436ac3', roles: ['6e4a4605-548e-4afb-a887-dfbf7a3232ea'], tenantId: 'ruban', tenants: ['ruban'], creationTime: '2022-09-09T10:54:44.904Z', id: 'admin', displayName: 'admin', modifiedTime: '2022-10-27T08:51:10.622Z', sso: null, isAdmin: true, isAdministrator: true }
|
36
|
+
const mockUserPermissions = [{ Action: 'devices.ACCESS', Allowed: true }, { Action: 'devices.CREATE_DEVICE', Allowed: true }, { Action: 'devicetypes.CREATE_DEVICE_TYPE', Allowed: true }, { Action: 'devices.DELETE', Allowed: true }, { Action: 'metrics.ACCESS', Allowed: true }, { Action: 'metrics.CREATE_METRIC', Allowed: true }, { Action: 'users.ACCESS', Allowed: true }, { Action: 'users.CREATE_USER', Allowed: true }, { Action: 'applications.ACCESS', Allowed: true }, { Action: 'applications.CREATE_APPLICATION', Allowed: true }, { Action: 'rulesengine.ACCESS', Allowed: true }, { Action: 'rulesengine.CREATE_RULE', Allowed: true }, { Action: 'microservices.ACCESS', Allowed: true }, { Action: 'microservices.CREATE_MICROSERVICE', Allowed: true }, { Action: 'componenttoolkit.ACCESS', Allowed: true }, { Action: 'componenttoolkit.CREATE_WIDGET', Allowed: true }, { Action: 'authorization.roles.ACCESS', Allowed: true }, { Action: 'authorization.roles.CREATE_ROLE', Allowed: true }, { Action: 'jobs.ACCESS', Allowed: true }, { Action: 'jobs.CREATE_JOB', Allowed: true }, { Action: 'files.ACCESS', Allowed: true }, { Action: 'files.CREATE_FILE', Allowed: true }, { Action: 'auditlogs.ACCESS', Allowed: true }, { Action: 'twintypes.ACCESS', Allowed: true }, { Action: 'twintypes.CREATE_TWINTYPE', Allowed: true }, { Action: 'twins.ACCESS', Allowed: true }, { Action: 'twins.CREATE_TWIN', Allowed: true }, { Action: 'features.ACCESS', Allowed: true }, { Action: 'features.CREATE_FEATURE', Allowed: true }, { Action: 'oauthclients.ACCESS', Allowed: true }, { Action: 'oauthclients.CREATE', Allowed: true }, { Action: 'digitalSignatures.ACCESS' }, { Action: 'digitalSignatures.CREATE_DIGITAL_SIGNATURE' }]
|
37
|
+
|
38
|
+
const mockLocalStorage: { [key: string]: string } = { 'user': JSON.stringify(mockUser), 'user-permissions': JSON.stringify(mockUserPermissions) }
|
39
|
+
vi.spyOn(Object.getPrototypeOf(window.localStorage), 'getItem').mockImplementation((key) => {
|
40
|
+
return mockLocalStorage[key as string]
|
41
|
+
})
|
42
|
+
|
43
|
+
const userSession = useUserSessionStore()
|
44
|
+
expect(userSession.user).not.toBeNull()
|
45
|
+
expect(userSession.user).toEqual(mockUser)
|
46
|
+
|
47
|
+
expect(userSession.userPermissions).not.toBeNull()
|
48
|
+
expect(userSession.userPermissions).toEqual(mockUserPermissions)
|
49
|
+
|
50
|
+
expect(userSession.userName).toEqual('admin')
|
51
|
+
expect(userSession.getPermission('devices.ACCESS')).toBeTruthy()
|
52
|
+
expect(userSession.getPermission('noEXISTING.ACCESS')).toBeFalsy()
|
53
|
+
expect(userSession.getPermission('')).toBeTruthy()
|
54
|
+
})
|
55
|
+
|
56
|
+
it('pullUser to an empty store', async () => {
|
57
|
+
const mockUser = { _id: '631b1b74abf76669a84d0d54', name: 'admin', UUID: '451e5a4868b3fd5bda206fc82e436ac3', roles: ['6e4a4605-548e-4afb-a887-dfbf7a3232ea'], tenantId: 'ruban', tenants: ['ruban'], creationTime: '2022-09-09T10:54:44.904Z', id: 'admin', displayName: 'admin', modifiedTime: '2022-10-27T08:51:10.622Z', sso: null, isAdmin: true, isAdministrator: true }
|
58
|
+
const mockUserPermissions = [{ Action: 'devices.ACCESS', Allowed: true }, { Action: 'devices.CREATE_DEVICE', Allowed: true }, { Action: 'devicetypes.CREATE_DEVICE_TYPE', Allowed: true }, { Action: 'devices.DELETE', Allowed: true }, { Action: 'metrics.ACCESS', Allowed: true }, { Action: 'metrics.CREATE_METRIC', Allowed: true }, { Action: 'users.ACCESS', Allowed: true }, { Action: 'users.CREATE_USER', Allowed: true }, { Action: 'applications.ACCESS', Allowed: true }, { Action: 'applications.CREATE_APPLICATION', Allowed: true }, { Action: 'rulesengine.ACCESS', Allowed: true }, { Action: 'rulesengine.CREATE_RULE', Allowed: true }, { Action: 'microservices.ACCESS', Allowed: true }, { Action: 'microservices.CREATE_MICROSERVICE', Allowed: true }, { Action: 'componenttoolkit.ACCESS', Allowed: true }, { Action: 'componenttoolkit.CREATE_WIDGET', Allowed: true }, { Action: 'authorization.roles.ACCESS', Allowed: true }, { Action: 'authorization.roles.CREATE_ROLE', Allowed: true }, { Action: 'jobs.ACCESS', Allowed: true }, { Action: 'jobs.CREATE_JOB', Allowed: true }, { Action: 'files.ACCESS', Allowed: true }, { Action: 'files.CREATE_FILE', Allowed: true }, { Action: 'auditlogs.ACCESS', Allowed: true }, { Action: 'twintypes.ACCESS', Allowed: true }, { Action: 'twintypes.CREATE_TWINTYPE', Allowed: true }, { Action: 'twins.ACCESS', Allowed: true }, { Action: 'twins.CREATE_TWIN', Allowed: true }, { Action: 'features.ACCESS', Allowed: true }, { Action: 'features.CREATE_FEATURE', Allowed: true }, { Action: 'oauthclients.ACCESS', Allowed: true }, { Action: 'oauthclients.CREATE', Allowed: true }]
|
59
|
+
|
60
|
+
const userSession = useUserSessionStore()
|
61
|
+
|
62
|
+
expect(userSession.user).toBeNull()
|
63
|
+
expect(userSession.userPermissions).toEqual([])
|
64
|
+
|
65
|
+
const spyGetUser = vi.spyOn(UserSessionService, 'getUser').mockResolvedValue(mockUser)
|
66
|
+
const spyGetUserPermissions = vi.spyOn(UserSessionService, 'getUserPermissions').mockResolvedValue(mockUserPermissions)
|
67
|
+
|
68
|
+
const spySetItem = vi.spyOn(Object.getPrototypeOf(window.localStorage), 'setItem').mockImplementation(() => {})
|
69
|
+
|
70
|
+
await userSession.pullUser()
|
71
|
+
|
72
|
+
expect(spyGetUser).toHaveBeenCalled()
|
73
|
+
expect(spyGetUserPermissions).toHaveBeenCalled()
|
74
|
+
expect(spySetItem).toHaveBeenCalledWith('user', JSON.stringify(mockUser))
|
75
|
+
expect(spySetItem).toHaveBeenCalledWith('user-permissions', JSON.stringify(mockUserPermissions))
|
76
|
+
})
|
77
|
+
|
78
|
+
it('pullUser with an API ERROR', async () => {
|
79
|
+
const userSession = useUserSessionStore()
|
80
|
+
expect(userSession.user).toBeNull()
|
81
|
+
const spyGetTenantSettings = vi.spyOn(UserSessionService, 'getUser').mockImplementationOnce(() => { throw new Error('error') })
|
82
|
+
|
83
|
+
const spySetItem = vi.spyOn(Object.getPrototypeOf(window.localStorage), 'setItem')
|
84
|
+
await userSession.pullUser()
|
85
|
+
|
86
|
+
expect(spyGetTenantSettings).toHaveBeenCalled()
|
87
|
+
expect(spySetItem).toHaveBeenCalledTimes(0)
|
88
|
+
})
|
89
|
+
|
90
|
+
it('logout and empty the store', async () => {
|
91
|
+
const mockUser = { _id: '631b1b74abf76669a84d0d54', name: 'admin', UUID: '451e5a4868b3fd5bda206fc82e436ac3', roles: ['6e4a4605-548e-4afb-a887-dfbf7a3232ea'], tenantId: 'ruban', tenants: ['ruban'], creationTime: '2022-09-09T10:54:44.904Z', id: 'admin', displayName: 'admin', modifiedTime: '2022-10-27T08:51:10.622Z', sso: null, isAdmin: true, isAdministrator: true }
|
92
|
+
const mockUserPermissions = [{ Action: 'devices.ACCESS', Allowed: true }, { Action: 'devices.CREATE_DEVICE', Allowed: true }, { Action: 'devicetypes.CREATE_DEVICE_TYPE', Allowed: true }, { Action: 'devices.DELETE', Allowed: true }, { Action: 'metrics.ACCESS', Allowed: true }, { Action: 'metrics.CREATE_METRIC', Allowed: true }, { Action: 'users.ACCESS', Allowed: true }, { Action: 'users.CREATE_USER', Allowed: true }, { Action: 'applications.ACCESS', Allowed: true }, { Action: 'applications.CREATE_APPLICATION', Allowed: true }, { Action: 'rulesengine.ACCESS', Allowed: true }, { Action: 'rulesengine.CREATE_RULE', Allowed: true }, { Action: 'microservices.ACCESS', Allowed: true }, { Action: 'microservices.CREATE_MICROSERVICE', Allowed: true }, { Action: 'componenttoolkit.ACCESS', Allowed: true }, { Action: 'componenttoolkit.CREATE_WIDGET', Allowed: true }, { Action: 'authorization.roles.ACCESS', Allowed: true }, { Action: 'authorization.roles.CREATE_ROLE', Allowed: true }, { Action: 'jobs.ACCESS', Allowed: true }, { Action: 'jobs.CREATE_JOB', Allowed: true }, { Action: 'files.ACCESS', Allowed: true }, { Action: 'files.CREATE_FILE', Allowed: true }, { Action: 'auditlogs.ACCESS', Allowed: true }, { Action: 'twintypes.ACCESS', Allowed: true }, { Action: 'twintypes.CREATE_TWINTYPE', Allowed: true }, { Action: 'twins.ACCESS', Allowed: true }, { Action: 'twins.CREATE_TWIN', Allowed: true }, { Action: 'features.ACCESS', Allowed: true }, { Action: 'features.CREATE_FEATURE', Allowed: true }, { Action: 'oauthclients.ACCESS', Allowed: true }, { Action: 'oauthclients.CREATE', Allowed: true }, { Action: 'digitalSignatures.ACCESS' }, { Action: 'digitalSignatures.CREATE_DIGITAL_SIGNATURE' }]
|
93
|
+
|
94
|
+
const mockLocalStorage: { [key: string]: string } = { 'user': JSON.stringify(mockUser), 'user-permissions': JSON.stringify(mockUserPermissions) }
|
95
|
+
vi.spyOn(Object.getPrototypeOf(window.localStorage), 'getItem').mockImplementation((key) => {
|
96
|
+
return mockLocalStorage[key as string]
|
97
|
+
})
|
98
|
+
const spyRemoveItem = vi.spyOn(Object.getPrototypeOf(window.localStorage), 'removeItem').mockImplementation(() => {})
|
99
|
+
|
100
|
+
const userSession = useUserSessionStore()
|
101
|
+
await userSession.logout()
|
102
|
+
|
103
|
+
expect(userSession.userName).toEqual('no user')
|
104
|
+
expect(userSession.getPermission('devices.ACCESS')).toBeFalsy()
|
105
|
+
expect(spyRemoveItem).toHaveBeenCalledTimes(2)
|
106
|
+
})
|
107
|
+
})
|
@@ -0,0 +1,57 @@
|
|
1
|
+
import { acceptHMRUpdate, defineStore } from 'pinia'
|
2
|
+
import type { ComputedRef, Ref } from 'vue'
|
3
|
+
import UserSessionService from '~/services/userSessionService'
|
4
|
+
import type { Permission, User } from '~/types'
|
5
|
+
|
6
|
+
export const useUserSessionStore = defineStore('userSession', () => {
|
7
|
+
// state
|
8
|
+
|
9
|
+
const user: Ref<User | null> = ref(JSON.parse(localStorage.getItem('user') || 'null'))
|
10
|
+
const userPermissions: Ref<Permission[]> = ref(JSON.parse(localStorage.getItem('user-permissions') || '[]'))
|
11
|
+
|
12
|
+
// getters
|
13
|
+
|
14
|
+
const userName: ComputedRef<string> = computed(() => user.value?.name || 'no user')
|
15
|
+
|
16
|
+
const getPermission: ComputedRef<(actionKey: string) => boolean> = computed(() => {
|
17
|
+
return (actionKey: string) => {
|
18
|
+
return actionKey ? (userPermissions.value?.find((permission: any) => permission.Action === actionKey)?.Allowed || false) : true
|
19
|
+
}
|
20
|
+
})
|
21
|
+
|
22
|
+
// actions
|
23
|
+
const pullUser = async () => {
|
24
|
+
try {
|
25
|
+
const userRequest = await UserSessionService.getUser()
|
26
|
+
user.value = userRequest
|
27
|
+
if (user.value?.UUID) {
|
28
|
+
const userPermissionsRequest: Permission[] = await UserSessionService.getUserPermissions()
|
29
|
+
userPermissions.value = userPermissionsRequest
|
30
|
+
|
31
|
+
localStorage.setItem('user', JSON.stringify(user.value))
|
32
|
+
localStorage.setItem('user-permissions', JSON.stringify(userPermissions.value))
|
33
|
+
}
|
34
|
+
}
|
35
|
+
catch (err: any) {
|
36
|
+
if (err.response?.status === 401) {
|
37
|
+
window.location.href = '/ui/login.html'
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
const logout = () => {
|
43
|
+
user.value = null
|
44
|
+
userPermissions.value = []
|
45
|
+
localStorage.removeItem('user')
|
46
|
+
localStorage.removeItem('user-permission')
|
47
|
+
window.location.href = '/ui/logout'
|
48
|
+
}
|
49
|
+
|
50
|
+
// init Store
|
51
|
+
pullUser()
|
52
|
+
|
53
|
+
return { user, pullUser, userName, userPermissions, getPermission, logout }
|
54
|
+
})
|
55
|
+
|
56
|
+
if (import.meta.hot)
|
57
|
+
import.meta.hot.accept(acceptHMRUpdate(useUserSessionStore, import.meta.hot))
|
package/src/types.ts
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
export interface User {
|
2
|
+
UUID: string
|
3
|
+
_id: string
|
4
|
+
creationTime: string
|
5
|
+
displayName: string
|
6
|
+
id: string
|
7
|
+
isAdmin: boolean
|
8
|
+
isAdministrator: boolean
|
9
|
+
modifiedTime: string
|
10
|
+
name: string
|
11
|
+
roles: string[]
|
12
|
+
sso: null | any
|
13
|
+
tenantId: string
|
14
|
+
tenants: string[]
|
15
|
+
|
16
|
+
}
|
17
|
+
|
18
|
+
export interface Permission {
|
19
|
+
'Action': string
|
20
|
+
'Allowed': boolean
|
21
|
+
}
|
22
|
+
|
23
|
+
|
24
|
+
export interface TwinType {
|
25
|
+
_id: string
|
26
|
+
name: string
|
27
|
+
description: string
|
28
|
+
labels?: object
|
29
|
+
customAttributes: object
|
30
|
+
tenantId: string
|
31
|
+
UUID: string
|
32
|
+
created: number
|
33
|
+
owner: string
|
34
|
+
modified?: number
|
35
|
+
}
|
36
|
+
|
37
|
+
export interface Twin {
|
38
|
+
_id: string
|
39
|
+
name: string
|
40
|
+
description: string
|
41
|
+
labels: object
|
42
|
+
digitalTwinType: string
|
43
|
+
customAttributes: object
|
44
|
+
tenantId: string
|
45
|
+
deviceType: string
|
46
|
+
UUID: string
|
47
|
+
createdTime: number
|
48
|
+
owner: string
|
49
|
+
digitalTwinTypeName: string
|
50
|
+
}
|
51
|
+
|
52
|
+
export interface TwinCounter {
|
53
|
+
twin: Twin
|
54
|
+
UUID: string
|
55
|
+
values: [number, number][]
|
56
|
+
count: number
|
57
|
+
}
|
58
|
+
|
59
|
+
export interface Device {
|
60
|
+
_id: string
|
61
|
+
name: string
|
62
|
+
description: string
|
63
|
+
labels: object
|
64
|
+
tenantId: string
|
65
|
+
deviceType: string
|
66
|
+
UUID: string
|
67
|
+
createdTime: number
|
68
|
+
owner: string
|
69
|
+
lastSeen?: number
|
70
|
+
}
|
71
|
+
|
72
|
+
export interface DeviceCounterMetric extends Device {
|
73
|
+
values: { [metricName: string]: [string, number][] }[]
|
74
|
+
totals: { [metricName: string]: number }
|
75
|
+
}
|
76
|
+
|
77
|
+
export interface DeviceCounter {
|
78
|
+
device: Device
|
79
|
+
UUID: string
|
80
|
+
values: [number, number][]
|
81
|
+
count: number
|
82
|
+
}
|
83
|
+
|
84
|
+
export interface TimeseriesQuery {
|
85
|
+
name: string
|
86
|
+
customName?: string
|
87
|
+
timeseries: TimeseriesMetricConfig
|
88
|
+
ui: {
|
89
|
+
seriesType: string
|
90
|
+
lineType?: string
|
91
|
+
axisType?: string
|
92
|
+
scaleType?: string
|
93
|
+
stackType?: string
|
94
|
+
axisPosition?: string
|
95
|
+
customColor?: string
|
96
|
+
axisLabel?: string
|
97
|
+
fillArea?: boolean
|
98
|
+
}
|
99
|
+
}
|
100
|
+
|
101
|
+
export interface AggregatorConfig {
|
102
|
+
aggregator: string
|
103
|
+
bucketunit?: string
|
104
|
+
bucketvalue?: number
|
105
|
+
aggregatorType: string
|
106
|
+
aggregatorMultiplicator?: number
|
107
|
+
}
|
108
|
+
|
109
|
+
export interface DataTableQuery {
|
110
|
+
aggregator: string
|
111
|
+
metrics: string []
|
112
|
+
devices?: string []
|
113
|
+
metricsType?: string
|
114
|
+
groupBy?: { name: string; tags: string[] }
|
115
|
+
tags?: { [key: string]: string[] }
|
116
|
+
}
|
117
|
+
export interface TimeseriesMetricConfig extends AggregatorConfig {
|
118
|
+
metricName: string
|
119
|
+
groupBy?: { name: string; tags: string[] }
|
120
|
+
tags?: { [key: string]: string[] }
|
121
|
+
}
|
122
|
+
|
123
|
+
export interface TimeseriesDataResponse<T> {
|
124
|
+
queries: TimeseriesDataQuery<T>[]
|
125
|
+
}
|
126
|
+
|
127
|
+
export interface TimeseriesDataQuery<T> {
|
128
|
+
sample_size: number
|
129
|
+
results: TimeseriesDataResult<T>[]
|
130
|
+
}
|
131
|
+
export interface TimeseriesDataResult<T> {
|
132
|
+
name: string
|
133
|
+
group_by: { name: string; tags?: string[]; group?: any; [key: string]: any }[]
|
134
|
+
values: [number, T][]
|
135
|
+
tags?: any
|
136
|
+
}
|
137
|
+
|
138
|
+
export interface TimeseriesQueryMetric {
|
139
|
+
name: string
|
140
|
+
aggregators: {
|
141
|
+
name: string
|
142
|
+
sampling: {
|
143
|
+
value: number
|
144
|
+
unit: string
|
145
|
+
}
|
146
|
+
}[]
|
147
|
+
group_by: {
|
148
|
+
name: string
|
149
|
+
tags: string[]
|
150
|
+
}[]
|
151
|
+
tags: {
|
152
|
+
[key: string]: string[]
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
export interface Metric {
|
157
|
+
_id?: string
|
158
|
+
name: string
|
159
|
+
description?: string
|
160
|
+
label: string
|
161
|
+
units?: string
|
162
|
+
labels?: { [key: string]: string }
|
163
|
+
semantics?: string
|
164
|
+
}
|
165
|
+
|
166
|
+
export interface MetricCounter {
|
167
|
+
count: number
|
168
|
+
metadata: object
|
169
|
+
name: string
|
170
|
+
values: [number, number][]
|
171
|
+
|
172
|
+
}
|
173
|
+
|