@ikatec/digisac-api-sdk 4.1.0 → 4.2.0
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 +7 -4
- package/dist/apis/authHistory/AuthHistoryApi.cjs +12 -12
- package/dist/apis/authHistory/AuthHistoryApi.cjs.map +1 -1
- package/dist/apis/authHistory/AuthHistoryApi.d.ts +12 -19
- package/dist/apis/authHistory/AuthHistoryApi.d.ts.map +1 -1
- package/dist/apis/authHistory/AuthHistoryApi.mjs +12 -12
- package/dist/apis/authHistory/AuthHistoryApi.mjs.map +1 -1
- package/dist/apis/authHistory/types.d.ts +4 -3
- package/dist/apis/authHistory/types.d.ts.map +1 -1
- package/dist/apis/customFields/types.d.ts +21 -19
- package/dist/apis/customFields/types.d.ts.map +1 -1
- package/dist/apis/dashboard/DashboardApi.cjs +19 -0
- package/dist/apis/dashboard/DashboardApi.cjs.map +1 -0
- package/dist/apis/dashboard/DashboardApi.d.ts +12 -0
- package/dist/apis/dashboard/DashboardApi.d.ts.map +1 -0
- package/dist/apis/dashboard/DashboardApi.mjs +18 -0
- package/dist/apis/dashboard/DashboardApi.mjs.map +1 -0
- package/dist/apis/dashboard/index.cjs +4 -0
- package/dist/apis/dashboard/index.d.ts +3 -0
- package/dist/apis/dashboard/index.d.ts.map +1 -0
- package/dist/apis/dashboard/index.mjs +3 -0
- package/dist/apis/dashboard/types.cjs +0 -0
- package/dist/apis/dashboard/types.d.ts +53 -0
- package/dist/apis/dashboard/types.d.ts.map +1 -0
- package/dist/apis/dashboard/types.mjs +0 -0
- package/dist/apis/holiday/types.d.ts +34 -8
- package/dist/apis/holiday/types.d.ts.map +1 -1
- package/dist/apis/index.cjs +6 -0
- package/dist/apis/index.d.ts +2 -0
- package/dist/apis/index.d.ts.map +1 -1
- package/dist/apis/index.mjs +5 -1
- package/dist/apis/now/NowApi.cjs +37 -0
- package/dist/apis/now/NowApi.cjs.map +1 -0
- package/dist/apis/now/NowApi.d.ts +26 -0
- package/dist/apis/now/NowApi.d.ts.map +1 -0
- package/dist/apis/now/NowApi.mjs +36 -0
- package/dist/apis/now/NowApi.mjs.map +1 -0
- package/dist/apis/now/index.cjs +4 -0
- package/dist/apis/now/index.d.ts +3 -0
- package/dist/apis/now/index.d.ts.map +1 -0
- package/dist/apis/now/index.mjs +3 -0
- package/dist/apis/now/types.cjs +0 -0
- package/dist/apis/now/types.d.ts +45 -0
- package/dist/apis/now/types.d.ts.map +1 -0
- package/dist/apis/now/types.mjs +0 -0
- package/dist/apis/people/types.d.ts +7 -2
- package/dist/apis/people/types.d.ts.map +1 -1
- package/dist/apis/permissions/PermissionsApi.cjs +6 -2
- package/dist/apis/permissions/PermissionsApi.cjs.map +1 -1
- package/dist/apis/permissions/PermissionsApi.d.ts +7 -3
- package/dist/apis/permissions/PermissionsApi.d.ts.map +1 -1
- package/dist/apis/permissions/PermissionsApi.mjs +6 -2
- package/dist/apis/permissions/PermissionsApi.mjs.map +1 -1
- package/dist/apis/permissions/types.d.ts +4 -14
- package/dist/apis/permissions/types.d.ts.map +1 -1
- package/dist/apis/personalAccessTokens/PersonalAccessTokensApi.cjs +5 -0
- package/dist/apis/personalAccessTokens/PersonalAccessTokensApi.cjs.map +1 -1
- package/dist/apis/personalAccessTokens/PersonalAccessTokensApi.d.ts +5 -0
- package/dist/apis/personalAccessTokens/PersonalAccessTokensApi.d.ts.map +1 -1
- package/dist/apis/personalAccessTokens/PersonalAccessTokensApi.mjs +5 -0
- package/dist/apis/personalAccessTokens/PersonalAccessTokensApi.mjs.map +1 -1
- package/dist/apis/personalAccessTokens/types.d.ts +23 -4
- package/dist/apis/personalAccessTokens/types.d.ts.map +1 -1
- package/dist/apis/roles/RolesApi.cjs.map +1 -1
- package/dist/apis/roles/RolesApi.d.ts +2 -2
- package/dist/apis/roles/RolesApi.d.ts.map +1 -1
- package/dist/apis/roles/RolesApi.mjs.map +1 -1
- package/dist/apis/roles/types.d.ts +27 -4
- package/dist/apis/roles/types.d.ts.map +1 -1
- package/dist/apis/terms/TermsApi.cjs +4 -1
- package/dist/apis/terms/TermsApi.cjs.map +1 -1
- package/dist/apis/terms/TermsApi.d.ts +4 -1
- package/dist/apis/terms/TermsApi.d.ts.map +1 -1
- package/dist/apis/terms/TermsApi.mjs +4 -1
- package/dist/apis/terms/TermsApi.mjs.map +1 -1
- package/dist/apis/timetable/types.d.ts +12 -7
- package/dist/apis/timetable/types.d.ts.map +1 -1
- package/dist/core/BaseCrudApi.cjs +3 -30
- package/dist/core/BaseCrudApi.cjs.map +1 -1
- package/dist/core/BaseCrudApi.d.ts +4 -24
- package/dist/core/BaseCrudApi.d.ts.map +1 -1
- package/dist/core/BaseCrudApi.mjs +3 -30
- package/dist/core/BaseCrudApi.mjs.map +1 -1
- package/dist/core/BaseReadApi.cjs +44 -0
- package/dist/core/BaseReadApi.cjs.map +1 -0
- package/dist/core/BaseReadApi.d.ts +32 -0
- package/dist/core/BaseReadApi.d.ts.map +1 -0
- package/dist/core/BaseReadApi.mjs +43 -0
- package/dist/core/BaseReadApi.mjs.map +1 -0
- package/dist/core/index.cjs +2 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.mjs +2 -1
- package/dist/index.cjs +2 -0
- package/dist/index.mjs +2 -1
- package/package.json +1 -1
- package/src/apis/authHistory/AuthHistoryApi.test.ts +10 -2
- package/src/apis/authHistory/AuthHistoryApi.ts +13 -39
- package/src/apis/authHistory/types.ts +4 -3
- package/src/apis/customFields/types.ts +21 -25
- package/src/apis/dashboard/DashboardApi.test.ts +82 -0
- package/src/apis/dashboard/DashboardApi.ts +18 -0
- package/src/apis/dashboard/index.ts +2 -0
- package/src/apis/dashboard/types.ts +53 -0
- package/src/apis/holiday/types.ts +36 -8
- package/src/apis/index.ts +2 -0
- package/src/apis/now/NowApi.test.ts +125 -0
- package/src/apis/now/NowApi.ts +59 -0
- package/src/apis/now/index.ts +2 -0
- package/src/apis/now/types.ts +47 -0
- package/src/apis/payloads.test-d.ts +39 -0
- package/src/apis/payloads.test.ts +81 -0
- package/src/apis/people/types.ts +7 -2
- package/src/apis/permissions/PermissionsApi.test.ts +93 -0
- package/src/apis/permissions/PermissionsApi.ts +7 -7
- package/src/apis/permissions/types.ts +4 -16
- package/src/apis/personalAccessTokens/PersonalAccessTokensApi.ts +5 -0
- package/src/apis/personalAccessTokens/types.ts +25 -4
- package/src/apis/queryFormat.test.ts +75 -0
- package/src/apis/roles/RolesApi.ts +7 -2
- package/src/apis/roles/types.ts +27 -4
- package/src/apis/terms/TermsApi.ts +4 -1
- package/src/apis/timetable/types.ts +13 -7
- package/src/core/BaseCrudApi.ts +4 -60
- package/src/core/BaseReadApi.ts +70 -0
- package/src/core/index.ts +1 -0
package/src/apis/people/types.ts
CHANGED
|
@@ -17,10 +17,15 @@ export type PersonRelationships = 'organizations' | 'contacts'
|
|
|
17
17
|
|
|
18
18
|
export type CreatePersonPayload = {
|
|
19
19
|
name: string
|
|
20
|
-
document?: string
|
|
20
|
+
document?: string | null
|
|
21
|
+
/** Organizations of the account to link the person to (400 if any belongs to another account). */
|
|
22
|
+
organizationIds?: string[]
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export type UpdatePersonPayload = {
|
|
24
|
-
|
|
26
|
+
/** Required by the backend on every update (400 `required` otherwise). */
|
|
27
|
+
name: string
|
|
25
28
|
document?: string | null
|
|
29
|
+
/** Replaces the linked organizations: `[]` unlinks all, omitting it keeps them. */
|
|
30
|
+
organizationIds?: string[]
|
|
26
31
|
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { PermissionsApi } from './PermissionsApi'
|
|
3
|
+
import type { ApiClient } from '../../core/ApiClient'
|
|
4
|
+
import type { Permission } from './types'
|
|
5
|
+
|
|
6
|
+
// ── Mock client factory ──────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
function createMockClient(): ApiClient {
|
|
9
|
+
return {
|
|
10
|
+
setAccessToken: vi.fn().mockReturnThis(),
|
|
11
|
+
request: vi.fn(),
|
|
12
|
+
get: vi.fn(),
|
|
13
|
+
post: vi.fn(),
|
|
14
|
+
put: vi.fn(),
|
|
15
|
+
patch: vi.fn(),
|
|
16
|
+
delete: vi.fn(),
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ── Fixtures ─────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
const permission: Permission = { id: 'p-1', name: 'customFields.create', type: 'customFields' }
|
|
23
|
+
|
|
24
|
+
// ── Tests ────────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
describe('PermissionsApi', () => {
|
|
27
|
+
let client: ReturnType<typeof createMockClient>
|
|
28
|
+
let api: PermissionsApi
|
|
29
|
+
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
client = createMockClient()
|
|
32
|
+
api = new PermissionsApi(client)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('exposes only the read routes served by the backend', () => {
|
|
36
|
+
expect(api.basePath).toBe('/permissions')
|
|
37
|
+
expect(api).not.toHaveProperty('create')
|
|
38
|
+
expect(api).not.toHaveProperty('updateById')
|
|
39
|
+
expect(api).not.toHaveProperty('deleteById')
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
describe('getMany', () => {
|
|
43
|
+
it('calls client.get with /permissions and no query string when there is no query', async () => {
|
|
44
|
+
vi.mocked(client.get).mockResolvedValue({ data: [permission], total: 1 })
|
|
45
|
+
await api.getMany()
|
|
46
|
+
expect(client.get).toHaveBeenCalledWith('/permissions', undefined)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('serializes the query in the client format', async () => {
|
|
50
|
+
vi.mocked(client.get).mockResolvedValue([permission])
|
|
51
|
+
const result = await api.getMany({ paginate: false })
|
|
52
|
+
expect(client.get).toHaveBeenCalledWith(
|
|
53
|
+
`/permissions?query=${encodeURIComponent(JSON.stringify({ paginate: false }))}`,
|
|
54
|
+
undefined,
|
|
55
|
+
)
|
|
56
|
+
expect(result).toEqual([permission])
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe('getOne', () => {
|
|
61
|
+
it('asks for a single unpaginated result and returns it', async () => {
|
|
62
|
+
vi.mocked(client.get).mockResolvedValue([permission])
|
|
63
|
+
const result = await api.getOne({ where: { name: 'customFields.create' } })
|
|
64
|
+
const query = { where: { name: 'customFields.create' }, limit: 1, paginate: false }
|
|
65
|
+
expect(client.get).toHaveBeenCalledWith(
|
|
66
|
+
`/permissions?query=${encodeURIComponent(JSON.stringify(query))}`,
|
|
67
|
+
undefined,
|
|
68
|
+
)
|
|
69
|
+
expect(result).toEqual(permission)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('returns null when nothing matches', async () => {
|
|
73
|
+
vi.mocked(client.get).mockResolvedValue([])
|
|
74
|
+
expect(await api.getOne()).toBeNull()
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
describe('getById', () => {
|
|
79
|
+
it('calls client.get with /permissions/:id', async () => {
|
|
80
|
+
vi.mocked(client.get).mockResolvedValue(permission)
|
|
81
|
+
const result = await api.getById('p-1')
|
|
82
|
+
expect(client.get).toHaveBeenCalledWith('/permissions/p-1', undefined)
|
|
83
|
+
expect(result).toEqual(permission)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('forwards custom headers', async () => {
|
|
87
|
+
vi.mocked(client.get).mockResolvedValue(permission)
|
|
88
|
+
const headers = { 'X-Custom': 'h' }
|
|
89
|
+
await api.getById('p-1', undefined, headers)
|
|
90
|
+
expect(client.get).toHaveBeenCalledWith('/permissions/p-1', headers)
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
})
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { ApiClient } from '../../core/ApiClient'
|
|
2
|
-
import {
|
|
3
|
-
import type { Permission
|
|
2
|
+
import { BaseReadApi } from '../../core/BaseReadApi'
|
|
3
|
+
import type { Permission } from './types'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
> {
|
|
5
|
+
/**
|
|
6
|
+
* The permission catalog used by roles. The backend only serves `GET /permissions` and
|
|
7
|
+
* `GET /permissions/:id` (guarded by `roles.view`), so there is no create/update/delete.
|
|
8
|
+
*/
|
|
9
|
+
export class PermissionsApi extends BaseReadApi<Permission> {
|
|
10
10
|
constructor(client: ApiClient) {
|
|
11
11
|
super(client, '/permissions')
|
|
12
12
|
}
|
|
@@ -210,24 +210,12 @@ export type PermissionName =
|
|
|
210
210
|
| 'kanban.update'
|
|
211
211
|
| 'kanban.destroy'
|
|
212
212
|
|
|
213
|
+
/**
|
|
214
|
+
* A permission, as `GET /permissions` answers it (the transformer only picks these fields).
|
|
215
|
+
* Permissions are a fixed catalog: the API is read-only.
|
|
216
|
+
*/
|
|
213
217
|
export type Permission = {
|
|
214
218
|
id: string
|
|
215
219
|
name: PermissionName
|
|
216
220
|
type: PermissionType | null
|
|
217
|
-
description: string | null
|
|
218
|
-
createdAt: string
|
|
219
|
-
updatedAt: string
|
|
220
|
-
deletedAt: string | null
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
export type CreatePermissionPayload = {
|
|
224
|
-
name: PermissionName
|
|
225
|
-
type?: PermissionType
|
|
226
|
-
description?: string
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export type UpdatePermissionPayload = {
|
|
230
|
-
name?: PermissionName
|
|
231
|
-
type?: PermissionType | null
|
|
232
|
-
description?: string | null
|
|
233
221
|
}
|
|
@@ -6,6 +6,11 @@ import type {
|
|
|
6
6
|
UpdatePersonalAccessTokenPayload,
|
|
7
7
|
} from './types'
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Personal access tokens of the authenticated user (`/me/tokens`, all routes guarded by
|
|
11
|
+
* `settingsApi.update`). The listing is scoped to the user; `getById` of a missing token answers
|
|
12
|
+
* 404 (`{ message: 'Not found' }`) instead of `null`.
|
|
13
|
+
*/
|
|
9
14
|
export class PersonalAccessTokensApi extends BaseCrudApi<
|
|
10
15
|
PersonalAccessToken,
|
|
11
16
|
CreatePersonalAccessTokenPayload,
|
|
@@ -1,28 +1,49 @@
|
|
|
1
1
|
import type { User } from '../users/types'
|
|
2
2
|
|
|
3
|
+
export type PersonalAccessTokenStatus = 'active' | 'revoked' | 'expired'
|
|
4
|
+
|
|
5
|
+
/** Accepted lifetimes of a token (`No expiration` keeps `accessTokenExpiresAt` null). */
|
|
6
|
+
export type PersonalAccessTokenExpiresIn = '30d' | '90d' | '180d' | '1y' | 'No expiration'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A personal access token of the authenticated user.
|
|
10
|
+
*
|
|
11
|
+
* `accessToken` comes in full only in the `create` answer; every other route masks it
|
|
12
|
+
* (`abcde***vwxyz`). `status` and `user` (only `{ name }`) come on `getMany`/`getById`.
|
|
13
|
+
* `deleteById` revokes the token (soft delete): it keeps being listed with `status: 'revoked'`.
|
|
14
|
+
*/
|
|
3
15
|
export type PersonalAccessToken = {
|
|
4
16
|
id: string
|
|
5
17
|
name: string
|
|
18
|
+
/** `*` when created without a scope. */
|
|
6
19
|
scope: string | null
|
|
7
20
|
accessToken: string
|
|
8
21
|
accessTokenExpiresAt: string | null
|
|
9
22
|
userId: string
|
|
23
|
+
clientId: string | null
|
|
24
|
+
impersonate?: boolean | null
|
|
10
25
|
createdAt: string
|
|
11
26
|
updatedAt: string
|
|
12
27
|
deletedAt: string | null
|
|
28
|
+
/** Only on `getMany`/`getById` answers. */
|
|
29
|
+
status?: PersonalAccessTokenStatus
|
|
13
30
|
// relationships
|
|
14
|
-
|
|
31
|
+
/** Only on `getMany`/`getById` answers, with just the name. */
|
|
32
|
+
user?: Pick<User, 'name'>
|
|
15
33
|
}
|
|
16
34
|
|
|
17
35
|
export type PersonalAccessTokenRelationships = 'user'
|
|
18
36
|
|
|
19
37
|
export type CreatePersonalAccessTokenPayload = {
|
|
38
|
+
/** Up to 255 characters. */
|
|
20
39
|
name: string
|
|
40
|
+
/** Up to 255 characters (`*` by default). */
|
|
21
41
|
scope?: string
|
|
22
|
-
|
|
42
|
+
expiresIn?: PersonalAccessTokenExpiresIn
|
|
23
43
|
}
|
|
24
44
|
|
|
25
45
|
export type UpdatePersonalAccessTokenPayload = {
|
|
26
|
-
|
|
27
|
-
|
|
46
|
+
/** Required by the backend on every update (400 `required` otherwise). */
|
|
47
|
+
name: string
|
|
48
|
+
scope?: string
|
|
28
49
|
}
|
|
@@ -5,6 +5,9 @@ import { BaseApiClient } from '../core/BaseApiClient'
|
|
|
5
5
|
import type { QueryFormat } from '../core/queryString'
|
|
6
6
|
import { AuthHistoryApi } from './authHistory/AuthHistoryApi'
|
|
7
7
|
import { ContactsApi } from './contacts/ContactsApi'
|
|
8
|
+
import { DashboardApi } from './dashboard/DashboardApi'
|
|
9
|
+
import { NowApi } from './now/NowApi'
|
|
10
|
+
import { PermissionsApi } from './permissions/PermissionsApi'
|
|
8
11
|
import { ServiceAccessManagementApi } from './serviceAccessManagement/ServiceAccessManagementApi'
|
|
9
12
|
import { StickerUsersApi } from './stickerUsers/StickerUsersApi'
|
|
10
13
|
import { TermsApi } from './terms/TermsApi'
|
|
@@ -84,6 +87,78 @@ const cases: Case[] = [
|
|
|
84
87
|
return (f ? api.withQueryFormat(f) : api).getMany({ page: 2, perPage: 5 })
|
|
85
88
|
},
|
|
86
89
|
},
|
|
90
|
+
{
|
|
91
|
+
name: 'DashboardApi.getGeneral',
|
|
92
|
+
path: '/dashboard/general',
|
|
93
|
+
query: { status: 'closed', departmentId: ['d1'] },
|
|
94
|
+
call: (client, f) => {
|
|
95
|
+
const api = new DashboardApi(client)
|
|
96
|
+
return (f ? api.withQueryFormat(f) : api).getGeneral({
|
|
97
|
+
status: 'closed',
|
|
98
|
+
departmentId: ['d1'],
|
|
99
|
+
})
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'NowApi.getResume',
|
|
104
|
+
path: '/now/resume',
|
|
105
|
+
query: { departmentId: ['d1'], userId: 'u1' },
|
|
106
|
+
call: (client, f) => {
|
|
107
|
+
const api = new NowApi(client)
|
|
108
|
+
return (f ? api.withQueryFormat(f) : api).getResume({ departmentId: ['d1'], userId: 'u1' })
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: 'NowApi.getAttendanceResume',
|
|
113
|
+
path: '/now/attendance-resume',
|
|
114
|
+
query: { departmentId: ['d1'], userId: 'u1' },
|
|
115
|
+
call: (client, f) => {
|
|
116
|
+
const api = new NowApi(client)
|
|
117
|
+
return (f ? api.withQueryFormat(f) : api).getAttendanceResume({
|
|
118
|
+
departmentId: ['d1'],
|
|
119
|
+
userId: 'u1',
|
|
120
|
+
})
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: 'NowApi.getDepartmentsResume',
|
|
125
|
+
path: '/now/departments-resume',
|
|
126
|
+
query: { departmentId: ['d1'], userId: 'u1' },
|
|
127
|
+
call: (client, f) => {
|
|
128
|
+
const api = new NowApi(client)
|
|
129
|
+
return (f ? api.withQueryFormat(f) : api).getDepartmentsResume({
|
|
130
|
+
departmentId: ['d1'],
|
|
131
|
+
userId: 'u1',
|
|
132
|
+
})
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: 'PermissionsApi.getMany',
|
|
137
|
+
path: '/permissions',
|
|
138
|
+
query: listQuery,
|
|
139
|
+
call: (client, f) => {
|
|
140
|
+
const api = new PermissionsApi(client)
|
|
141
|
+
return (f ? api.withQueryFormat(f) : api).getMany(listQuery)
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: 'PermissionsApi.getById',
|
|
146
|
+
path: '/permissions/p1',
|
|
147
|
+
query: getQuery,
|
|
148
|
+
call: (client, f) => {
|
|
149
|
+
const api = new PermissionsApi(client)
|
|
150
|
+
return (f ? api.withQueryFormat(f) : api).getById('p1', { attributes: ['id'] })
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: 'AuthHistoryApi.getById',
|
|
155
|
+
path: '/auth-history/a1',
|
|
156
|
+
query: getQuery,
|
|
157
|
+
call: (client, f) => {
|
|
158
|
+
const api = new AuthHistoryApi(client)
|
|
159
|
+
return (f ? api.withQueryFormat(f) : api).getById('a1', { attributes: ['id'] })
|
|
160
|
+
},
|
|
161
|
+
},
|
|
87
162
|
{
|
|
88
163
|
name: 'ServiceAccessManagementApi.getMany',
|
|
89
164
|
path: '/service-access-management',
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import type { ApiClient } from '../../core/ApiClient'
|
|
2
2
|
import { BaseCrudApi } from '../../core/BaseCrudApi'
|
|
3
|
-
import type { Role, CreateRolePayload, UpdateRolePayload } from './types'
|
|
3
|
+
import type { Role, CreateRolePayload, UpdateRolePayload, RolesListQuery } from './types'
|
|
4
4
|
|
|
5
|
-
export class RolesApi extends BaseCrudApi<
|
|
5
|
+
export class RolesApi extends BaseCrudApi<
|
|
6
|
+
Role,
|
|
7
|
+
CreateRolePayload,
|
|
8
|
+
UpdateRolePayload,
|
|
9
|
+
RolesListQuery
|
|
10
|
+
> {
|
|
6
11
|
constructor(client: ApiClient) {
|
|
7
12
|
super(client, '/roles')
|
|
8
13
|
}
|
package/src/apis/roles/types.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import type { ListQuery } from '../../core/types'
|
|
1
2
|
import type { Permission } from '../permissions/types'
|
|
2
3
|
|
|
4
|
+
/**
|
|
5
|
+
* A role (cargo). Roles are not soft-deleted, so there is no `deletedAt`.
|
|
6
|
+
*/
|
|
3
7
|
export type Role = {
|
|
4
8
|
id: string
|
|
5
9
|
displayName: string
|
|
@@ -7,21 +11,40 @@ export type Role = {
|
|
|
7
11
|
accountId: string
|
|
8
12
|
createdAt: string
|
|
9
13
|
updatedAt: string
|
|
10
|
-
|
|
14
|
+
/** Only when listed with `customInclude: ['usersCount']`. */
|
|
15
|
+
usersCount?: number
|
|
11
16
|
// relationships
|
|
12
17
|
permissions?: Permission[]
|
|
13
18
|
}
|
|
14
19
|
|
|
15
20
|
export type RoleRelationships = 'permissions'
|
|
16
21
|
|
|
22
|
+
/**
|
|
23
|
+
* The permissions of a role, by id or `{ id }`. The backend reads the `permissions` key (also
|
|
24
|
+
* `permissionsId`/`permissionsIds`) — any other key is silently ignored and the role ends up with
|
|
25
|
+
* no permissions.
|
|
26
|
+
*/
|
|
27
|
+
export type RolePermissionRef = string | { id: string }
|
|
28
|
+
|
|
17
29
|
export type CreateRolePayload = {
|
|
30
|
+
/** Up to 255 characters; must be unique in the account. */
|
|
18
31
|
displayName: string
|
|
19
32
|
isAdmin?: boolean
|
|
20
|
-
|
|
33
|
+
permissions?: RolePermissionRef[]
|
|
21
34
|
}
|
|
22
35
|
|
|
23
36
|
export type UpdateRolePayload = {
|
|
24
|
-
|
|
37
|
+
/** Required by the backend on every update (400 `required` otherwise). */
|
|
38
|
+
displayName: string
|
|
25
39
|
isAdmin?: boolean | null
|
|
26
|
-
|
|
40
|
+
/** Replaces the role permissions. An empty array leaves them unchanged. */
|
|
41
|
+
permissions?: RolePermissionRef[]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** List query of `/roles`, with the backend extras to count the users of each role. */
|
|
45
|
+
export type RolesListQuery = ListQuery<Role> & {
|
|
46
|
+
/** `['usersCount']` adds `usersCount` to each role. */
|
|
47
|
+
customInclude?: 'usersCount'[]
|
|
48
|
+
/** Which users `usersCount` counts (default `all`). */
|
|
49
|
+
usersCountFilter?: 'all' | 'active' | 'archived'
|
|
27
50
|
}
|
|
@@ -12,7 +12,10 @@ import type { Term } from './types'
|
|
|
12
12
|
export class TermsApi extends BaseApi implements HasGetById<Term> {
|
|
13
13
|
public readonly basePath = '/terms'
|
|
14
14
|
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Returns a single term by its ID, scoped to the current account. When it does not exist the
|
|
17
|
+
* backend answers 200 with an empty body, which resolves to `undefined`.
|
|
18
|
+
*/
|
|
16
19
|
getById(id: string, query?: GetByIdQuery<Term>, headers?: Record<string, string>): Promise<Term> {
|
|
17
20
|
const queryString = this.toQueryString(query)
|
|
18
21
|
return this.client.get<Term>(`${this.basePath}/${id}${queryString}`, headers)
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
export type TimetableWeekDay = 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A working period of the timetable. `start`/`end` are `HH:MM` and `end` must be later than
|
|
5
|
+
* `start`; the period applies to every day in `weekDays`.
|
|
6
|
+
*/
|
|
1
7
|
export type TimetableWorkSlot = {
|
|
2
|
-
|
|
8
|
+
/** `HH:MM` */
|
|
3
9
|
start: string
|
|
10
|
+
/** `HH:MM`, later than `start`. */
|
|
4
11
|
end: string
|
|
5
|
-
|
|
12
|
+
weekDays: TimetableWeekDay[]
|
|
6
13
|
}
|
|
7
14
|
|
|
8
15
|
export type Timetable = {
|
|
@@ -16,13 +23,12 @@ export type Timetable = {
|
|
|
16
23
|
}
|
|
17
24
|
|
|
18
25
|
export type CreateTimetablePayload = {
|
|
26
|
+
/** Must be unique in the account (409 otherwise, unless the account allows duplicate names). */
|
|
19
27
|
name: string
|
|
28
|
+
/** Minutes before the end of the period to notify. Must be truthy: `0` answers 400 `required`. */
|
|
20
29
|
previousTimeToNotification: number
|
|
21
30
|
workPlan: TimetableWorkSlot[]
|
|
22
31
|
}
|
|
23
32
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
previousTimeToNotification?: number
|
|
27
|
-
workPlan?: TimetableWorkSlot[]
|
|
28
|
-
}
|
|
33
|
+
/** The backend requires the three fields on every update (400 `required` otherwise). */
|
|
34
|
+
export type UpdateTimetablePayload = CreateTimetablePayload
|
package/src/core/BaseCrudApi.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import type { CrudApi, GetByIdQuery, ListQuery, Paginated } from './types'
|
|
1
|
+
import { BaseReadApi } from './BaseReadApi'
|
|
2
|
+
import type { CrudApi, ListQuery } from './types'
|
|
4
3
|
export type {
|
|
5
4
|
WhereClause,
|
|
6
5
|
IncludeItem,
|
|
@@ -15,71 +14,16 @@ export type {
|
|
|
15
14
|
CrudApi,
|
|
16
15
|
} from './types'
|
|
17
16
|
|
|
17
|
+
/** Generic CRUD resource: the reads of `BaseReadApi` plus `create`, `updateById` and `deleteById`. */
|
|
18
18
|
export class BaseCrudApi<
|
|
19
19
|
TResponse,
|
|
20
20
|
TCreate,
|
|
21
21
|
TUpdate = TCreate,
|
|
22
22
|
TQuery extends ListQuery<TResponse> = ListQuery<TResponse>,
|
|
23
23
|
>
|
|
24
|
-
extends
|
|
24
|
+
extends BaseReadApi<TResponse, TQuery>
|
|
25
25
|
implements CrudApi<TResponse, TCreate, TUpdate, TQuery>
|
|
26
26
|
{
|
|
27
|
-
public readonly basePath: string
|
|
28
|
-
|
|
29
|
-
constructor(client: ApiClient, basePath: string) {
|
|
30
|
-
super(client)
|
|
31
|
-
this.basePath = basePath.replace(/\/$/, '')
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
getMany(
|
|
35
|
-
query: TQuery & { paginate: false },
|
|
36
|
-
headers?: Record<string, string>,
|
|
37
|
-
): Promise<TResponse[]>
|
|
38
|
-
getMany(
|
|
39
|
-
query?: TQuery & { paginate?: true },
|
|
40
|
-
headers?: Record<string, string>,
|
|
41
|
-
): Promise<Paginated<TResponse>>
|
|
42
|
-
getMany(
|
|
43
|
-
query?: TQuery,
|
|
44
|
-
headers?: Record<string, string>,
|
|
45
|
-
): Promise<Paginated<TResponse> | TResponse[]>
|
|
46
|
-
getMany(
|
|
47
|
-
query?: TQuery,
|
|
48
|
-
headers?: Record<string, string>,
|
|
49
|
-
): Promise<Paginated<TResponse> | TResponse[]> {
|
|
50
|
-
const queryString = this.toQueryString(query)
|
|
51
|
-
return this.client.get<Paginated<TResponse> | TResponse[]>(
|
|
52
|
-
`${this.basePath}${queryString}`,
|
|
53
|
-
headers,
|
|
54
|
-
)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Returns the first resource matching the query, or `null` when none match.
|
|
59
|
-
* Convenience over `getMany` with `limit: 1`.
|
|
60
|
-
* @permissions $resourceName.view
|
|
61
|
-
*/
|
|
62
|
-
async getOne(query?: TQuery, headers?: Record<string, string>): Promise<TResponse | null> {
|
|
63
|
-
const results = await this.getMany(
|
|
64
|
-
{ ...query, limit: 1, paginate: false } as TQuery & { paginate: false },
|
|
65
|
-
headers,
|
|
66
|
-
)
|
|
67
|
-
// An empty response body resolves to `undefined`.
|
|
68
|
-
return results?.[0] ?? null
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Returns a single resource by ID.
|
|
73
|
-
* @permissions $resourceName.view
|
|
74
|
-
*/
|
|
75
|
-
getById(
|
|
76
|
-
id: string,
|
|
77
|
-
query?: GetByIdQuery<TResponse>,
|
|
78
|
-
headers?: Record<string, string>,
|
|
79
|
-
): Promise<TResponse> {
|
|
80
|
-
return this.client.get<TResponse>(`${this.basePath}/${id}${this.toQueryString(query)}`, headers)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
27
|
/**
|
|
84
28
|
* Creates a new resource.
|
|
85
29
|
* @permissions $resourceName.create
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { ApiClient } from './ApiClient'
|
|
2
|
+
import { BaseApi } from './BaseApi'
|
|
3
|
+
import type { GetByIdQuery, HasGetById, HasGetMany, ListQuery, Paginated } from './types'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generic read-only resource: `getMany`, `getOne` and `getById` under `basePath`. Base of
|
|
7
|
+
* `BaseCrudApi`, and of the resources the backend only serves for reading (e.g. permissions,
|
|
8
|
+
* auth history).
|
|
9
|
+
*/
|
|
10
|
+
export class BaseReadApi<TResponse, TQuery extends ListQuery<TResponse> = ListQuery<TResponse>>
|
|
11
|
+
extends BaseApi
|
|
12
|
+
implements HasGetMany<TResponse, TQuery>, HasGetById<TResponse>
|
|
13
|
+
{
|
|
14
|
+
public readonly basePath: string
|
|
15
|
+
|
|
16
|
+
constructor(client: ApiClient, basePath: string) {
|
|
17
|
+
super(client)
|
|
18
|
+
this.basePath = basePath.replace(/\/$/, '')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
getMany(
|
|
22
|
+
query: TQuery & { paginate: false },
|
|
23
|
+
headers?: Record<string, string>,
|
|
24
|
+
): Promise<TResponse[]>
|
|
25
|
+
getMany(
|
|
26
|
+
query?: TQuery & { paginate?: true },
|
|
27
|
+
headers?: Record<string, string>,
|
|
28
|
+
): Promise<Paginated<TResponse>>
|
|
29
|
+
getMany(
|
|
30
|
+
query?: TQuery,
|
|
31
|
+
headers?: Record<string, string>,
|
|
32
|
+
): Promise<Paginated<TResponse> | TResponse[]>
|
|
33
|
+
getMany(
|
|
34
|
+
query?: TQuery,
|
|
35
|
+
headers?: Record<string, string>,
|
|
36
|
+
): Promise<Paginated<TResponse> | TResponse[]> {
|
|
37
|
+
const queryString = this.toQueryString(query)
|
|
38
|
+
return this.client.get<Paginated<TResponse> | TResponse[]>(
|
|
39
|
+
`${this.basePath}${queryString}`,
|
|
40
|
+
headers,
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Returns the first resource matching the query, or `null` when none match.
|
|
46
|
+
* Convenience over `getMany` with `limit: 1`.
|
|
47
|
+
* @permissions $resourceName.view
|
|
48
|
+
*/
|
|
49
|
+
async getOne(query?: TQuery, headers?: Record<string, string>): Promise<TResponse | null> {
|
|
50
|
+
const results = await this.getMany(
|
|
51
|
+
{ ...query, limit: 1, paginate: false } as TQuery & { paginate: false },
|
|
52
|
+
headers,
|
|
53
|
+
)
|
|
54
|
+
// An empty response body resolves to `undefined`.
|
|
55
|
+
return results?.[0] ?? null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Returns a single resource by ID. When it does not exist most routes answer 200 with an empty
|
|
60
|
+
* body, which resolves to `undefined`.
|
|
61
|
+
* @permissions $resourceName.view
|
|
62
|
+
*/
|
|
63
|
+
getById(
|
|
64
|
+
id: string,
|
|
65
|
+
query?: GetByIdQuery<TResponse>,
|
|
66
|
+
headers?: Record<string, string>,
|
|
67
|
+
): Promise<TResponse> {
|
|
68
|
+
return this.client.get<TResponse>(`${this.basePath}/${id}${this.toQueryString(query)}`, headers)
|
|
69
|
+
}
|
|
70
|
+
}
|
package/src/core/index.ts
CHANGED
|
@@ -20,6 +20,7 @@ export {
|
|
|
20
20
|
} from './errors'
|
|
21
21
|
export type { FieldError, ErrorClassName, ApiErrorOptions } from './errors'
|
|
22
22
|
export { BaseApi } from './BaseApi'
|
|
23
|
+
export { BaseReadApi } from './BaseReadApi'
|
|
23
24
|
export { BaseCrudApi } from './BaseCrudApi'
|
|
24
25
|
export { ArchivableCrudApi } from './ArchivableCrudApi'
|
|
25
26
|
export type { WhereClause, IncludeItem, ListQuery, GetByIdQuery, Paginated } from './types'
|