@tak-ps/node-tak 12.29.0 → 12.31.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/CHANGELOG.md +9 -1
- package/dist/lib/api/mission-property.d.ts +65 -0
- package/dist/lib/api/mission-property.js +114 -0
- package/dist/lib/api/mission-property.js.map +1 -0
- package/dist/lib/api/user-management.d.ts +168 -0
- package/dist/lib/api/user-management.js +296 -0
- package/dist/lib/api/user-management.js.map +1 -0
- package/dist/lib/api.d.ts +5 -1
- package/dist/lib/api.js +10 -2
- package/dist/lib/api.js.map +1 -1
- package/dist/test/mission-property.test.d.ts +1 -0
- package/dist/test/mission-property.test.js +73 -0
- package/dist/test/mission-property.test.js.map +1 -0
- package/dist/test/user-management.test.d.ts +1 -0
- package/dist/test/user-management.test.js +158 -0
- package/dist/test/user-management.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/lib/api/mission-property.ts +155 -0
- package/lib/api/user-management.ts +326 -0
- package/lib/api.ts +11 -1
- package/package.json +1 -1
- package/test/mission-property.test.ts +95 -0
- package/test/user-management.test.ts +207 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { Type, Static } from '@sinclair/typebox';
|
|
2
|
+
import Commands, { CommandOutputFormat, type ParsedArgs } from '../commands.js';
|
|
3
|
+
|
|
4
|
+
const GroupLists = {
|
|
5
|
+
groupList: Type.Optional(Type.Array(Type.String())),
|
|
6
|
+
groupListIN: Type.Optional(Type.Array(Type.String())),
|
|
7
|
+
groupListOUT: Type.Optional(Type.Array(Type.String()))
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const UserManagementUsername = Type.Object({
|
|
11
|
+
username: Type.String()
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export const UserManagementGroupName = Type.Object({
|
|
15
|
+
groupname: Type.String()
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const UserManagementNewUser = Type.Object({
|
|
19
|
+
username: Type.String(),
|
|
20
|
+
password: Type.String(),
|
|
21
|
+
...GroupLists
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export const UserManagementBulkUsers = Type.Object({
|
|
25
|
+
usernameExpression: Type.String({ description: 'Username pattern containing [N] which is replaced by each integer from startN to endN' }),
|
|
26
|
+
startN: Type.Integer(),
|
|
27
|
+
endN: Type.Integer(),
|
|
28
|
+
...GroupLists
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export const UserManagementUserPassword = Type.Object({
|
|
32
|
+
username: Type.String(),
|
|
33
|
+
password: Type.String()
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export const UserManagementUserGroups = Type.Object({
|
|
37
|
+
username: Type.String(),
|
|
38
|
+
...GroupLists
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
export const UserManagementGroupUsers = Type.Object({
|
|
42
|
+
groupname: Type.String(),
|
|
43
|
+
usersInGroupList: Type.Optional(Type.Array(Type.String())),
|
|
44
|
+
usersInGroupListIN: Type.Optional(Type.Array(Type.String())),
|
|
45
|
+
usersInGroupListOUT: Type.Optional(Type.Array(Type.String()))
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export const UserManagementList_Username = Type.Array(UserManagementUsername);
|
|
49
|
+
export const UserManagementList_GroupName = Type.Array(UserManagementGroupName);
|
|
50
|
+
export const UserManagementList_UserPassword = Type.Array(UserManagementUserPassword);
|
|
51
|
+
|
|
52
|
+
export const UserManagementUserGroupsResponse = Type.Object({
|
|
53
|
+
username: Type.String(),
|
|
54
|
+
groupList: Type.Array(Type.String()),
|
|
55
|
+
groupListIN: Type.Array(Type.String()),
|
|
56
|
+
groupListOUT: Type.Array(Type.String())
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
export const UserManagementGroupUsersResponse = Type.Object({
|
|
60
|
+
groupname: Type.String(),
|
|
61
|
+
usersInGroupList: Type.Array(Type.String()),
|
|
62
|
+
usersInGroupListIN: Type.Array(Type.String()),
|
|
63
|
+
usersInGroupListOUT: Type.Array(Type.String())
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Manage File (non-LDAP) User Accounts on TAK Server
|
|
68
|
+
*
|
|
69
|
+
* {@link https://docs.tak.gov/api/takserver#tag/file-user-account-management-api TAK Server Docs}.
|
|
70
|
+
*/
|
|
71
|
+
export default class UserManagementCommands extends Commands {
|
|
72
|
+
schema = {
|
|
73
|
+
'list-users': {
|
|
74
|
+
description: 'List File Users',
|
|
75
|
+
params: Type.Object({}),
|
|
76
|
+
query: Type.Object({}),
|
|
77
|
+
formats: [ CommandOutputFormat.JSON ]
|
|
78
|
+
},
|
|
79
|
+
'list-groups': {
|
|
80
|
+
description: 'List Group Names used by File Users',
|
|
81
|
+
params: Type.Object({}),
|
|
82
|
+
query: Type.Object({}),
|
|
83
|
+
formats: [ CommandOutputFormat.JSON ]
|
|
84
|
+
},
|
|
85
|
+
'user-groups': {
|
|
86
|
+
description: 'List Groups for a given File User',
|
|
87
|
+
params: Type.Object({ username: Type.String() }),
|
|
88
|
+
query: Type.Object({}),
|
|
89
|
+
formats: [ CommandOutputFormat.JSON ]
|
|
90
|
+
},
|
|
91
|
+
'group-users': {
|
|
92
|
+
description: 'List File Users in a given Group',
|
|
93
|
+
params: Type.Object({ group: Type.String() }),
|
|
94
|
+
query: Type.Object({}),
|
|
95
|
+
formats: [ CommandOutputFormat.JSON ]
|
|
96
|
+
},
|
|
97
|
+
'delete-user': {
|
|
98
|
+
description: 'Delete a File User',
|
|
99
|
+
params: Type.Object({ username: Type.String() }),
|
|
100
|
+
query: Type.Object({}),
|
|
101
|
+
formats: [ CommandOutputFormat.JSON ]
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async cli(args: ParsedArgs): Promise<object | string> {
|
|
106
|
+
if (args._[3] === 'list-users') {
|
|
107
|
+
const list = await this.listUsers();
|
|
108
|
+
|
|
109
|
+
if (args.format === 'json') {
|
|
110
|
+
return list;
|
|
111
|
+
} else {
|
|
112
|
+
return list.map((user) => user.username).join('\n');
|
|
113
|
+
}
|
|
114
|
+
} else if (args._[3] === 'list-groups') {
|
|
115
|
+
const list = await this.listGroups();
|
|
116
|
+
|
|
117
|
+
if (args.format === 'json') {
|
|
118
|
+
return list;
|
|
119
|
+
} else {
|
|
120
|
+
return list.map((group) => group.groupname).join('\n');
|
|
121
|
+
}
|
|
122
|
+
} else if (args._[3] === 'user-groups') {
|
|
123
|
+
if (!args._[4]) throw new Error('username must be provided');
|
|
124
|
+
|
|
125
|
+
const user = await this.userGroups(args._[4]);
|
|
126
|
+
|
|
127
|
+
if (args.format === 'json') {
|
|
128
|
+
return user;
|
|
129
|
+
} else {
|
|
130
|
+
return [
|
|
131
|
+
`${user.username}`,
|
|
132
|
+
` BOTH: ${user.groupList.join(', ')}`,
|
|
133
|
+
` IN: ${user.groupListIN.join(', ')}`,
|
|
134
|
+
` OUT: ${user.groupListOUT.join(', ')}`
|
|
135
|
+
].join('\n');
|
|
136
|
+
}
|
|
137
|
+
} else if (args._[3] === 'group-users') {
|
|
138
|
+
if (!args._[4]) throw new Error('group must be provided');
|
|
139
|
+
|
|
140
|
+
const group = await this.groupUsers(args._[4]);
|
|
141
|
+
|
|
142
|
+
if (args.format === 'json') {
|
|
143
|
+
return group;
|
|
144
|
+
} else {
|
|
145
|
+
return [
|
|
146
|
+
`${group.groupname}`,
|
|
147
|
+
` BOTH: ${group.usersInGroupList.join(', ')}`,
|
|
148
|
+
` IN: ${group.usersInGroupListIN.join(', ')}`,
|
|
149
|
+
` OUT: ${group.usersInGroupListOUT.join(', ')}`
|
|
150
|
+
].join('\n');
|
|
151
|
+
}
|
|
152
|
+
} else if (args._[3] === 'delete-user') {
|
|
153
|
+
if (!args._[4]) throw new Error('username must be provided');
|
|
154
|
+
|
|
155
|
+
await this.deleteUser(args._[4]);
|
|
156
|
+
|
|
157
|
+
return `Deleted ${args._[4]}`;
|
|
158
|
+
} else {
|
|
159
|
+
throw new Error('Unsupported Subcommand');
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Create a new File User
|
|
165
|
+
*
|
|
166
|
+
* {@link https://docs.tak.gov/api/takserver#tag/file-user-account-management-api/operation/createOrUpdateFileUser TAK Server Docs}.
|
|
167
|
+
*/
|
|
168
|
+
async createUser(user: Static<typeof UserManagementNewUser>): Promise<void> {
|
|
169
|
+
const url = new URL('/Marti/api/user-management/api/new-user', this.api.url);
|
|
170
|
+
|
|
171
|
+
await this.api.fetch(url, {
|
|
172
|
+
method: 'POST',
|
|
173
|
+
body: {
|
|
174
|
+
username: user.username,
|
|
175
|
+
password: user.password,
|
|
176
|
+
groupList: user.groupList ?? [],
|
|
177
|
+
groupListIN: user.groupListIN ?? [],
|
|
178
|
+
groupListOUT: user.groupListOUT ?? []
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Create File Users in bulk from a username expression containing `[N]`
|
|
185
|
+
* Passwords are generated by the server and only returned once
|
|
186
|
+
*
|
|
187
|
+
* {@link https://docs.tak.gov/api/takserver#tag/file-user-account-management-api/operation/createFileUsersInBulk TAK Server Docs}.
|
|
188
|
+
*/
|
|
189
|
+
async createUsers(
|
|
190
|
+
bulk: Static<typeof UserManagementBulkUsers>
|
|
191
|
+
): Promise<Static<typeof UserManagementList_UserPassword>> {
|
|
192
|
+
const url = new URL('/Marti/api/user-management/api/new-users', this.api.url);
|
|
193
|
+
|
|
194
|
+
return await this.api.fetch(url, {
|
|
195
|
+
method: 'POST',
|
|
196
|
+
body: {
|
|
197
|
+
usernameExpression: bulk.usernameExpression,
|
|
198
|
+
startN: bulk.startN,
|
|
199
|
+
endN: bulk.endN,
|
|
200
|
+
groupList: bulk.groupList ?? [],
|
|
201
|
+
groupListIN: bulk.groupListIN ?? [],
|
|
202
|
+
groupListOUT: bulk.groupListOUT ?? []
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* List all File Users
|
|
209
|
+
*
|
|
210
|
+
* {@link https://docs.tak.gov/api/takserver#tag/file-user-account-management-api/operation/getAllUsers_3 TAK Server Docs}.
|
|
211
|
+
*/
|
|
212
|
+
async listUsers(): Promise<Static<typeof UserManagementList_Username>> {
|
|
213
|
+
const url = new URL('/Marti/api/user-management/api/list-users', this.api.url);
|
|
214
|
+
|
|
215
|
+
return await this.api.fetch(url, {
|
|
216
|
+
method: 'GET'
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Get the Groups associated with a File User
|
|
222
|
+
*
|
|
223
|
+
* {@link https://docs.tak.gov/api/takserver#tag/file-user-account-management-api/operation/getGroupsForUsers TAK Server Docs}.
|
|
224
|
+
*/
|
|
225
|
+
async userGroups(username: string): Promise<Static<typeof UserManagementUserGroupsResponse>> {
|
|
226
|
+
const url = new URL(`/Marti/api/user-management/api/get-groups-for-user/${encodeURIComponent(username)}`, this.api.url);
|
|
227
|
+
|
|
228
|
+
return await this.api.fetch(url, {
|
|
229
|
+
method: 'GET'
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Change the password of a File User
|
|
235
|
+
*
|
|
236
|
+
* {@link https://docs.tak.gov/api/takserver#tag/file-user-account-management-api/operation/changeUserPassword TAK Server Docs}.
|
|
237
|
+
*/
|
|
238
|
+
async changePassword(user: Static<typeof UserManagementUserPassword>): Promise<void> {
|
|
239
|
+
const url = new URL('/Marti/api/user-management/api/change-user-password', this.api.url);
|
|
240
|
+
|
|
241
|
+
await this.api.fetch(url, {
|
|
242
|
+
method: 'PUT',
|
|
243
|
+
body: {
|
|
244
|
+
username: user.username,
|
|
245
|
+
password: user.password
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Replace the Groups associated with a File User
|
|
252
|
+
*
|
|
253
|
+
* {@link https://docs.tak.gov/api/takserver#tag/file-user-account-management-api/operation/updateGroupsForUser TAK Server Docs}.
|
|
254
|
+
*/
|
|
255
|
+
async updateUserGroups(user: Static<typeof UserManagementUserGroups>): Promise<void> {
|
|
256
|
+
const url = new URL('/Marti/api/user-management/api/update-groups', this.api.url);
|
|
257
|
+
|
|
258
|
+
await this.api.fetch(url, {
|
|
259
|
+
method: 'PUT',
|
|
260
|
+
body: {
|
|
261
|
+
username: user.username,
|
|
262
|
+
groupList: user.groupList ?? [],
|
|
263
|
+
groupListIN: user.groupListIN ?? [],
|
|
264
|
+
groupListOUT: user.groupListOUT ?? []
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Replace the File Users associated with a Group
|
|
271
|
+
*
|
|
272
|
+
* {@link https://docs.tak.gov/api/takserver#tag/file-user-account-management-api/operation/updateUsersForGroup TAK Server Docs}.
|
|
273
|
+
*/
|
|
274
|
+
async updateGroupUsers(group: Static<typeof UserManagementGroupUsers>): Promise<void> {
|
|
275
|
+
const url = new URL('/Marti/api/user-management/api/update-group-users', this.api.url);
|
|
276
|
+
|
|
277
|
+
await this.api.fetch(url, {
|
|
278
|
+
method: 'PUT',
|
|
279
|
+
body: {
|
|
280
|
+
groupname: group.groupname,
|
|
281
|
+
usersInGroupList: group.usersInGroupList ?? [],
|
|
282
|
+
usersInGroupListIN: group.usersInGroupListIN ?? [],
|
|
283
|
+
usersInGroupListOUT: group.usersInGroupListOUT ?? []
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Delete a File User
|
|
290
|
+
*
|
|
291
|
+
* {@link https://docs.tak.gov/api/takserver#tag/file-user-account-management-api/operation/deleteUser_1 TAK Server Docs}.
|
|
292
|
+
*/
|
|
293
|
+
async deleteUser(username: string): Promise<void> {
|
|
294
|
+
const url = new URL(`/Marti/api/user-management/api/delete-user/${encodeURIComponent(username)}`, this.api.url);
|
|
295
|
+
|
|
296
|
+
await this.api.fetch(url, {
|
|
297
|
+
method: 'DELETE'
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* List all Group Names used by File Users
|
|
303
|
+
*
|
|
304
|
+
* {@link https://docs.tak.gov/api/takserver#tag/file-user-account-management-api/operation/getAllGroupNames TAK Server Docs}.
|
|
305
|
+
*/
|
|
306
|
+
async listGroups(): Promise<Static<typeof UserManagementList_GroupName>> {
|
|
307
|
+
const url = new URL('/Marti/api/user-management/api/list-groupnames', this.api.url);
|
|
308
|
+
|
|
309
|
+
return await this.api.fetch(url, {
|
|
310
|
+
method: 'GET'
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Get the File Users in a Group along with their IN/OUT/BOTH direction
|
|
316
|
+
*
|
|
317
|
+
* {@link https://docs.tak.gov/api/takserver#tag/file-user-account-management-api/operation/getUsersInGroup TAK Server Docs}.
|
|
318
|
+
*/
|
|
319
|
+
async groupUsers(group: string): Promise<Static<typeof UserManagementGroupUsersResponse>> {
|
|
320
|
+
const url = new URL(`/Marti/api/user-management/api/users-in-group/${encodeURIComponent(group)}`, this.api.url);
|
|
321
|
+
|
|
322
|
+
return await this.api.fetch(url, {
|
|
323
|
+
method: 'GET'
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}
|
package/lib/api.ts
CHANGED
|
@@ -6,6 +6,7 @@ import Locate from './api/locate.js';
|
|
|
6
6
|
import Mission, { MissionSubscriberRole } from './api/mission.js';
|
|
7
7
|
import MissionInvite, { MissionInviteType } from './api/mission-invite.js';
|
|
8
8
|
import MissionLog from './api/mission-log.js';
|
|
9
|
+
import MissionProperty from './api/mission-property.js';
|
|
9
10
|
import MissionLayer, { MissionLayerType } from './api/mission-layer.js';
|
|
10
11
|
import Credentials from './api/credentials.js';
|
|
11
12
|
import Security from './api/security.js';
|
|
@@ -20,6 +21,7 @@ import Group from './api/groups.js';
|
|
|
20
21
|
import Subscription from './api/subscriptions.js';
|
|
21
22
|
import Video from './api/video.js';
|
|
22
23
|
import Export from './api/export.js';
|
|
24
|
+
import UserManagement from './api/user-management.js';
|
|
23
25
|
import Err from '@openaddresses/batch-error';
|
|
24
26
|
import { TAKServerError, isHTML } from './utils/html-error.js';
|
|
25
27
|
import * as auth from './auth.js';
|
|
@@ -75,6 +77,7 @@ export const CommandList: Record<string, keyof TAKAPI> = {
|
|
|
75
77
|
mission: 'Mission',
|
|
76
78
|
'mission-invite': 'MissionInvite',
|
|
77
79
|
'mission-log': 'MissionLog',
|
|
80
|
+
'mission-property': 'MissionProperty',
|
|
78
81
|
'mission-layer': 'MissionLayer',
|
|
79
82
|
credential: 'Credentials',
|
|
80
83
|
certificate: 'Certificate',
|
|
@@ -88,7 +91,8 @@ export const CommandList: Record<string, keyof TAKAPI> = {
|
|
|
88
91
|
video: 'Video',
|
|
89
92
|
export: 'Export',
|
|
90
93
|
query: 'Query',
|
|
91
|
-
file: 'Files'
|
|
94
|
+
file: 'Files',
|
|
95
|
+
'user-management': 'UserManagement'
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
/**
|
|
@@ -105,6 +109,7 @@ export default class TAKAPI {
|
|
|
105
109
|
Iconsets: Iconsets;
|
|
106
110
|
Mission: Mission;
|
|
107
111
|
MissionLog: MissionLog;
|
|
112
|
+
MissionProperty: MissionProperty;
|
|
108
113
|
MissionInvite: MissionInvite;
|
|
109
114
|
MissionLayer: MissionLayer;
|
|
110
115
|
Credentials: Credentials;
|
|
@@ -120,6 +125,7 @@ export default class TAKAPI {
|
|
|
120
125
|
Export: Export;
|
|
121
126
|
Query: Query;
|
|
122
127
|
Files: Files;
|
|
128
|
+
UserManagement: UserManagement;
|
|
123
129
|
|
|
124
130
|
constructor(url: URL, auth: auth.APIAuth) {
|
|
125
131
|
this.url = url;
|
|
@@ -136,6 +142,7 @@ export default class TAKAPI {
|
|
|
136
142
|
this.Iconsets = new Iconsets(this);
|
|
137
143
|
this.Mission = new Mission(this);
|
|
138
144
|
this.MissionLog = new MissionLog(this);
|
|
145
|
+
this.MissionProperty = new MissionProperty(this);
|
|
139
146
|
this.MissionInvite = new MissionInvite(this);
|
|
140
147
|
this.MissionLayer = new MissionLayer(this);
|
|
141
148
|
this.Client = new Client(this);
|
|
@@ -147,6 +154,7 @@ export default class TAKAPI {
|
|
|
147
154
|
this.Injectors = new Injectors(this);
|
|
148
155
|
this.Repeater = new Repeater(this);
|
|
149
156
|
this.Files = new Files(this);
|
|
157
|
+
this.UserManagement = new UserManagement(this);
|
|
150
158
|
}
|
|
151
159
|
|
|
152
160
|
static async init(url: URL, auth: auth.APIAuth): Promise<TAKAPI> {
|
|
@@ -242,6 +250,7 @@ export {
|
|
|
242
250
|
Mission,
|
|
243
251
|
MissionInvite,
|
|
244
252
|
MissionLog,
|
|
253
|
+
MissionProperty,
|
|
245
254
|
MissionLayer,
|
|
246
255
|
Client,
|
|
247
256
|
Credentials,
|
|
@@ -256,6 +265,7 @@ export {
|
|
|
256
265
|
Subscription,
|
|
257
266
|
Video,
|
|
258
267
|
Export,
|
|
268
|
+
UserManagement,
|
|
259
269
|
MissionSubscriberRole,
|
|
260
270
|
MissionLayerType,
|
|
261
271
|
MissionInviteType
|
package/package.json
CHANGED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { Readable } from 'node:stream';
|
|
4
|
+
import { Client } from 'undici';
|
|
5
|
+
import type { Dispatcher } from 'undici';
|
|
6
|
+
import TAKAPI from '../lib/api.js';
|
|
7
|
+
import { APIAuthCertificate } from '../lib/auth.js';
|
|
8
|
+
|
|
9
|
+
type RequestArgs = Parameters<Client['request']>[0];
|
|
10
|
+
|
|
11
|
+
const GUID = '9ba05e1d-970f-49e1-a153-7216fecf5279';
|
|
12
|
+
|
|
13
|
+
function mockResponse(statusCode: number, contentType: string, body: string): Dispatcher.ResponseData {
|
|
14
|
+
return {
|
|
15
|
+
statusCode,
|
|
16
|
+
headers: { 'content-type': contentType },
|
|
17
|
+
body: Readable.from([Buffer.from(body)]),
|
|
18
|
+
trailers: {}
|
|
19
|
+
} as unknown as Dispatcher.ResponseData;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function capture(
|
|
23
|
+
response: string,
|
|
24
|
+
fn: (api: TAKAPI) => Promise<unknown>
|
|
25
|
+
): Promise<{ requests: RequestArgs[], result: unknown }> {
|
|
26
|
+
const originalRequest = Client.prototype.request;
|
|
27
|
+
const requests: RequestArgs[] = [];
|
|
28
|
+
|
|
29
|
+
Client.prototype.request = async function(opts: RequestArgs): Promise<Dispatcher.ResponseData> {
|
|
30
|
+
requests.push(opts);
|
|
31
|
+
return mockResponse(200, response ? 'application/json' : 'text/plain', response);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const api = new TAKAPI(new URL('https://tak.example.com'), new APIAuthCertificate('cert', 'key'));
|
|
36
|
+
const result = await fn(api);
|
|
37
|
+
return { requests, result };
|
|
38
|
+
} finally {
|
|
39
|
+
Client.prototype.request = originalRequest;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const ITEM = JSON.stringify({ version: '3', type: 'com.bbn.marti.sync.model.MissionProperty', data: { key: 'test.hello', value: 'world' } });
|
|
44
|
+
const LIST = JSON.stringify({ version: '3', type: 'com.bbn.marti.sync.model.MissionProperty', data: [{ key: 'test.hello', value: 'world' }] });
|
|
45
|
+
|
|
46
|
+
test('MissionProperty.list uses the guid properties endpoint with a prefix filter', async () => {
|
|
47
|
+
const { requests, result } = await capture(LIST, (api) => api.MissionProperty.list(GUID, { prefix: 'test.' }));
|
|
48
|
+
|
|
49
|
+
assert.equal(requests.length, 1);
|
|
50
|
+
assert.equal(requests[0].method, 'GET');
|
|
51
|
+
assert.equal(requests[0].path, `/Marti/api/missions/guid/${GUID}/properties?prefix=test.`);
|
|
52
|
+
assert.deepEqual((result as { data: unknown }).data, [{ key: 'test.hello', value: 'world' }]);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('MissionProperty.get encodes the key in the path', async () => {
|
|
56
|
+
const { requests } = await capture(ITEM, (api) => api.MissionProperty.get(GUID, 'a b'));
|
|
57
|
+
|
|
58
|
+
assert.equal(requests[0].method, 'GET');
|
|
59
|
+
assert.equal(requests[0].path, `/Marti/api/missions/guid/${GUID}/properties/a%20b`);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('MissionProperty.set PUTs key/value with creatorUid as a query param', async () => {
|
|
63
|
+
const { requests } = await capture(ITEM, (api) => api.MissionProperty.set(GUID, {
|
|
64
|
+
key: 'test.hello',
|
|
65
|
+
value: 'world',
|
|
66
|
+
creatorUid: 'alice'
|
|
67
|
+
}));
|
|
68
|
+
|
|
69
|
+
assert.equal(requests[0].method, 'PUT');
|
|
70
|
+
assert.equal(requests[0].path, `/Marti/api/missions/guid/${GUID}/properties?creatorUid=alice`);
|
|
71
|
+
assert.deepEqual(JSON.parse(String(requests[0].body)), { key: 'test.hello', value: 'world' });
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('MissionProperty.delete removes a single key', async () => {
|
|
75
|
+
const { requests } = await capture('', (api) => api.MissionProperty.delete(GUID, 'test.hello', 'alice'));
|
|
76
|
+
|
|
77
|
+
assert.equal(requests[0].method, 'DELETE');
|
|
78
|
+
assert.equal(requests[0].path, `/Marti/api/missions/guid/${GUID}/properties/test.hello?creatorUid=alice`);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('MissionProperty.deleteAll removes every key', async () => {
|
|
82
|
+
const { requests } = await capture('', (api) => api.MissionProperty.deleteAll(GUID, 'alice'));
|
|
83
|
+
|
|
84
|
+
assert.equal(requests[0].method, 'DELETE');
|
|
85
|
+
assert.equal(requests[0].path, `/Marti/api/missions/guid/${GUID}/properties?creatorUid=alice`);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('MissionProperty resolves a mission name to its GUID first', async () => {
|
|
89
|
+
const MISSION = JSON.stringify({ version: '3', type: 'Mission', data: [{ name: 'kv-test', guid: GUID }] });
|
|
90
|
+
const { requests } = await capture(MISSION, (api) => api.MissionProperty.list('kv-test'));
|
|
91
|
+
|
|
92
|
+
assert.equal(requests.length, 2);
|
|
93
|
+
assert.equal(requests[0].path, '/Marti/api/missions/kv-test');
|
|
94
|
+
assert.equal(requests[1].path, `/Marti/api/missions/guid/${GUID}/properties`);
|
|
95
|
+
});
|