@camunda/ccma-api 3.0.0 → 4.0.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/dist/index.d.ts +12 -8
- package/dist/index.js +4 -0
- package/dist/index.js.map +2 -2
- package/dist/services/accounts/approvals.m2m.service.d.ts +157 -0
- package/dist/services/accounts/approvals.m2m.service.js +226 -0
- package/dist/services/accounts/approvals.m2m.service.js.map +2 -2
- package/dist/services/accounts/hdp.service.d.ts +16 -3
- package/dist/services/accounts/hdp.service.js +45 -2
- package/dist/services/accounts/hdp.service.js.map +2 -2
- package/dist/services/accounts/orgs.service.d.ts +17 -1
- package/dist/services/accounts/orgs.service.js +33 -0
- package/dist/services/accounts/orgs.service.js.map +2 -2
- package/dist/services/console/console.approvals.m2m.service.d.ts +12 -1
- package/dist/services/console/console.approvals.m2m.service.js +31 -0
- package/dist/services/console/console.approvals.m2m.service.js.map +2 -2
- package/dist/services/control/control.approvals.service.d.ts +276 -0
- package/dist/services/control/control.approvals.service.js +155 -0
- package/dist/services/control/control.approvals.service.js.map +2 -2
- package/dist/services/control/control.dashboards.service.d.ts +76 -0
- package/dist/services/control/control.dashboards.service.js +180 -0
- package/dist/services/control/control.dashboards.service.js.map +7 -0
- package/dist/services/control/control.dashboards.service.spec.d.ts +1 -0
- package/dist/services/control/control.feature-flags.service.d.ts +53 -0
- package/dist/services/control/control.feature-flags.service.js +123 -0
- package/dist/services/control/control.feature-flags.service.js.map +7 -0
- package/dist/services/control/control.feature-flags.service.spec.d.ts +1 -0
- package/dist/services/control/control.platform.service.d.ts +14 -0
- package/dist/services/control/control.platform.service.js +27 -0
- package/dist/services/control/control.platform.service.js.map +2 -2
- package/dist/services/control/control.platform.service.spec.d.ts +1 -0
- package/dist/services/external/external.accounts.service.d.ts +2 -2
- package/dist/services/external/external.accounts.service.js.map +2 -2
- package/dist/services/github/github.service.d.ts +20 -0
- package/dist/services/github/github.service.js +50 -1
- package/dist/services/github/github.service.js.map +2 -2
- package/dist/services/openrouter/openrouter.service.d.ts +35 -0
- package/dist/services/openrouter/openrouter.service.js +45 -0
- package/dist/services/openrouter/openrouter.service.js.map +2 -2
- package/dist/services/slack/slack-blockkit.service.d.ts +7 -0
- package/dist/services/slack/slack-blockkit.service.js +1 -0
- package/dist/services/slack/slack-blockkit.service.js.map +2 -2
- package/dist/utils/environments.js +3 -4
- package/dist/utils/environments.js.map +2 -2
- package/dist/utils/sse.d.ts +1 -0
- package/dist/utils/sse.js +7 -0
- package/dist/utils/sse.js.map +2 -2
- package/package.json +2 -2
|
@@ -121,6 +121,160 @@ export interface MoveClusterOrgPayloadDto {
|
|
|
121
121
|
/** A copy, for the same reason as `clusterName`. */
|
|
122
122
|
targetOrgName?: string;
|
|
123
123
|
}
|
|
124
|
+
export interface ChangePlatformRolesPayloadDto {
|
|
125
|
+
userId: string;
|
|
126
|
+
/** A copy, so a list shows a person and not an Auth0 id. Nothing reads it. */
|
|
127
|
+
userEmail?: string;
|
|
128
|
+
/**
|
|
129
|
+
* A CHANGE, and never the whole set. Accounts applies it over the roles that
|
|
130
|
+
* the user holds when the action RUNS, so a role change that somebody else
|
|
131
|
+
* made while the request waited stays.
|
|
132
|
+
*/
|
|
133
|
+
rolesToAdd: string[];
|
|
134
|
+
rolesToRemove: string[];
|
|
135
|
+
}
|
|
136
|
+
export interface ChangeOrgLimitsPayloadDto {
|
|
137
|
+
orgId: string;
|
|
138
|
+
/** A copy, so a list shows a name that a person knows. Nothing reads it. */
|
|
139
|
+
orgName?: string;
|
|
140
|
+
/** One key of `allSalesPlanLimits()`. One limit for each request. */
|
|
141
|
+
limitKey: string;
|
|
142
|
+
/**
|
|
143
|
+
* The override that the person who asked READ. Absent for "not set".
|
|
144
|
+
*
|
|
145
|
+
* Accounts compares it with the override that it holds when the action
|
|
146
|
+
* runs, and writes nothing when the two differ. See `staleApprovalKeys`.
|
|
147
|
+
*/
|
|
148
|
+
expected?: string | number | boolean;
|
|
149
|
+
/** The new override. `null` clears it, and the default of the plan applies. */
|
|
150
|
+
value: string | number | boolean | null;
|
|
151
|
+
}
|
|
152
|
+
export interface ChangeSalesPlanPayloadDto {
|
|
153
|
+
orgId: string;
|
|
154
|
+
/** A copy, so a list shows a name that a person knows. Nothing reads it. */
|
|
155
|
+
orgName?: string;
|
|
156
|
+
salesPlanId: string;
|
|
157
|
+
/** A copy of the name of the new plan, for the approver to read. */
|
|
158
|
+
salesPlanName?: string;
|
|
159
|
+
/**
|
|
160
|
+
* The plan that the person who asked READ. Absent for "no plan".
|
|
161
|
+
*
|
|
162
|
+
* Accounts compares it with the plan that it holds when the action runs,
|
|
163
|
+
* and switches nothing when the two differ.
|
|
164
|
+
*/
|
|
165
|
+
expectedSalesPlanId?: string;
|
|
166
|
+
/** A copy of the name of the current plan, for the approver to read. */
|
|
167
|
+
expectedSalesPlanName?: string;
|
|
168
|
+
/** Skips the validation of the switch. The approver sees it. */
|
|
169
|
+
force: boolean;
|
|
170
|
+
}
|
|
171
|
+
/** Every value of a flag row that a person can change. */
|
|
172
|
+
export interface FeatureFlagValuesDto {
|
|
173
|
+
description: string;
|
|
174
|
+
enabled: boolean;
|
|
175
|
+
orgIds: string[];
|
|
176
|
+
emailDomains: string[];
|
|
177
|
+
/** The team that owns the flag. A create needs it. An older row has none. */
|
|
178
|
+
owner?: string;
|
|
179
|
+
/** `YYYY-MM-DD`, the day after which the flag is late. */
|
|
180
|
+
expiresAt?: string;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* One change of the feature flag table: the action, and the whole row that it
|
|
184
|
+
* asks for.
|
|
185
|
+
*
|
|
186
|
+
* An edit and a delete carry the row that the person who asked READ, as
|
|
187
|
+
* `expected`. The action writes nothing when the row changed in the meantime.
|
|
188
|
+
* `key` of an edit and a delete is a copy for the approver, and the action
|
|
189
|
+
* compares it too.
|
|
190
|
+
*/
|
|
191
|
+
export type ChangeFeatureFlagPayloadDto = {
|
|
192
|
+
action: 'create';
|
|
193
|
+
key: string;
|
|
194
|
+
value: FeatureFlagValuesDto & {
|
|
195
|
+
owner: string;
|
|
196
|
+
};
|
|
197
|
+
} | {
|
|
198
|
+
action: 'update';
|
|
199
|
+
id: string;
|
|
200
|
+
key: string;
|
|
201
|
+
expected: FeatureFlagValuesDto;
|
|
202
|
+
value: FeatureFlagValuesDto;
|
|
203
|
+
} | {
|
|
204
|
+
action: 'delete';
|
|
205
|
+
id: string;
|
|
206
|
+
key: string;
|
|
207
|
+
expected: FeatureFlagValuesDto;
|
|
208
|
+
};
|
|
209
|
+
export interface DeleteSsoConnectionPayloadDto {
|
|
210
|
+
strategy: 'waad' | 'samlp';
|
|
211
|
+
connectionId: string;
|
|
212
|
+
/** A copy, so a list shows a name that a person knows. Nothing reads it. */
|
|
213
|
+
connectionName?: string;
|
|
214
|
+
/**
|
|
215
|
+
* The organizations that the connection serves, as copies for the approver.
|
|
216
|
+
* The request is findable on the page of each one.
|
|
217
|
+
*/
|
|
218
|
+
orgIds?: string[];
|
|
219
|
+
}
|
|
220
|
+
export interface DeleteOrganizationPayloadDto {
|
|
221
|
+
orgId: string;
|
|
222
|
+
/**
|
|
223
|
+
* The name that the approvers read. Accounts refuses the deletion when the
|
|
224
|
+
* organization has another name when the action runs.
|
|
225
|
+
*/
|
|
226
|
+
orgName: string;
|
|
227
|
+
}
|
|
228
|
+
export interface DeleteUserPayloadDto {
|
|
229
|
+
userId: string;
|
|
230
|
+
/**
|
|
231
|
+
* The address that the approvers read. Accounts refuses the deletion when
|
|
232
|
+
* the user has another address when the action runs.
|
|
233
|
+
*/
|
|
234
|
+
userEmail: string;
|
|
235
|
+
}
|
|
236
|
+
export interface ChangeUserEmailPayloadDto {
|
|
237
|
+
userId: string;
|
|
238
|
+
/**
|
|
239
|
+
* The address that the person who asked READ. Accounts compares it with
|
|
240
|
+
* the address that the user has when the action runs, and changes nothing
|
|
241
|
+
* when the two differ.
|
|
242
|
+
*/
|
|
243
|
+
expectedEmail: string;
|
|
244
|
+
newEmail: string;
|
|
245
|
+
}
|
|
246
|
+
export interface PurgeUsersPayloadDto {
|
|
247
|
+
/**
|
|
248
|
+
* The users to delete, at most `PURGE_USERS_MAX_USERS`. The address is a
|
|
249
|
+
* copy for the approver, and accounts refuses a user whose address is not
|
|
250
|
+
* that copy. A list that came from a file of ids carries none.
|
|
251
|
+
*/
|
|
252
|
+
users: {
|
|
253
|
+
userId: string;
|
|
254
|
+
email?: string;
|
|
255
|
+
}[];
|
|
256
|
+
}
|
|
257
|
+
export interface UpdateUserMetadataPayloadDto {
|
|
258
|
+
userId: string;
|
|
259
|
+
/** A copy, so a list shows a person and not an Auth0 id. Nothing reads it. */
|
|
260
|
+
userEmail?: string;
|
|
261
|
+
/** `app` writes `app_metadata`, `user` writes `user_metadata`. */
|
|
262
|
+
kind: 'app' | 'user';
|
|
263
|
+
/**
|
|
264
|
+
* The metadata that the person who asked READ. Absent for "not set".
|
|
265
|
+
* Accounts writes nothing when it holds another value when the action runs.
|
|
266
|
+
*/
|
|
267
|
+
expected?: Record<string, unknown>;
|
|
268
|
+
metadata: Record<string, unknown>;
|
|
269
|
+
}
|
|
270
|
+
export interface DeleteInternalBackupPayloadDto {
|
|
271
|
+
clusterId: string;
|
|
272
|
+
/** A copy, so a list shows a name that a person knows. Nothing reads it. */
|
|
273
|
+
clusterName?: string;
|
|
274
|
+
backupName: string;
|
|
275
|
+
/** A copy of the Slack link of the incident, for the approver to read. */
|
|
276
|
+
incidentLink?: string;
|
|
277
|
+
}
|
|
124
278
|
export declare class ControlApprovalsService {
|
|
125
279
|
/**
|
|
126
280
|
* The registered privileged actions, with the rules that guard each one.
|
|
@@ -170,6 +324,128 @@ export declare class ControlApprovalsService {
|
|
|
170
324
|
approval: ApprovalJobDto;
|
|
171
325
|
created: boolean;
|
|
172
326
|
}>;
|
|
327
|
+
/**
|
|
328
|
+
* Opens a change-platform-roles request.
|
|
329
|
+
*
|
|
330
|
+
* Opening one needs `accounts:admin:changeRole`, the same permission that
|
|
331
|
+
* approving needs, and the reason is necessary. A 200 means that a request
|
|
332
|
+
* for the same user is already open.
|
|
333
|
+
*/
|
|
334
|
+
static createChangePlatformRoles(params: CreateApprovalRequest<ChangePlatformRolesPayloadDto>): Promise<{
|
|
335
|
+
approval: ApprovalJobDto;
|
|
336
|
+
created: boolean;
|
|
337
|
+
}>;
|
|
338
|
+
/**
|
|
339
|
+
* Opens a delete-internal-backup request.
|
|
340
|
+
*
|
|
341
|
+
* Opening one needs `console:clusters:canCreateInternalBackup`, the same
|
|
342
|
+
* permission that approving needs. A 200 means that a request for the same
|
|
343
|
+
* backup is already open.
|
|
344
|
+
*/
|
|
345
|
+
static createDeleteInternalBackup(params: CreateApprovalRequest<DeleteInternalBackupPayloadDto>): Promise<{
|
|
346
|
+
approval: ApprovalJobDto;
|
|
347
|
+
created: boolean;
|
|
348
|
+
}>;
|
|
349
|
+
/**
|
|
350
|
+
* Opens a change-org-limits request.
|
|
351
|
+
*
|
|
352
|
+
* Opening one needs `accounts:admin:mayChangeOrgLimits`, the same
|
|
353
|
+
* permission that approving needs. A 200 means that a request for the same
|
|
354
|
+
* limit of the same organization is already open.
|
|
355
|
+
*/
|
|
356
|
+
static createChangeOrgLimits(params: CreateApprovalRequest<ChangeOrgLimitsPayloadDto>): Promise<{
|
|
357
|
+
approval: ApprovalJobDto;
|
|
358
|
+
created: boolean;
|
|
359
|
+
}>;
|
|
360
|
+
/**
|
|
361
|
+
* Opens a delete-sso-connection request.
|
|
362
|
+
*
|
|
363
|
+
* Opening one needs `AccountsPermissions.ADMIN_AUTH0_CONNECTIONS_DELETE`, the same
|
|
364
|
+
* permission that approving needs. A 200 means that a request for the same
|
|
365
|
+
* connection is already open.
|
|
366
|
+
*/
|
|
367
|
+
static createDeleteSsoConnection(params: CreateApprovalRequest<DeleteSsoConnectionPayloadDto>): Promise<{
|
|
368
|
+
approval: ApprovalJobDto;
|
|
369
|
+
created: boolean;
|
|
370
|
+
}>;
|
|
371
|
+
/**
|
|
372
|
+
* Opens a change-sales-plan request.
|
|
373
|
+
*
|
|
374
|
+
* Opening one needs `AccountsPermissions.SALES_PLAN_SWITCH`, the same
|
|
375
|
+
* permission that approving needs. A 200 means that a request for the same
|
|
376
|
+
* organization is already open.
|
|
377
|
+
*/
|
|
378
|
+
static createChangeSalesPlan(params: CreateApprovalRequest<ChangeSalesPlanPayloadDto>): Promise<{
|
|
379
|
+
approval: ApprovalJobDto;
|
|
380
|
+
created: boolean;
|
|
381
|
+
}>;
|
|
382
|
+
/**
|
|
383
|
+
* Opens a delete-organization request.
|
|
384
|
+
*
|
|
385
|
+
* Opening one needs `AccountsPermissions.DELETE_ORG`, the same permission
|
|
386
|
+
* that approving needs. A 200 means that a request for the same
|
|
387
|
+
* organization is already open.
|
|
388
|
+
*/
|
|
389
|
+
static createDeleteOrganization(params: CreateApprovalRequest<DeleteOrganizationPayloadDto>): Promise<{
|
|
390
|
+
approval: ApprovalJobDto;
|
|
391
|
+
created: boolean;
|
|
392
|
+
}>;
|
|
393
|
+
/**
|
|
394
|
+
* Opens a delete-user request.
|
|
395
|
+
*
|
|
396
|
+
* Opening one needs `AccountsPermissions.DELETE_USER`, the same permission
|
|
397
|
+
* that approving needs. A 200 means that a request for the same user is
|
|
398
|
+
* already open.
|
|
399
|
+
*/
|
|
400
|
+
static createDeleteUser(params: CreateApprovalRequest<DeleteUserPayloadDto>): Promise<{
|
|
401
|
+
approval: ApprovalJobDto;
|
|
402
|
+
created: boolean;
|
|
403
|
+
}>;
|
|
404
|
+
/**
|
|
405
|
+
* Opens a change-user-email request.
|
|
406
|
+
*
|
|
407
|
+
* Opening one needs `AccountsPermissions.ADMIN_CHANGE_EMAIL`, the same
|
|
408
|
+
* permission that approving needs. A 200 means that a request for the same
|
|
409
|
+
* user is already open.
|
|
410
|
+
*/
|
|
411
|
+
static createChangeUserEmail(params: CreateApprovalRequest<ChangeUserEmailPayloadDto>): Promise<{
|
|
412
|
+
approval: ApprovalJobDto;
|
|
413
|
+
created: boolean;
|
|
414
|
+
}>;
|
|
415
|
+
/**
|
|
416
|
+
* Opens a purge-users request.
|
|
417
|
+
*
|
|
418
|
+
* Opening one needs `AccountsPermissions.GOD_PURGE_USERS` and
|
|
419
|
+
* `AccountsPermissions.DELETE_USER`, the same permissions that approving
|
|
420
|
+
* needs. A 200 means that a request for the same set of users is already
|
|
421
|
+
* open.
|
|
422
|
+
*/
|
|
423
|
+
static createPurgeUsers(params: CreateApprovalRequest<PurgeUsersPayloadDto>): Promise<{
|
|
424
|
+
approval: ApprovalJobDto;
|
|
425
|
+
created: boolean;
|
|
426
|
+
}>;
|
|
427
|
+
/**
|
|
428
|
+
* Opens an update-user-metadata request.
|
|
429
|
+
*
|
|
430
|
+
* Opening one needs `AccountsPermissions.GOD_UPDATE_USER_METADATA`, the
|
|
431
|
+
* same permission that approving needs. A 200 means that a request for the
|
|
432
|
+
* same user and the same kind of metadata is already open.
|
|
433
|
+
*/
|
|
434
|
+
static createUpdateUserMetadata(params: CreateApprovalRequest<UpdateUserMetadataPayloadDto>): Promise<{
|
|
435
|
+
approval: ApprovalJobDto;
|
|
436
|
+
created: boolean;
|
|
437
|
+
}>;
|
|
438
|
+
/**
|
|
439
|
+
* Opens a change-feature-flag request.
|
|
440
|
+
*
|
|
441
|
+
* Opening one needs `aafw`, the same permission that approving needs. A 200
|
|
442
|
+
* means that a request for the same flag is already open. On int this is
|
|
443
|
+
* the only way to change a flag.
|
|
444
|
+
*/
|
|
445
|
+
static createChangeFeatureFlag(params: CreateApprovalRequest<ChangeFeatureFlagPayloadDto>): Promise<{
|
|
446
|
+
approval: ApprovalJobDto;
|
|
447
|
+
created: boolean;
|
|
448
|
+
}>;
|
|
173
449
|
/** Adds the approval of the caller. The last one can run the action. */
|
|
174
450
|
static acknowledge(params: ApprovalIdRequest): Promise<ApprovalJobDto>;
|
|
175
451
|
/** Closes a request without running the action. The reason is necessary. */
|
|
@@ -158,6 +158,161 @@ class ControlApprovalsService {
|
|
|
158
158
|
`api/approvals/${useCase}`
|
|
159
159
|
);
|
|
160
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Opens a change-platform-roles request.
|
|
163
|
+
*
|
|
164
|
+
* Opening one needs `accounts:admin:changeRole`, the same permission that
|
|
165
|
+
* approving needs, and the reason is necessary. A 200 means that a request
|
|
166
|
+
* for the same user is already open.
|
|
167
|
+
*/
|
|
168
|
+
static async createChangePlatformRoles(params) {
|
|
169
|
+
return ControlApprovalsService.create(
|
|
170
|
+
params,
|
|
171
|
+
"createChangePlatformRoles",
|
|
172
|
+
"api/approvals/change-platform-roles"
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Opens a delete-internal-backup request.
|
|
177
|
+
*
|
|
178
|
+
* Opening one needs `console:clusters:canCreateInternalBackup`, the same
|
|
179
|
+
* permission that approving needs. A 200 means that a request for the same
|
|
180
|
+
* backup is already open.
|
|
181
|
+
*/
|
|
182
|
+
static async createDeleteInternalBackup(params) {
|
|
183
|
+
return ControlApprovalsService.create(
|
|
184
|
+
params,
|
|
185
|
+
"createDeleteInternalBackup",
|
|
186
|
+
"api/approvals/delete-internal-backup"
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Opens a change-org-limits request.
|
|
191
|
+
*
|
|
192
|
+
* Opening one needs `accounts:admin:mayChangeOrgLimits`, the same
|
|
193
|
+
* permission that approving needs. A 200 means that a request for the same
|
|
194
|
+
* limit of the same organization is already open.
|
|
195
|
+
*/
|
|
196
|
+
static async createChangeOrgLimits(params) {
|
|
197
|
+
return ControlApprovalsService.create(
|
|
198
|
+
params,
|
|
199
|
+
"createChangeOrgLimits",
|
|
200
|
+
"api/approvals/change-org-limits"
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Opens a delete-sso-connection request.
|
|
205
|
+
*
|
|
206
|
+
* Opening one needs `AccountsPermissions.ADMIN_AUTH0_CONNECTIONS_DELETE`, the same
|
|
207
|
+
* permission that approving needs. A 200 means that a request for the same
|
|
208
|
+
* connection is already open.
|
|
209
|
+
*/
|
|
210
|
+
static async createDeleteSsoConnection(params) {
|
|
211
|
+
return ControlApprovalsService.create(
|
|
212
|
+
params,
|
|
213
|
+
"createDeleteSsoConnection",
|
|
214
|
+
"api/approvals/delete-sso-connection"
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Opens a change-sales-plan request.
|
|
219
|
+
*
|
|
220
|
+
* Opening one needs `AccountsPermissions.SALES_PLAN_SWITCH`, the same
|
|
221
|
+
* permission that approving needs. A 200 means that a request for the same
|
|
222
|
+
* organization is already open.
|
|
223
|
+
*/
|
|
224
|
+
static async createChangeSalesPlan(params) {
|
|
225
|
+
return ControlApprovalsService.create(
|
|
226
|
+
params,
|
|
227
|
+
"createChangeSalesPlan",
|
|
228
|
+
"api/approvals/change-sales-plan"
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Opens a delete-organization request.
|
|
233
|
+
*
|
|
234
|
+
* Opening one needs `AccountsPermissions.DELETE_ORG`, the same permission
|
|
235
|
+
* that approving needs. A 200 means that a request for the same
|
|
236
|
+
* organization is already open.
|
|
237
|
+
*/
|
|
238
|
+
static async createDeleteOrganization(params) {
|
|
239
|
+
return ControlApprovalsService.create(
|
|
240
|
+
params,
|
|
241
|
+
"createDeleteOrganization",
|
|
242
|
+
"api/approvals/delete-organization"
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Opens a delete-user request.
|
|
247
|
+
*
|
|
248
|
+
* Opening one needs `AccountsPermissions.DELETE_USER`, the same permission
|
|
249
|
+
* that approving needs. A 200 means that a request for the same user is
|
|
250
|
+
* already open.
|
|
251
|
+
*/
|
|
252
|
+
static async createDeleteUser(params) {
|
|
253
|
+
return ControlApprovalsService.create(
|
|
254
|
+
params,
|
|
255
|
+
"createDeleteUser",
|
|
256
|
+
"api/approvals/delete-user"
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Opens a change-user-email request.
|
|
261
|
+
*
|
|
262
|
+
* Opening one needs `AccountsPermissions.ADMIN_CHANGE_EMAIL`, the same
|
|
263
|
+
* permission that approving needs. A 200 means that a request for the same
|
|
264
|
+
* user is already open.
|
|
265
|
+
*/
|
|
266
|
+
static async createChangeUserEmail(params) {
|
|
267
|
+
return ControlApprovalsService.create(
|
|
268
|
+
params,
|
|
269
|
+
"createChangeUserEmail",
|
|
270
|
+
"api/approvals/change-user-email"
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Opens a purge-users request.
|
|
275
|
+
*
|
|
276
|
+
* Opening one needs `AccountsPermissions.GOD_PURGE_USERS` and
|
|
277
|
+
* `AccountsPermissions.DELETE_USER`, the same permissions that approving
|
|
278
|
+
* needs. A 200 means that a request for the same set of users is already
|
|
279
|
+
* open.
|
|
280
|
+
*/
|
|
281
|
+
static async createPurgeUsers(params) {
|
|
282
|
+
return ControlApprovalsService.create(
|
|
283
|
+
params,
|
|
284
|
+
"createPurgeUsers",
|
|
285
|
+
"api/approvals/purge-users"
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Opens an update-user-metadata request.
|
|
290
|
+
*
|
|
291
|
+
* Opening one needs `AccountsPermissions.GOD_UPDATE_USER_METADATA`, the
|
|
292
|
+
* same permission that approving needs. A 200 means that a request for the
|
|
293
|
+
* same user and the same kind of metadata is already open.
|
|
294
|
+
*/
|
|
295
|
+
static async createUpdateUserMetadata(params) {
|
|
296
|
+
return ControlApprovalsService.create(
|
|
297
|
+
params,
|
|
298
|
+
"createUpdateUserMetadata",
|
|
299
|
+
"api/approvals/update-user-metadata"
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Opens a change-feature-flag request.
|
|
304
|
+
*
|
|
305
|
+
* Opening one needs `aafw`, the same permission that approving needs. A 200
|
|
306
|
+
* means that a request for the same flag is already open. On int this is
|
|
307
|
+
* the only way to change a flag.
|
|
308
|
+
*/
|
|
309
|
+
static async createChangeFeatureFlag(params) {
|
|
310
|
+
return ControlApprovalsService.create(
|
|
311
|
+
params,
|
|
312
|
+
"createChangeFeatureFlag",
|
|
313
|
+
"api/approvals/change-feature-flag"
|
|
314
|
+
);
|
|
315
|
+
}
|
|
161
316
|
/** Adds the approval of the caller. The last one can run the action. */
|
|
162
317
|
static async acknowledge(params) {
|
|
163
318
|
return ControlApprovalsService.readOne(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/services/control/control.approvals.service.ts"],
|
|
4
|
-
"sourcesContent": ["/*\n * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH\n * under one or more contributor license agreements. Licensed under a commercial license.\n * You may not use this file except in compliance with the commercial license.\n */\n\nimport type {\n ApprovalEntityType,\n ApprovalJobDto,\n ApprovalJobStatus,\n ApprovalSortField,\n ApprovalSortOrder,\n ApprovalStatusCountsDto,\n ApprovalUseCaseDto,\n ClusterBulkOperationPayloadDto,\n ClusterBulkOperationType,\n} from '@camunda/ccma-shared-types'\nimport { CLUSTER_BULK_OPERATION_RULES } from '@camunda/ccma-shared-types'\nimport type {\n RequestBaseParams,\n RequestBaseServiceContext,\n} from '../../types/general.types.js'\nimport { request } from '../../utils/api.js'\nimport { HttpError } from '../../utils/errors.js'\n\n/**\n * The four-eyes approval surface of control-backend.\n *\n * One service for the whole mechanism, and not one for each use case. The\n * generic routes (list, read, approve, reject, withdraw, run) carry no\n * knowledge of a use case, which is what lets one view in control-frontend\n * show every privileged action. Only the create calls are per use case,\n * because a payload can be validated only for the use case it belongs to.\n *\n * Every route answers 404 while the feature flag `control.approvals` is off on\n * the stage, and while the service reaches no stage at all.\n */\n\nconst BASE = { id: 'control' } as const\n\nexport interface ApprovalPageDto {\n approvals: ApprovalJobDto[]\n page: {\n limit: number\n offset: number\n /** True when the next page holds more. Never read the length instead. */\n hasMore: boolean\n }\n}\n\n/**\n * What narrows the queue. The list and the counts read the same fields.\n *\n * `entityType` and `entityId` are ONE filter, not two. control-backend answers\n * 400 for one without the other, so the type carries both or neither.\n */\nexport type ApprovalFacetParams = {\n type?: string\n /**\n * One organization, matched whole against `orgId` of the payload.\n *\n * It reads only the payload, so `entityType`/`entityId` is wider. That filter\n * reads the entity link, so it finds a request that touches the organization\n * through another field. The organization page of control-frontend asks the\n * entity-link filter, not this one. `orgId` is also not `q`: the free text\n * reads the whole payload as text.\n */\n orgId?: string\n /**\n * Free text. control-backend matches it in the reason, the person who\n * opened the request, the action and its label, and the payload that the\n * summary shows.\n */\n q?: string\n /**\n * The queue of one person: what they opened, or what waits for them.\n *\n * `actionable` is the rule of the Approve button asked in SQL \u2014 pending,\n * opened by somebody else, not approved by this person yet, and guarded by\n * permissions that they hold.\n */\n mine?: 'created' | 'actionable'\n} & (\n | {\n /**\n * The kind of entity a request must touch. Read with `entityId`.\n *\n * It reads the entity link of control-backend, so it is wider and truer\n * than `orgId`: a cluster-move request answers for its source\n * organization, its target organization AND the cluster. The organization\n * and cluster detail pages of control-frontend ask this.\n */\n entityType: ApprovalEntityType\n /** The id of the entity a request must touch. Read with `entityType`. */\n entityId: string\n }\n | { entityType?: never; entityId?: never }\n)\n\nexport interface ListApprovalsRequest extends RequestBaseParams {\n params?: ApprovalFacetParams & {\n status?: ApprovalJobStatus\n sort?: ApprovalSortField\n order?: ApprovalSortOrder\n limit?: number\n offset?: number\n }\n}\n\nexport interface CountApprovalsRequest extends RequestBaseParams {\n params?: ApprovalFacetParams\n}\n\nexport interface ApprovalIdRequest extends RequestBaseParams {\n params: { id: string }\n}\n\nexport interface RejectApprovalRequest extends RequestBaseParams {\n params: { id: string; reason: string }\n}\n\n/** A withdrawal says why, or says nothing. The reason is not demanded. */\nexport interface WithdrawApprovalRequest extends RequestBaseParams {\n params: { id: string; reason?: string }\n}\n\n/** The envelope that every create route of every use case shares. */\nexport interface CreateApprovalRequest<TPayload> extends RequestBaseParams {\n params: {\n payload: TPayload\n reason?: string\n /** More approvals than the floor of the use case. Never fewer. */\n requiredAcks?: number\n }\n}\n\nexport interface SendToExpiredTrialPayloadDto {\n orgId: string\n orgName?: string\n}\n\nexport interface ExtendTrialPayloadDto {\n orgId: string\n orgName?: string\n /**\n * Days of trial counted from the moment the action RUNS.\n *\n * Never a date. Accounts holds the new expiry inside a window that it\n * measures when the call arrives, and under approvals that is days after the\n * person chose the value. See `use-cases/extend-trial.ts` in\n * control-backend.\n */\n days: number\n}\n\nexport interface MoveClusterOrgPayloadDto {\n clusterId: string\n /** A copy, so a list shows a name that a person knows. Nothing reads it. */\n clusterName?: string\n targetOrgId: string\n /** A copy, for the same reason as `clusterName`. */\n targetOrgName?: string\n // The SOURCE organization is not here on purpose. control-backend resolves it\n // server-side from the cluster's current owner, so a caller cannot attach the\n // request to an organization the cluster never belonged to.\n}\n\n/**\n * The query string of a read, with the fields that carry no value dropped.\n *\n * An empty value is not the same as an absent one: `q=` would reach the schema\n * of control-backend, which holds the text at one character and answers 400.\n */\nfunction querySuffix(params: object = {}): string {\n const query = new URLSearchParams()\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== '') {\n query.set(key, String(value))\n }\n }\n return query.size > 0 ? `?${query.toString()}` : ''\n}\n\nfunction contextFor(\n name: string,\n url: string,\n normalizedUrl: string,\n type: 'get' | 'post',\n source: string,\n): RequestBaseServiceContext {\n return {\n method: {\n name: `ControlApprovalsService.${name}`,\n url,\n normalizedUrl,\n type,\n },\n source,\n target: { name: 'control', type: 'internal' },\n }\n}\n\nexport class ControlApprovalsService {\n /**\n * The registered privileged actions, with the rules that guard each one.\n *\n * A frontend reads this to run the same policy as the service, so it never\n * holds a copy of the registry. A new use case then needs no frontend\n * deploy.\n */\n public static async listUseCases(\n params: RequestBaseParams,\n ): Promise<ApprovalUseCaseDto[]> {\n const serviceContext = contextFor(\n 'listUseCases',\n 'api/approvals/use-cases',\n 'api/approvals/use-cases',\n 'get',\n params.config.metrics?.source || 'unset',\n )\n const response = await request<{ useCases: ApprovalUseCaseDto[] }>({\n config: params.config,\n base: BASE,\n url: serviceContext.method.url,\n method: 'get',\n metricsDetails: serviceContext,\n })\n if (!response.ok) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n return response.body!.useCases\n }\n\n /**\n * One page of the requests that the caller can read.\n *\n * It is not the whole table: control-backend hides a request whose use case\n * the caller cannot act on, unless the caller opened it.\n */\n public static async list(\n params: ListApprovalsRequest,\n ): Promise<ApprovalPageDto> {\n const suffix = querySuffix(params.params)\n const serviceContext = contextFor(\n 'list',\n `api/approvals${suffix}`,\n 'api/approvals',\n 'get',\n params.config.metrics?.source || 'unset',\n )\n const response = await request<ApprovalPageDto>({\n config: params.config,\n base: BASE,\n url: serviceContext.method.url,\n method: 'get',\n metricsDetails: serviceContext,\n })\n if (!response.ok) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n return response.body!\n }\n\n /**\n * How many requests each status holds, for the status filters of a queue.\n *\n * It is the same question as `list` without the status, so a filter that\n * shows a number never opens an empty page.\n */\n public static async counts(\n params: CountApprovalsRequest,\n ): Promise<ApprovalStatusCountsDto> {\n const suffix = querySuffix(params.params)\n const serviceContext = contextFor(\n 'counts',\n `api/approvals/counts${suffix}`,\n 'api/approvals/counts',\n 'get',\n params.config.metrics?.source || 'unset',\n )\n const response = await request<{ counts: ApprovalStatusCountsDto }>({\n config: params.config,\n base: BASE,\n url: serviceContext.method.url,\n method: 'get',\n metricsDetails: serviceContext,\n })\n if (!response.ok) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n return response.body!.counts\n }\n\n public static async get(params: ApprovalIdRequest): Promise<ApprovalJobDto> {\n return ControlApprovalsService.readOne(\n params,\n 'get',\n `api/approvals/${params.params.id}`,\n 'api/approvals/:id',\n 'get',\n )\n }\n\n /**\n * What a create route answers, plus whether it opened a NEW request.\n *\n * The route answers 201 for a fresh request and 200 for the one already\n * open for the same target \u2014 `create` deduplicates. A caller that must\n * tell the two apart (a floor-of-one use case, where 200 is the ordinary\n * path and not a failure) reads `created`.\n */\n public static async createMoveClusterOrg(\n params: CreateApprovalRequest<MoveClusterOrgPayloadDto>,\n ): Promise<{ approval: ApprovalJobDto; created: boolean }> {\n return ControlApprovalsService.create(\n params,\n 'createMoveClusterOrg',\n 'api/approvals/move-cluster-org',\n )\n }\n\n /**\n * Opens a cluster bulk operation of one type.\n *\n * Each type is its own use case with its own route, because each one has\n * its own permission. The floor of approvals is the same for every type,\n * and it depends on the stage: 2 on prod, 1 on dev, int and local. The\n * route is `api/approvals/<use case>`, as for every other use case. A 200\n * means that the same operation over the same clusters is already open.\n */\n public static async createClusterBulkOperation<\n T extends ClusterBulkOperationType,\n >(\n type: T,\n params: CreateApprovalRequest<ClusterBulkOperationPayloadDto<T>>,\n ): Promise<{ approval: ApprovalJobDto; created: boolean }> {\n const useCase = CLUSTER_BULK_OPERATION_RULES[type].useCase\n return ControlApprovalsService.create(\n params,\n 'createClusterBulkOperation',\n `api/approvals/${useCase}`,\n )\n }\n\n /** Adds the approval of the caller. The last one can run the action. */\n public static async acknowledge(\n params: ApprovalIdRequest,\n ): Promise<ApprovalJobDto> {\n return ControlApprovalsService.readOne(\n params,\n 'acknowledge',\n `api/approvals/${params.params.id}/ack`,\n 'api/approvals/:id/ack',\n 'post',\n )\n }\n\n /** Closes a request without running the action. The reason is necessary. */\n public static async reject(\n params: RejectApprovalRequest,\n ): Promise<ApprovalJobDto> {\n return ControlApprovalsService.readOne(\n params,\n 'reject',\n `api/approvals/${params.params.id}/reject`,\n 'api/approvals/:id/reject',\n 'post',\n { reason: params.params.reason },\n )\n }\n\n /**\n * Takes back a request. Only the person who opened it can.\n *\n * The reason is optional, and it lands in the same field that a rejection\n * writes. The creator cannot reject their own request, so this is where they\n * say why they closed it.\n */\n public static async withdraw(\n params: WithdrawApprovalRequest,\n ): Promise<ApprovalJobDto> {\n return ControlApprovalsService.readOne(\n params,\n 'withdraw',\n `api/approvals/${params.params.id}/withdraw`,\n 'api/approvals/:id/withdraw',\n 'post',\n params.params.reason ? { reason: params.params.reason } : undefined,\n )\n }\n\n /**\n * Runs a request that holds its approvals.\n *\n * Only a use case in the explicit mode needs this. An action that throws\n * leaves the request `failed`, and this call still answers 200 with it, so\n * read `status` and `executionError` of the answer.\n */\n public static async execute(\n params: ApprovalIdRequest,\n ): Promise<ApprovalJobDto> {\n return ControlApprovalsService.readOne(\n params,\n 'execute',\n `api/approvals/${params.params.id}/execute`,\n 'api/approvals/:id/execute',\n 'post',\n )\n }\n\n /** Opens a send-to-expired-trial request. Any signed-in Camundi can. */\n public static async createSendToExpiredTrial(\n params: CreateApprovalRequest<SendToExpiredTrialPayloadDto>,\n ): Promise<ApprovalJobDto> {\n const { approval } = await ControlApprovalsService.create(\n params,\n 'createSendToExpiredTrial',\n 'api/approvals/send-to-expired-trial',\n )\n return approval\n }\n\n /**\n * Opens an extend-trial request.\n *\n * Opening one needs `accounts:admin:extendTrial`, the same permission that\n * approving needs, and the reason is necessary.\n */\n public static async createExtendTrial(\n params: CreateApprovalRequest<ExtendTrialPayloadDto>,\n ): Promise<ApprovalJobDto> {\n const { approval } = await ControlApprovalsService.create(\n params,\n 'createExtendTrial',\n 'api/approvals/extend-trial',\n )\n return approval\n }\n\n /**\n * The shared half of every call that answers one request.\n *\n * Six routes answer `{ approval }` and differ only in the verb and the path.\n * One private helper keeps the metrics context, the status check and the\n * unwrapping in one place.\n */\n private static async readOne(\n params: RequestBaseParams,\n name: string,\n url: string,\n normalizedUrl: string,\n method: 'get' | 'post',\n body?: Record<string, unknown>,\n ): Promise<ApprovalJobDto> {\n const serviceContext = contextFor(\n name,\n url,\n normalizedUrl,\n method,\n params.config.metrics?.source || 'unset',\n )\n const response = await request<{ approval: ApprovalJobDto }>({\n config: params.config,\n base: BASE,\n url,\n method,\n metricsDetails: serviceContext,\n body,\n })\n if (!response.ok) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n return response.body!.approval\n }\n\n /**\n * The shared half of every create route. See `approvalRequestBody`.\n *\n * The route answers 201 for a fresh request and 200 for the one already\n * open for the same target (`create` deduplicates). `created` carries that\n * difference. `createSendToExpiredTrial` and `createExtendTrial` do not\n * need it, so they read `approval` alone off this result.\n */\n private static async create<TPayload>(\n params: CreateApprovalRequest<TPayload>,\n name: string,\n url: string,\n ): Promise<{ approval: ApprovalJobDto; created: boolean }> {\n const serviceContext = contextFor(\n name,\n url,\n url,\n 'post',\n params.config.metrics?.source || 'unset',\n )\n const response = await request<{ approval: ApprovalJobDto }>({\n config: params.config,\n base: BASE,\n url,\n method: 'post',\n metricsDetails: serviceContext,\n body: {\n payload: params.params.payload,\n reason: params.params.reason,\n requiredAcks: params.params.requiredAcks,\n },\n })\n if (!response.ok) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n return {\n approval: response.body!.approval,\n created: response.status === 201,\n }\n }\n}\n"],
|
|
5
|
-
"mappings": "AAiBA,SAAS,oCAAoC;AAK7C,SAAS,eAAe;AACxB,SAAS,iBAAiB;AAe1B,MAAM,OAAO,EAAE,IAAI,UAAU;
|
|
4
|
+
"sourcesContent": ["/*\n * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH\n * under one or more contributor license agreements. Licensed under a commercial license.\n * You may not use this file except in compliance with the commercial license.\n */\n\nimport type {\n ApprovalEntityType,\n ApprovalJobDto,\n ApprovalJobStatus,\n ApprovalSortField,\n ApprovalSortOrder,\n ApprovalStatusCountsDto,\n ApprovalUseCaseDto,\n ClusterBulkOperationPayloadDto,\n ClusterBulkOperationType,\n} from '@camunda/ccma-shared-types'\nimport { CLUSTER_BULK_OPERATION_RULES } from '@camunda/ccma-shared-types'\nimport type {\n RequestBaseParams,\n RequestBaseServiceContext,\n} from '../../types/general.types.js'\nimport { request } from '../../utils/api.js'\nimport { HttpError } from '../../utils/errors.js'\n\n/**\n * The four-eyes approval surface of control-backend.\n *\n * One service for the whole mechanism, and not one for each use case. The\n * generic routes (list, read, approve, reject, withdraw, run) carry no\n * knowledge of a use case, which is what lets one view in control-frontend\n * show every privileged action. Only the create calls are per use case,\n * because a payload can be validated only for the use case it belongs to.\n *\n * A use case that walks its own rollout behind a flag answers 404 on its\n * create route while that flag is off.\n */\n\nconst BASE = { id: 'control' } as const\n\nexport interface ApprovalPageDto {\n approvals: ApprovalJobDto[]\n page: {\n limit: number\n offset: number\n /** True when the next page holds more. Never read the length instead. */\n hasMore: boolean\n }\n}\n\n/**\n * What narrows the queue. The list and the counts read the same fields.\n *\n * `entityType` and `entityId` are ONE filter, not two. control-backend answers\n * 400 for one without the other, so the type carries both or neither.\n */\nexport type ApprovalFacetParams = {\n type?: string\n /**\n * One organization, matched whole against `orgId` of the payload.\n *\n * It reads only the payload, so `entityType`/`entityId` is wider. That filter\n * reads the entity link, so it finds a request that touches the organization\n * through another field. The organization page of control-frontend asks the\n * entity-link filter, not this one. `orgId` is also not `q`: the free text\n * reads the whole payload as text.\n */\n orgId?: string\n /**\n * Free text. control-backend matches it in the reason, the person who\n * opened the request, the action and its label, and the payload that the\n * summary shows.\n */\n q?: string\n /**\n * The queue of one person: what they opened, or what waits for them.\n *\n * `actionable` is the rule of the Approve button asked in SQL \u2014 pending,\n * opened by somebody else, not approved by this person yet, and guarded by\n * permissions that they hold.\n */\n mine?: 'created' | 'actionable'\n} & (\n | {\n /**\n * The kind of entity a request must touch. Read with `entityId`.\n *\n * It reads the entity link of control-backend, so it is wider and truer\n * than `orgId`: a cluster-move request answers for its source\n * organization, its target organization AND the cluster. The organization\n * and cluster detail pages of control-frontend ask this.\n */\n entityType: ApprovalEntityType\n /** The id of the entity a request must touch. Read with `entityType`. */\n entityId: string\n }\n | { entityType?: never; entityId?: never }\n)\n\nexport interface ListApprovalsRequest extends RequestBaseParams {\n params?: ApprovalFacetParams & {\n status?: ApprovalJobStatus\n sort?: ApprovalSortField\n order?: ApprovalSortOrder\n limit?: number\n offset?: number\n }\n}\n\nexport interface CountApprovalsRequest extends RequestBaseParams {\n params?: ApprovalFacetParams\n}\n\nexport interface ApprovalIdRequest extends RequestBaseParams {\n params: { id: string }\n}\n\nexport interface RejectApprovalRequest extends RequestBaseParams {\n params: { id: string; reason: string }\n}\n\n/** A withdrawal says why, or says nothing. The reason is not demanded. */\nexport interface WithdrawApprovalRequest extends RequestBaseParams {\n params: { id: string; reason?: string }\n}\n\n/** The envelope that every create route of every use case shares. */\nexport interface CreateApprovalRequest<TPayload> extends RequestBaseParams {\n params: {\n payload: TPayload\n reason?: string\n /** More approvals than the floor of the use case. Never fewer. */\n requiredAcks?: number\n }\n}\n\nexport interface SendToExpiredTrialPayloadDto {\n orgId: string\n orgName?: string\n}\n\nexport interface ExtendTrialPayloadDto {\n orgId: string\n orgName?: string\n /**\n * Days of trial counted from the moment the action RUNS.\n *\n * Never a date. Accounts holds the new expiry inside a window that it\n * measures when the call arrives, and under approvals that is days after the\n * person chose the value. See `use-cases/extend-trial.ts` in\n * control-backend.\n */\n days: number\n}\n\nexport interface MoveClusterOrgPayloadDto {\n clusterId: string\n /** A copy, so a list shows a name that a person knows. Nothing reads it. */\n clusterName?: string\n targetOrgId: string\n /** A copy, for the same reason as `clusterName`. */\n targetOrgName?: string\n // The SOURCE organization is not here on purpose. control-backend resolves it\n // server-side from the cluster's current owner, so a caller cannot attach the\n // request to an organization the cluster never belonged to.\n}\n\nexport interface ChangePlatformRolesPayloadDto {\n userId: string\n /** A copy, so a list shows a person and not an Auth0 id. Nothing reads it. */\n userEmail?: string\n /**\n * A CHANGE, and never the whole set. Accounts applies it over the roles that\n * the user holds when the action RUNS, so a role change that somebody else\n * made while the request waited stays.\n */\n rolesToAdd: string[]\n rolesToRemove: string[]\n}\n\nexport interface ChangeOrgLimitsPayloadDto {\n orgId: string\n /** A copy, so a list shows a name that a person knows. Nothing reads it. */\n orgName?: string\n /** One key of `allSalesPlanLimits()`. One limit for each request. */\n limitKey: string\n /**\n * The override that the person who asked READ. Absent for \"not set\".\n *\n * Accounts compares it with the override that it holds when the action\n * runs, and writes nothing when the two differ. See `staleApprovalKeys`.\n */\n expected?: string | number | boolean\n /** The new override. `null` clears it, and the default of the plan applies. */\n value: string | number | boolean | null\n}\n\nexport interface ChangeSalesPlanPayloadDto {\n orgId: string\n /** A copy, so a list shows a name that a person knows. Nothing reads it. */\n orgName?: string\n salesPlanId: string\n /** A copy of the name of the new plan, for the approver to read. */\n salesPlanName?: string\n /**\n * The plan that the person who asked READ. Absent for \"no plan\".\n *\n * Accounts compares it with the plan that it holds when the action runs,\n * and switches nothing when the two differ.\n */\n expectedSalesPlanId?: string\n /** A copy of the name of the current plan, for the approver to read. */\n expectedSalesPlanName?: string\n /** Skips the validation of the switch. The approver sees it. */\n force: boolean\n}\n\n/** Every value of a flag row that a person can change. */\nexport interface FeatureFlagValuesDto {\n description: string\n enabled: boolean\n orgIds: string[]\n emailDomains: string[]\n /** The team that owns the flag. A create needs it. An older row has none. */\n owner?: string\n /** `YYYY-MM-DD`, the day after which the flag is late. */\n expiresAt?: string\n}\n\n/**\n * One change of the feature flag table: the action, and the whole row that it\n * asks for.\n *\n * An edit and a delete carry the row that the person who asked READ, as\n * `expected`. The action writes nothing when the row changed in the meantime.\n * `key` of an edit and a delete is a copy for the approver, and the action\n * compares it too.\n */\nexport type ChangeFeatureFlagPayloadDto =\n | {\n action: 'create'\n key: string\n value: FeatureFlagValuesDto & { owner: string }\n }\n | {\n action: 'update'\n id: string\n key: string\n expected: FeatureFlagValuesDto\n value: FeatureFlagValuesDto\n }\n | {\n action: 'delete'\n id: string\n key: string\n expected: FeatureFlagValuesDto\n }\n\nexport interface DeleteSsoConnectionPayloadDto {\n strategy: 'waad' | 'samlp'\n connectionId: string\n /** A copy, so a list shows a name that a person knows. Nothing reads it. */\n connectionName?: string\n /**\n * The organizations that the connection serves, as copies for the approver.\n * The request is findable on the page of each one.\n */\n orgIds?: string[]\n}\n\nexport interface DeleteOrganizationPayloadDto {\n orgId: string\n /**\n * The name that the approvers read. Accounts refuses the deletion when the\n * organization has another name when the action runs.\n */\n orgName: string\n}\n\nexport interface DeleteUserPayloadDto {\n userId: string\n /**\n * The address that the approvers read. Accounts refuses the deletion when\n * the user has another address when the action runs.\n */\n userEmail: string\n}\n\nexport interface ChangeUserEmailPayloadDto {\n userId: string\n /**\n * The address that the person who asked READ. Accounts compares it with\n * the address that the user has when the action runs, and changes nothing\n * when the two differ.\n */\n expectedEmail: string\n newEmail: string\n}\n\nexport interface PurgeUsersPayloadDto {\n /**\n * The users to delete, at most `PURGE_USERS_MAX_USERS`. The address is a\n * copy for the approver, and accounts refuses a user whose address is not\n * that copy. A list that came from a file of ids carries none.\n */\n users: { userId: string; email?: string }[]\n}\n\nexport interface UpdateUserMetadataPayloadDto {\n userId: string\n /** A copy, so a list shows a person and not an Auth0 id. Nothing reads it. */\n userEmail?: string\n /** `app` writes `app_metadata`, `user` writes `user_metadata`. */\n kind: 'app' | 'user'\n /**\n * The metadata that the person who asked READ. Absent for \"not set\".\n * Accounts writes nothing when it holds another value when the action runs.\n */\n expected?: Record<string, unknown>\n metadata: Record<string, unknown>\n}\n\nexport interface DeleteInternalBackupPayloadDto {\n clusterId: string\n /** A copy, so a list shows a name that a person knows. Nothing reads it. */\n clusterName?: string\n backupName: string\n /** A copy of the Slack link of the incident, for the approver to read. */\n incidentLink?: string\n}\n\n/**\n * The query string of a read, with the fields that carry no value dropped.\n *\n * An empty value is not the same as an absent one: `q=` would reach the schema\n * of control-backend, which holds the text at one character and answers 400.\n */\nfunction querySuffix(params: object = {}): string {\n const query = new URLSearchParams()\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== '') {\n query.set(key, String(value))\n }\n }\n return query.size > 0 ? `?${query.toString()}` : ''\n}\n\nfunction contextFor(\n name: string,\n url: string,\n normalizedUrl: string,\n type: 'get' | 'post',\n source: string,\n): RequestBaseServiceContext {\n return {\n method: {\n name: `ControlApprovalsService.${name}`,\n url,\n normalizedUrl,\n type,\n },\n source,\n target: { name: 'control', type: 'internal' },\n }\n}\n\nexport class ControlApprovalsService {\n /**\n * The registered privileged actions, with the rules that guard each one.\n *\n * A frontend reads this to run the same policy as the service, so it never\n * holds a copy of the registry. A new use case then needs no frontend\n * deploy.\n */\n public static async listUseCases(\n params: RequestBaseParams,\n ): Promise<ApprovalUseCaseDto[]> {\n const serviceContext = contextFor(\n 'listUseCases',\n 'api/approvals/use-cases',\n 'api/approvals/use-cases',\n 'get',\n params.config.metrics?.source || 'unset',\n )\n const response = await request<{ useCases: ApprovalUseCaseDto[] }>({\n config: params.config,\n base: BASE,\n url: serviceContext.method.url,\n method: 'get',\n metricsDetails: serviceContext,\n })\n if (!response.ok) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n return response.body!.useCases\n }\n\n /**\n * One page of the requests that the caller can read.\n *\n * It is not the whole table: control-backend hides a request whose use case\n * the caller cannot act on, unless the caller opened it.\n */\n public static async list(\n params: ListApprovalsRequest,\n ): Promise<ApprovalPageDto> {\n const suffix = querySuffix(params.params)\n const serviceContext = contextFor(\n 'list',\n `api/approvals${suffix}`,\n 'api/approvals',\n 'get',\n params.config.metrics?.source || 'unset',\n )\n const response = await request<ApprovalPageDto>({\n config: params.config,\n base: BASE,\n url: serviceContext.method.url,\n method: 'get',\n metricsDetails: serviceContext,\n })\n if (!response.ok) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n return response.body!\n }\n\n /**\n * How many requests each status holds, for the status filters of a queue.\n *\n * It is the same question as `list` without the status, so a filter that\n * shows a number never opens an empty page.\n */\n public static async counts(\n params: CountApprovalsRequest,\n ): Promise<ApprovalStatusCountsDto> {\n const suffix = querySuffix(params.params)\n const serviceContext = contextFor(\n 'counts',\n `api/approvals/counts${suffix}`,\n 'api/approvals/counts',\n 'get',\n params.config.metrics?.source || 'unset',\n )\n const response = await request<{ counts: ApprovalStatusCountsDto }>({\n config: params.config,\n base: BASE,\n url: serviceContext.method.url,\n method: 'get',\n metricsDetails: serviceContext,\n })\n if (!response.ok) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n return response.body!.counts\n }\n\n public static async get(params: ApprovalIdRequest): Promise<ApprovalJobDto> {\n return ControlApprovalsService.readOne(\n params,\n 'get',\n `api/approvals/${params.params.id}`,\n 'api/approvals/:id',\n 'get',\n )\n }\n\n /**\n * What a create route answers, plus whether it opened a NEW request.\n *\n * The route answers 201 for a fresh request and 200 for the one already\n * open for the same target \u2014 `create` deduplicates. A caller that must\n * tell the two apart (a floor-of-one use case, where 200 is the ordinary\n * path and not a failure) reads `created`.\n */\n public static async createMoveClusterOrg(\n params: CreateApprovalRequest<MoveClusterOrgPayloadDto>,\n ): Promise<{ approval: ApprovalJobDto; created: boolean }> {\n return ControlApprovalsService.create(\n params,\n 'createMoveClusterOrg',\n 'api/approvals/move-cluster-org',\n )\n }\n\n /**\n * Opens a cluster bulk operation of one type.\n *\n * Each type is its own use case with its own route, because each one has\n * its own permission. The floor of approvals is the same for every type,\n * and it depends on the stage: 2 on prod, 1 on dev, int and local. The\n * route is `api/approvals/<use case>`, as for every other use case. A 200\n * means that the same operation over the same clusters is already open.\n */\n public static async createClusterBulkOperation<\n T extends ClusterBulkOperationType,\n >(\n type: T,\n params: CreateApprovalRequest<ClusterBulkOperationPayloadDto<T>>,\n ): Promise<{ approval: ApprovalJobDto; created: boolean }> {\n const useCase = CLUSTER_BULK_OPERATION_RULES[type].useCase\n return ControlApprovalsService.create(\n params,\n 'createClusterBulkOperation',\n `api/approvals/${useCase}`,\n )\n }\n\n /**\n * Opens a change-platform-roles request.\n *\n * Opening one needs `accounts:admin:changeRole`, the same permission that\n * approving needs, and the reason is necessary. A 200 means that a request\n * for the same user is already open.\n */\n public static async createChangePlatformRoles(\n params: CreateApprovalRequest<ChangePlatformRolesPayloadDto>,\n ): Promise<{ approval: ApprovalJobDto; created: boolean }> {\n return ControlApprovalsService.create(\n params,\n 'createChangePlatformRoles',\n 'api/approvals/change-platform-roles',\n )\n }\n\n /**\n * Opens a delete-internal-backup request.\n *\n * Opening one needs `console:clusters:canCreateInternalBackup`, the same\n * permission that approving needs. A 200 means that a request for the same\n * backup is already open.\n */\n public static async createDeleteInternalBackup(\n params: CreateApprovalRequest<DeleteInternalBackupPayloadDto>,\n ): Promise<{ approval: ApprovalJobDto; created: boolean }> {\n return ControlApprovalsService.create(\n params,\n 'createDeleteInternalBackup',\n 'api/approvals/delete-internal-backup',\n )\n }\n\n /**\n * Opens a change-org-limits request.\n *\n * Opening one needs `accounts:admin:mayChangeOrgLimits`, the same\n * permission that approving needs. A 200 means that a request for the same\n * limit of the same organization is already open.\n */\n public static async createChangeOrgLimits(\n params: CreateApprovalRequest<ChangeOrgLimitsPayloadDto>,\n ): Promise<{ approval: ApprovalJobDto; created: boolean }> {\n return ControlApprovalsService.create(\n params,\n 'createChangeOrgLimits',\n 'api/approvals/change-org-limits',\n )\n }\n\n /**\n * Opens a delete-sso-connection request.\n *\n * Opening one needs `AccountsPermissions.ADMIN_AUTH0_CONNECTIONS_DELETE`, the same\n * permission that approving needs. A 200 means that a request for the same\n * connection is already open.\n */\n public static async createDeleteSsoConnection(\n params: CreateApprovalRequest<DeleteSsoConnectionPayloadDto>,\n ): Promise<{ approval: ApprovalJobDto; created: boolean }> {\n return ControlApprovalsService.create(\n params,\n 'createDeleteSsoConnection',\n 'api/approvals/delete-sso-connection',\n )\n }\n\n /**\n * Opens a change-sales-plan request.\n *\n * Opening one needs `AccountsPermissions.SALES_PLAN_SWITCH`, the same\n * permission that approving needs. A 200 means that a request for the same\n * organization is already open.\n */\n public static async createChangeSalesPlan(\n params: CreateApprovalRequest<ChangeSalesPlanPayloadDto>,\n ): Promise<{ approval: ApprovalJobDto; created: boolean }> {\n return ControlApprovalsService.create(\n params,\n 'createChangeSalesPlan',\n 'api/approvals/change-sales-plan',\n )\n }\n\n /**\n * Opens a delete-organization request.\n *\n * Opening one needs `AccountsPermissions.DELETE_ORG`, the same permission\n * that approving needs. A 200 means that a request for the same\n * organization is already open.\n */\n public static async createDeleteOrganization(\n params: CreateApprovalRequest<DeleteOrganizationPayloadDto>,\n ): Promise<{ approval: ApprovalJobDto; created: boolean }> {\n return ControlApprovalsService.create(\n params,\n 'createDeleteOrganization',\n 'api/approvals/delete-organization',\n )\n }\n\n /**\n * Opens a delete-user request.\n *\n * Opening one needs `AccountsPermissions.DELETE_USER`, the same permission\n * that approving needs. A 200 means that a request for the same user is\n * already open.\n */\n public static async createDeleteUser(\n params: CreateApprovalRequest<DeleteUserPayloadDto>,\n ): Promise<{ approval: ApprovalJobDto; created: boolean }> {\n return ControlApprovalsService.create(\n params,\n 'createDeleteUser',\n 'api/approvals/delete-user',\n )\n }\n\n /**\n * Opens a change-user-email request.\n *\n * Opening one needs `AccountsPermissions.ADMIN_CHANGE_EMAIL`, the same\n * permission that approving needs. A 200 means that a request for the same\n * user is already open.\n */\n public static async createChangeUserEmail(\n params: CreateApprovalRequest<ChangeUserEmailPayloadDto>,\n ): Promise<{ approval: ApprovalJobDto; created: boolean }> {\n return ControlApprovalsService.create(\n params,\n 'createChangeUserEmail',\n 'api/approvals/change-user-email',\n )\n }\n\n /**\n * Opens a purge-users request.\n *\n * Opening one needs `AccountsPermissions.GOD_PURGE_USERS` and\n * `AccountsPermissions.DELETE_USER`, the same permissions that approving\n * needs. A 200 means that a request for the same set of users is already\n * open.\n */\n public static async createPurgeUsers(\n params: CreateApprovalRequest<PurgeUsersPayloadDto>,\n ): Promise<{ approval: ApprovalJobDto; created: boolean }> {\n return ControlApprovalsService.create(\n params,\n 'createPurgeUsers',\n 'api/approvals/purge-users',\n )\n }\n\n /**\n * Opens an update-user-metadata request.\n *\n * Opening one needs `AccountsPermissions.GOD_UPDATE_USER_METADATA`, the\n * same permission that approving needs. A 200 means that a request for the\n * same user and the same kind of metadata is already open.\n */\n public static async createUpdateUserMetadata(\n params: CreateApprovalRequest<UpdateUserMetadataPayloadDto>,\n ): Promise<{ approval: ApprovalJobDto; created: boolean }> {\n return ControlApprovalsService.create(\n params,\n 'createUpdateUserMetadata',\n 'api/approvals/update-user-metadata',\n )\n }\n\n /**\n * Opens a change-feature-flag request.\n *\n * Opening one needs `aafw`, the same permission that approving needs. A 200\n * means that a request for the same flag is already open. On int this is\n * the only way to change a flag.\n */\n public static async createChangeFeatureFlag(\n params: CreateApprovalRequest<ChangeFeatureFlagPayloadDto>,\n ): Promise<{ approval: ApprovalJobDto; created: boolean }> {\n return ControlApprovalsService.create(\n params,\n 'createChangeFeatureFlag',\n 'api/approvals/change-feature-flag',\n )\n }\n\n /** Adds the approval of the caller. The last one can run the action. */\n public static async acknowledge(\n params: ApprovalIdRequest,\n ): Promise<ApprovalJobDto> {\n return ControlApprovalsService.readOne(\n params,\n 'acknowledge',\n `api/approvals/${params.params.id}/ack`,\n 'api/approvals/:id/ack',\n 'post',\n )\n }\n\n /** Closes a request without running the action. The reason is necessary. */\n public static async reject(\n params: RejectApprovalRequest,\n ): Promise<ApprovalJobDto> {\n return ControlApprovalsService.readOne(\n params,\n 'reject',\n `api/approvals/${params.params.id}/reject`,\n 'api/approvals/:id/reject',\n 'post',\n { reason: params.params.reason },\n )\n }\n\n /**\n * Takes back a request. Only the person who opened it can.\n *\n * The reason is optional, and it lands in the same field that a rejection\n * writes. The creator cannot reject their own request, so this is where they\n * say why they closed it.\n */\n public static async withdraw(\n params: WithdrawApprovalRequest,\n ): Promise<ApprovalJobDto> {\n return ControlApprovalsService.readOne(\n params,\n 'withdraw',\n `api/approvals/${params.params.id}/withdraw`,\n 'api/approvals/:id/withdraw',\n 'post',\n params.params.reason ? { reason: params.params.reason } : undefined,\n )\n }\n\n /**\n * Runs a request that holds its approvals.\n *\n * Only a use case in the explicit mode needs this. An action that throws\n * leaves the request `failed`, and this call still answers 200 with it, so\n * read `status` and `executionError` of the answer.\n */\n public static async execute(\n params: ApprovalIdRequest,\n ): Promise<ApprovalJobDto> {\n return ControlApprovalsService.readOne(\n params,\n 'execute',\n `api/approvals/${params.params.id}/execute`,\n 'api/approvals/:id/execute',\n 'post',\n )\n }\n\n /** Opens a send-to-expired-trial request. Any signed-in Camundi can. */\n public static async createSendToExpiredTrial(\n params: CreateApprovalRequest<SendToExpiredTrialPayloadDto>,\n ): Promise<ApprovalJobDto> {\n const { approval } = await ControlApprovalsService.create(\n params,\n 'createSendToExpiredTrial',\n 'api/approvals/send-to-expired-trial',\n )\n return approval\n }\n\n /**\n * Opens an extend-trial request.\n *\n * Opening one needs `accounts:admin:extendTrial`, the same permission that\n * approving needs, and the reason is necessary.\n */\n public static async createExtendTrial(\n params: CreateApprovalRequest<ExtendTrialPayloadDto>,\n ): Promise<ApprovalJobDto> {\n const { approval } = await ControlApprovalsService.create(\n params,\n 'createExtendTrial',\n 'api/approvals/extend-trial',\n )\n return approval\n }\n\n /**\n * The shared half of every call that answers one request.\n *\n * Six routes answer `{ approval }` and differ only in the verb and the path.\n * One private helper keeps the metrics context, the status check and the\n * unwrapping in one place.\n */\n private static async readOne(\n params: RequestBaseParams,\n name: string,\n url: string,\n normalizedUrl: string,\n method: 'get' | 'post',\n body?: Record<string, unknown>,\n ): Promise<ApprovalJobDto> {\n const serviceContext = contextFor(\n name,\n url,\n normalizedUrl,\n method,\n params.config.metrics?.source || 'unset',\n )\n const response = await request<{ approval: ApprovalJobDto }>({\n config: params.config,\n base: BASE,\n url,\n method,\n metricsDetails: serviceContext,\n body,\n })\n if (!response.ok) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n return response.body!.approval\n }\n\n /**\n * The shared half of every create route. See `approvalRequestBody`.\n *\n * The route answers 201 for a fresh request and 200 for the one already\n * open for the same target (`create` deduplicates). `created` carries that\n * difference. `createSendToExpiredTrial` and `createExtendTrial` do not\n * need it, so they read `approval` alone off this result.\n */\n private static async create<TPayload>(\n params: CreateApprovalRequest<TPayload>,\n name: string,\n url: string,\n ): Promise<{ approval: ApprovalJobDto; created: boolean }> {\n const serviceContext = contextFor(\n name,\n url,\n url,\n 'post',\n params.config.metrics?.source || 'unset',\n )\n const response = await request<{ approval: ApprovalJobDto }>({\n config: params.config,\n base: BASE,\n url,\n method: 'post',\n metricsDetails: serviceContext,\n body: {\n payload: params.params.payload,\n reason: params.params.reason,\n requiredAcks: params.params.requiredAcks,\n },\n })\n if (!response.ok) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n return {\n approval: response.body!.approval,\n created: response.status === 201,\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { ChartQueryCheck, DashboardCatalogue, DashboardDefinition, DashboardDto, DashboardListDto, DashboardNewTile } from '@camunda/ccma-shared-types';
|
|
2
|
+
import type { RequestBaseParams } from '../../types/general.types.js';
|
|
3
|
+
export interface ListDashboardsRequest extends RequestBaseParams {
|
|
4
|
+
params?: {
|
|
5
|
+
archived?: boolean;
|
|
6
|
+
limit?: number;
|
|
7
|
+
offset?: number;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export interface DashboardIdRequest extends RequestBaseParams {
|
|
11
|
+
params: {
|
|
12
|
+
id: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export interface CreateDashboardRequest extends RequestBaseParams {
|
|
16
|
+
params: {
|
|
17
|
+
title: string;
|
|
18
|
+
definition?: DashboardDefinition;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export interface UpdateDashboardRequest extends RequestBaseParams {
|
|
22
|
+
params: {
|
|
23
|
+
id: string;
|
|
24
|
+
title: string;
|
|
25
|
+
definition: DashboardDefinition;
|
|
26
|
+
/**
|
|
27
|
+
* The `version` of the read that the edit started from. The backend
|
|
28
|
+
* answers 409 when the row moved on, so a stale tab never replaces a
|
|
29
|
+
* newer save.
|
|
30
|
+
*/
|
|
31
|
+
version: number;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export interface AddDashboardTileRequest extends RequestBaseParams {
|
|
35
|
+
params: {
|
|
36
|
+
id: string;
|
|
37
|
+
tile: DashboardNewTile;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export interface CheckChartQueryRequest extends RequestBaseParams {
|
|
41
|
+
params: {
|
|
42
|
+
query: string;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export declare class ControlDashboardsService {
|
|
46
|
+
/**
|
|
47
|
+
* What a dashboard definition can hold on this stage: the kinds of tiles,
|
|
48
|
+
* the cards, the chart metrics and the limits. The chart metrics are empty
|
|
49
|
+
* where `control.dashboardCharts` is off.
|
|
50
|
+
*/
|
|
51
|
+
static catalogue(params: RequestBaseParams): Promise<DashboardCatalogue>;
|
|
52
|
+
/**
|
|
53
|
+
* Checks a chart query and gives the spec of a chart tile. A query with a
|
|
54
|
+
* mistake is an answer with `valid: false`, and not a failure. Answers 404
|
|
55
|
+
* where `control.dashboardCharts` is off.
|
|
56
|
+
*/
|
|
57
|
+
static checkChartQuery(params: CheckChartQueryRequest): Promise<ChartQueryCheck>;
|
|
58
|
+
/** One page of the live or the archived dashboards, with no tiles. */
|
|
59
|
+
static list(params: ListDashboardsRequest): Promise<DashboardListDto>;
|
|
60
|
+
static get(params: DashboardIdRequest): Promise<DashboardDto>;
|
|
61
|
+
static create(params: CreateDashboardRequest): Promise<DashboardDto>;
|
|
62
|
+
/** Saves the title and the tiles. Answers 409 for a stale `version`. */
|
|
63
|
+
static update(params: UpdateDashboardRequest): Promise<DashboardDto>;
|
|
64
|
+
/**
|
|
65
|
+
* Adds a tile after the last tile: to the right of the last row when it
|
|
66
|
+
* fits there, else under every tile. It needs no version.
|
|
67
|
+
*/
|
|
68
|
+
static addTile(params: AddDashboardTileRequest): Promise<DashboardDto>;
|
|
69
|
+
static archive(params: DashboardIdRequest): Promise<DashboardDto>;
|
|
70
|
+
static restore(params: DashboardIdRequest): Promise<DashboardDto>;
|
|
71
|
+
/** Pins the dashboard for the caller alone. */
|
|
72
|
+
static pin(params: DashboardIdRequest): Promise<void>;
|
|
73
|
+
static unpin(params: DashboardIdRequest): Promise<void>;
|
|
74
|
+
private static action;
|
|
75
|
+
private static send;
|
|
76
|
+
}
|