@camunda/ccma-api 2.1.0 → 3.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.
Files changed (40) hide show
  1. package/dist/index.d.ts +12 -4
  2. package/dist/index.js +8 -0
  3. package/dist/index.js.map +2 -2
  4. package/dist/services/accounts/approvals.m2m.service.d.ts +33 -0
  5. package/dist/services/accounts/approvals.m2m.service.js +64 -0
  6. package/dist/services/accounts/approvals.m2m.service.js.map +7 -0
  7. package/dist/services/accounts/hdp.service.d.ts +62 -1
  8. package/dist/services/accounts/hdp.service.js +123 -0
  9. package/dist/services/accounts/hdp.service.js.map +2 -2
  10. package/dist/services/accounts/hdp.service.spec.d.ts +1 -0
  11. package/dist/services/accounts/orgs.service.d.ts +6 -2
  12. package/dist/services/accounts/orgs.service.js +6 -2
  13. package/dist/services/accounts/orgs.service.js.map +2 -2
  14. package/dist/services/console/console.admin.service.d.ts +25 -0
  15. package/dist/services/console/console.admin.service.js +40 -0
  16. package/dist/services/console/console.admin.service.js.map +2 -2
  17. package/dist/services/console/console.approvals.m2m.service.d.ts +47 -0
  18. package/dist/services/console/console.approvals.m2m.service.js +105 -0
  19. package/dist/services/console/console.approvals.m2m.service.js.map +7 -0
  20. package/dist/services/console/console.channel.service.d.ts +4 -2
  21. package/dist/services/console/console.channel.service.js.map +2 -2
  22. package/dist/services/console/console.cluster.service.d.ts +15 -0
  23. package/dist/services/console/console.cluster.service.js +40 -0
  24. package/dist/services/console/console.cluster.service.js.map +2 -2
  25. package/dist/services/console/console.cluster.v2.service.d.ts +11 -3
  26. package/dist/services/console/console.cluster.v2.service.js +37 -2
  27. package/dist/services/console/console.cluster.v2.service.js.map +2 -2
  28. package/dist/services/control/control.annotations.service.d.ts +149 -0
  29. package/dist/services/control/control.annotations.service.js +193 -0
  30. package/dist/services/control/control.annotations.service.js.map +7 -0
  31. package/dist/services/control/control.approvals.service.d.ts +118 -6
  32. package/dist/services/control/control.approvals.service.js +99 -13
  33. package/dist/services/control/control.approvals.service.js.map +2 -2
  34. package/dist/services/control/control.platform.service.d.ts +24 -0
  35. package/dist/services/control/control.platform.service.js +36 -0
  36. package/dist/services/control/control.platform.service.js.map +7 -0
  37. package/dist/services/external/external.accounts.service.d.ts +10 -1
  38. package/dist/services/external/external.accounts.service.js +39 -0
  39. package/dist/services/external/external.accounts.service.js.map +2 -2
  40. package/package.json +2 -2
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/services/console/console.cluster.v2.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 ClusterActivityV2Response,\n ClusterByClientV2Dto,\n ClusterDetailV2Dto,\n ClusterFacetsV2Dto,\n ClusterListItemV2Dto,\n ClusterParametersV2Dto,\n CursorPage,\n ListClustersV2Params,\n} 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'\nimport { toQueryString } from '../../utils/query.js'\n\nexport interface ListClustersV2Request extends RequestBaseParams {\n params?: ListClustersV2Params\n}\n\n/**\n * `orgIds` and `ids` can each be thousands of uuids (prefilter-clusters-by-orgs,\n * fleet query); a comma-list that long overflows the GET request line (~HTTP\n * 431/414). So `listClusters` and `exportCsv` are POSTs: each large list rides\n * in the JSON body, only when present, while every other (small) filter stays\n * in the query string, keeping the backend's existing query parsing.\n */\nfunction splitBodyLists(params: ListClustersV2Params | undefined): {\n query: string\n body: { orgIds?: string[]; ids?: string[] } | undefined\n} {\n const orgIds = params?.filter?.orgIds\n const ids = params?.filter?.ids\n const hasOrgIds = !!orgIds && orgIds.length > 0\n const hasIds = !!ids && ids.length > 0\n if (!hasOrgIds && !hasIds) {\n return { query: toQueryString(params), body: undefined }\n }\n const {\n orgIds: _omitOrgIds,\n ids: _omitIds,\n ...restFilter\n } = params?.filter ?? {}\n const body: { orgIds?: string[]; ids?: string[] } = {}\n if (hasOrgIds) {\n body.orgIds = orgIds\n }\n if (hasIds) {\n body.ids = ids\n }\n return {\n query: toQueryString({ ...params, filter: restFilter }),\n body,\n }\n}\n\nexport interface ListOrgClustersV2Request extends RequestBaseParams {\n params: { orgId: string } & ListClustersV2Params\n}\n\nexport interface GetClusterV2Request extends RequestBaseParams {\n params: { clusterId: string }\n}\n\nexport type GetClusterParametersV2Request = RequestBaseParams\n\n/**\n * Console-backend `/v2` read endpoints for the Control frontend.\n * Reads only \u2014 mutations stay on the existing v1 services (spec Q6).\n */\nexport class ConsoleClusterV2Service {\n public static async listClusters(\n params: ListClustersV2Request,\n ): Promise<CursorPage<ClusterListItemV2Dto>> {\n const { query, body } = splitBodyLists(params.params)\n const serviceContext: RequestBaseServiceContext = {\n method: {\n name: 'ConsoleClusterV2Service.listClusters',\n url: `v2/clusters${query}`,\n normalizedUrl: 'v2/clusters',\n type: 'post',\n },\n source: params.config.metrics?.source || 'unset',\n target: { name: 'console', type: 'internal' },\n }\n\n const response = await request<CursorPage<ClusterListItemV2Dto>>({\n ...params,\n base: { id: 'console' },\n url: serviceContext.method.url,\n method: serviceContext.method.type,\n metricsDetails: serviceContext,\n body,\n responseType: 'json',\n })\n\n if (!response.ok || !response.body) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n\n return response.body\n }\n\n public static async listOrgClusters(\n params: ListOrgClustersV2Request,\n ): Promise<CursorPage<ClusterListItemV2Dto>> {\n const { orgId, ...rest } = params.params\n const query = toQueryString(rest)\n const serviceContext: RequestBaseServiceContext = {\n method: {\n name: 'ConsoleClusterV2Service.listOrgClusters',\n url: `v2/orgs/${encodeURIComponent(orgId)}/clusters${query}`,\n normalizedUrl: 'v2/orgs/:orgId/clusters',\n type: 'get',\n },\n source: params.config.metrics?.source || 'unset',\n target: { name: 'console', type: 'internal' },\n }\n\n const response = await request<CursorPage<ClusterListItemV2Dto>>({\n ...params,\n base: { id: 'console' },\n url: serviceContext.method.url,\n method: serviceContext.method.type,\n metricsDetails: serviceContext,\n responseType: 'json',\n })\n\n if (!response.ok || !response.body) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n\n return response.body\n }\n\n public static async getCluster(\n params: GetClusterV2Request,\n ): Promise<ClusterDetailV2Dto> {\n const serviceContext: RequestBaseServiceContext = {\n method: {\n name: 'ConsoleClusterV2Service.getCluster',\n url: `v2/clusters/${encodeURIComponent(params.params.clusterId)}`,\n normalizedUrl: 'v2/clusters/:clusterId',\n type: 'get',\n },\n source: params.config.metrics?.source || 'unset',\n target: { name: 'console', type: 'internal' },\n }\n\n const response = await request<ClusterDetailV2Dto>({\n ...params,\n base: { id: 'console' },\n url: serviceContext.method.url,\n method: serviceContext.method.type,\n metricsDetails: serviceContext,\n responseType: 'json',\n })\n\n if (!response.ok || !response.body) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n\n return response.body\n }\n\n /** Aggregated distribution counts for the list page (GET /v2/clusters/facets). */\n public static async getFacets(\n params: RequestBaseParams,\n ): Promise<ClusterFacetsV2Dto> {\n const serviceContext: RequestBaseServiceContext = {\n method: {\n name: 'ConsoleClusterV2Service.getFacets',\n url: 'v2/clusters/facets',\n normalizedUrl: 'v2/clusters/facets',\n type: 'get',\n },\n source: params.config.metrics?.source || 'unset',\n target: { name: 'console', type: 'internal' },\n }\n\n const response = await request<ClusterFacetsV2Dto>({\n ...params,\n base: { id: 'console' },\n url: serviceContext.method.url,\n method: serviceContext.method.type,\n metricsDetails: serviceContext,\n responseType: 'json',\n })\n\n if (!response.ok || !response.body) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n\n return response.body\n }\n\n /**\n * Resolve Zeebe client-id fragments (min 6 chars) to cluster + owning org\n * (GET /v2/clusters/by-client). Backs the cross-backend org search.\n */\n public static async findByClient(\n params: { params: { q: string } } & RequestBaseParams,\n ): Promise<ClusterByClientV2Dto[]> {\n const serviceContext: RequestBaseServiceContext = {\n method: {\n name: 'ConsoleClusterV2Service.findByClient',\n url: `v2/clusters/by-client?q=${encodeURIComponent(params.params.q)}`,\n normalizedUrl: 'v2/clusters/by-client',\n type: 'get',\n },\n source: params.config.metrics?.source || 'unset',\n target: { name: 'console', type: 'internal' },\n }\n\n const response = await request<ClusterByClientV2Dto[]>({\n ...params,\n base: { id: 'console' },\n url: serviceContext.method.url,\n method: serviceContext.method.type,\n metricsDetails: serviceContext,\n responseType: 'json',\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\n return response.body ?? []\n }\n\n /**\n * Filtered cluster CSV export (POST /v2/clusters/export). Same filter params\n * as `listClusters` (`orgIds` and `ids` in the body, the rest in the query\n * string); the backend walks the cursor server-side and returns the whole\n * CSV as text.\n */\n public static async exportCsv(\n params: ListClustersV2Request,\n ): Promise<string> {\n const { query, body } = splitBodyLists(params.params)\n const serviceContext: RequestBaseServiceContext = {\n method: {\n name: 'ConsoleClusterV2Service.exportCsv',\n url: `v2/clusters/export${query}`,\n normalizedUrl: 'v2/clusters/export',\n type: 'post',\n },\n source: params.config.metrics?.source || 'unset',\n target: { name: 'console', type: 'internal' },\n }\n\n const response = await request<string>({\n ...params,\n base: { id: 'console' },\n url: serviceContext.method.url,\n method: serviceContext.method.type,\n metricsDetails: serviceContext,\n body,\n responseType: 'text',\n })\n\n if (!response.ok || response.body === undefined) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n\n return response.body\n }\n\n public static async getParameters(\n params: GetClusterParametersV2Request,\n ): Promise<ClusterParametersV2Dto> {\n const serviceContext: RequestBaseServiceContext = {\n method: {\n name: 'ConsoleClusterV2Service.getParameters',\n url: 'v2/clusters/parameters',\n normalizedUrl: 'v2/clusters/parameters',\n type: 'get',\n },\n source: params.config.metrics?.source || 'unset',\n target: { name: 'console', type: 'internal' },\n }\n\n const response = await request<ClusterParametersV2Dto>({\n ...params,\n base: { id: 'console' },\n url: serviceContext.method.url,\n method: serviceContext.method.type,\n metricsDetails: serviceContext,\n responseType: 'json',\n })\n\n if (!response.ok || !response.body) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n\n return response.body\n }\n\n /**\n * Fleet activity map with tile counts (`GET /v2/clusters/activity`). One\n * global read. The source is HDP, an external system, and the backend holds\n * the assembled response in Redis for one hour. Call this on a user action,\n * not on page load.\n */\n public static async getClusterActivity(\n params: RequestBaseParams,\n ): Promise<ClusterActivityV2Response> {\n const serviceContext: RequestBaseServiceContext = {\n method: {\n name: 'ConsoleClusterV2Service.getClusterActivity',\n url: 'v2/clusters/activity',\n normalizedUrl: 'v2/clusters/activity',\n type: 'get',\n },\n source: params.config.metrics?.source || 'unset',\n target: { name: 'console', type: 'internal' },\n }\n\n const response = await request<ClusterActivityV2Response>({\n ...params,\n base: { id: 'console' },\n responseType: 'json',\n url: serviceContext.method.url,\n method: serviceContext.method.type,\n metricsDetails: serviceContext,\n })\n if (!response.ok || !response.body) {\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"],
5
- "mappings": "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",
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 ClusterActivityV2Response,\n ClusterByClientV2Dto,\n ClusterDetailV2Dto,\n ClusterFacetsV2Dto,\n ClusterListItemV2Dto,\n ClusterLiveStateV2Dto,\n ClusterParametersV2Dto,\n CursorPage,\n ListClustersV2Params,\n} 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'\nimport { toQueryString } from '../../utils/query.js'\n\nexport interface ListClustersV2Request extends RequestBaseParams {\n params?: ListClustersV2Params\n}\n\n/**\n * `orgIds` and `ids` can each be thousands of uuids (prefilter-clusters-by-orgs,\n * fleet query); a comma-list that long overflows the GET request line (~HTTP\n * 431/414). So `listClusters` and `exportCsv` are POSTs: each large list rides\n * in the JSON body, only when present, while every other (small) filter stays\n * in the query string, keeping the backend's existing query parsing.\n */\nfunction splitBodyLists(params: ListClustersV2Params | undefined): {\n query: string\n body: { orgIds?: string[]; ids?: string[] } | undefined\n} {\n const orgIds = params?.filter?.orgIds\n const ids = params?.filter?.ids\n const hasOrgIds = !!orgIds && orgIds.length > 0\n const hasIds = !!ids && ids.length > 0\n if (!hasOrgIds && !hasIds) {\n return { query: toQueryString(params), body: undefined }\n }\n const {\n orgIds: _omitOrgIds,\n ids: _omitIds,\n ...restFilter\n } = params?.filter ?? {}\n const body: { orgIds?: string[]; ids?: string[] } = {}\n if (hasOrgIds) {\n body.orgIds = orgIds\n }\n if (hasIds) {\n body.ids = ids\n }\n return {\n query: toQueryString({ ...params, filter: restFilter }),\n body,\n }\n}\n\nexport interface ListOrgClustersV2Request extends RequestBaseParams {\n params: { orgId: string } & ListClustersV2Params\n}\n\nexport interface GetClusterV2Request extends RequestBaseParams {\n params: { clusterId: string }\n}\n\nexport type GetClusterParametersV2Request = RequestBaseParams\n\n/**\n * Console-backend `/v2` read endpoints for the Control frontend.\n * Reads only \u2014 mutations stay on the existing v1 services (spec Q6).\n */\nexport class ConsoleClusterV2Service {\n public static async listClusters(\n params: ListClustersV2Request,\n ): Promise<CursorPage<ClusterListItemV2Dto>> {\n const { query, body } = splitBodyLists(params.params)\n const serviceContext: RequestBaseServiceContext = {\n method: {\n name: 'ConsoleClusterV2Service.listClusters',\n url: `v2/clusters${query}`,\n normalizedUrl: 'v2/clusters',\n type: 'post',\n },\n source: params.config.metrics?.source || 'unset',\n target: { name: 'console', type: 'internal' },\n }\n\n const response = await request<CursorPage<ClusterListItemV2Dto>>({\n ...params,\n base: { id: 'console' },\n url: serviceContext.method.url,\n method: serviceContext.method.type,\n metricsDetails: serviceContext,\n body,\n responseType: 'json',\n })\n\n if (!response.ok || !response.body) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n\n return response.body\n }\n\n public static async listOrgClusters(\n params: ListOrgClustersV2Request,\n ): Promise<CursorPage<ClusterListItemV2Dto>> {\n const { orgId, ...rest } = params.params\n const query = toQueryString(rest)\n const serviceContext: RequestBaseServiceContext = {\n method: {\n name: 'ConsoleClusterV2Service.listOrgClusters',\n url: `v2/orgs/${encodeURIComponent(orgId)}/clusters${query}`,\n normalizedUrl: 'v2/orgs/:orgId/clusters',\n type: 'get',\n },\n source: params.config.metrics?.source || 'unset',\n target: { name: 'console', type: 'internal' },\n }\n\n const response = await request<CursorPage<ClusterListItemV2Dto>>({\n ...params,\n base: { id: 'console' },\n url: serviceContext.method.url,\n method: serviceContext.method.type,\n metricsDetails: serviceContext,\n responseType: 'json',\n })\n\n if (!response.ok || !response.body) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n\n return response.body\n }\n\n public static async getCluster(\n params: GetClusterV2Request,\n ): Promise<ClusterDetailV2Dto> {\n const serviceContext: RequestBaseServiceContext = {\n method: {\n name: 'ConsoleClusterV2Service.getCluster',\n url: `v2/clusters/${encodeURIComponent(params.params.clusterId)}`,\n normalizedUrl: 'v2/clusters/:clusterId',\n type: 'get',\n },\n source: params.config.metrics?.source || 'unset',\n target: { name: 'console', type: 'internal' },\n }\n\n const response = await request<ClusterDetailV2Dto>({\n ...params,\n base: { id: 'console' },\n url: serviceContext.method.url,\n method: serviceContext.method.type,\n metricsDetails: serviceContext,\n responseType: 'json',\n })\n\n if (!response.ok || !response.body) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n\n return response.body\n }\n\n /** Aggregated distribution counts for the list page (GET /v2/clusters/facets). */\n public static async getFacets(\n params: RequestBaseParams,\n ): Promise<ClusterFacetsV2Dto> {\n const serviceContext: RequestBaseServiceContext = {\n method: {\n name: 'ConsoleClusterV2Service.getFacets',\n url: 'v2/clusters/facets',\n normalizedUrl: 'v2/clusters/facets',\n type: 'get',\n },\n source: params.config.metrics?.source || 'unset',\n target: { name: 'console', type: 'internal' },\n }\n\n const response = await request<ClusterFacetsV2Dto>({\n ...params,\n base: { id: 'console' },\n url: serviceContext.method.url,\n method: serviceContext.method.type,\n metricsDetails: serviceContext,\n responseType: 'json',\n })\n\n if (!response.ok || !response.body) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n\n return response.body\n }\n\n /**\n * Resolve Zeebe client-id fragments (min 6 chars) to cluster + owning org\n * (GET /v2/clusters/by-client). Backs the cross-backend org search.\n */\n public static async findByClient(\n params: { params: { q: string } } & RequestBaseParams,\n ): Promise<ClusterByClientV2Dto[]> {\n const serviceContext: RequestBaseServiceContext = {\n method: {\n name: 'ConsoleClusterV2Service.findByClient',\n url: `v2/clusters/by-client?q=${encodeURIComponent(params.params.q)}`,\n normalizedUrl: 'v2/clusters/by-client',\n type: 'get',\n },\n source: params.config.metrics?.source || 'unset',\n target: { name: 'console', type: 'internal' },\n }\n\n const response = await request<ClusterByClientV2Dto[]>({\n ...params,\n base: { id: 'console' },\n url: serviceContext.method.url,\n method: serviceContext.method.type,\n metricsDetails: serviceContext,\n responseType: 'json',\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\n return response.body ?? []\n }\n\n /**\n * Filtered cluster CSV export (POST /v2/clusters/export). Same filter params\n * as `listClusters` (`orgIds` and `ids` in the body, the rest in the query\n * string); the backend walks the cursor server-side and returns the whole\n * CSV as text.\n */\n public static async exportCsv(\n params: ListClustersV2Request,\n ): Promise<string> {\n const { query, body } = splitBodyLists(params.params)\n const serviceContext: RequestBaseServiceContext = {\n method: {\n name: 'ConsoleClusterV2Service.exportCsv',\n url: `v2/clusters/export${query}`,\n normalizedUrl: 'v2/clusters/export',\n type: 'post',\n },\n source: params.config.metrics?.source || 'unset',\n target: { name: 'console', type: 'internal' },\n }\n\n const response = await request<string>({\n ...params,\n base: { id: 'console' },\n url: serviceContext.method.url,\n method: serviceContext.method.type,\n metricsDetails: serviceContext,\n body,\n responseType: 'text',\n })\n\n if (!response.ok || response.body === undefined) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n\n return response.body\n }\n\n public static async getParameters(\n params: GetClusterParametersV2Request,\n ): Promise<ClusterParametersV2Dto> {\n const serviceContext: RequestBaseServiceContext = {\n method: {\n name: 'ConsoleClusterV2Service.getParameters',\n url: 'v2/clusters/parameters',\n normalizedUrl: 'v2/clusters/parameters',\n type: 'get',\n },\n source: params.config.metrics?.source || 'unset',\n target: { name: 'console', type: 'internal' },\n }\n\n const response = await request<ClusterParametersV2Dto>({\n ...params,\n base: { id: 'console' },\n url: serviceContext.method.url,\n method: serviceContext.method.type,\n metricsDetails: serviceContext,\n responseType: 'json',\n })\n\n if (!response.ok || !response.body) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n\n return response.body\n }\n\n /**\n * The fleet from the watcher memory of console-backend\n * (`GET /v2/clusters/live-state`). The route reads no database, thus a\n * client can poll it every second. The route sends `Cache-Control:\n * no-cache` and an ETag, thus the browser revalidates and gets a 304 when\n * the fleet did not change.\n */\n public static async getClusterLiveState(\n params: RequestBaseParams,\n ): Promise<ClusterLiveStateV2Dto> {\n const serviceContext: RequestBaseServiceContext = {\n method: {\n name: 'ConsoleClusterV2Service.getClusterLiveState',\n url: 'v2/clusters/live-state',\n normalizedUrl: 'v2/clusters/live-state',\n type: 'get',\n },\n source: params.config.metrics?.source || 'unset',\n target: { name: 'console', type: 'internal' },\n }\n\n const response = await request<ClusterLiveStateV2Dto>({\n ...params,\n base: { id: 'console' },\n url: serviceContext.method.url,\n method: serviceContext.method.type,\n metricsDetails: serviceContext,\n responseType: 'json',\n })\n\n if (!response.ok || !response.body) {\n throw new HttpError(\n `${serviceContext.method.name}: ${response.status} ${response.statusText}`,\n response.status,\n response.statusText,\n )\n }\n\n return response.body\n }\n\n /**\n * Fleet activity map with tile counts (`GET /v2/clusters/activity`). One\n * global read. The source is HDP, an external system, and the backend holds\n * the assembled response in Redis for one hour, thus a page can call this on\n * load.\n */\n public static async getClusterActivity(\n params: RequestBaseParams,\n ): Promise<ClusterActivityV2Response> {\n const serviceContext: RequestBaseServiceContext = {\n method: {\n name: 'ConsoleClusterV2Service.getClusterActivity',\n url: 'v2/clusters/activity',\n normalizedUrl: 'v2/clusters/activity',\n type: 'get',\n },\n source: params.config.metrics?.source || 'unset',\n target: { name: 'console', type: 'internal' },\n }\n\n const response = await request<ClusterActivityV2Response>({\n ...params,\n base: { id: 'console' },\n responseType: 'json',\n url: serviceContext.method.url,\n method: serviceContext.method.type,\n metricsDetails: serviceContext,\n })\n if (!response.ok || !response.body) {\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"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,149 @@
1
+ import type { AnnotationDto, AnnotationEntityKind, AnnotationKind, AnnotationLookupDto, AnnotationTagDto, EntityAnnotationsDto } from '@camunda/ccma-shared-types';
2
+ import type { RequestBaseParams } from '../../types/general.types.js';
3
+ /** What names the entity. Every write and the entity read carry it. */
4
+ export interface AnnotationTargetDto {
5
+ entityKind: AnnotationEntityKind;
6
+ entityId: string;
7
+ /**
8
+ * What the entity is called now. The backend stores it as a COPY, so a
9
+ * search result still reads after the entity is deleted. Send it when the
10
+ * caller knows the name.
11
+ */
12
+ entityLabel?: string;
13
+ }
14
+ export interface GetEntityAnnotationsRequest extends RequestBaseParams {
15
+ params: {
16
+ entityKind: AnnotationEntityKind;
17
+ entityId: string;
18
+ };
19
+ }
20
+ export interface LookupAnnotationsRequest extends RequestBaseParams {
21
+ params: {
22
+ entityKind: AnnotationEntityKind;
23
+ entityIds: string[];
24
+ };
25
+ }
26
+ export interface AddAnnotationNoteRequest extends RequestBaseParams {
27
+ params: AnnotationTargetDto & {
28
+ body: string;
29
+ };
30
+ }
31
+ export interface EditAnnotationNoteRequest extends RequestBaseParams {
32
+ params: {
33
+ id: string;
34
+ body: string;
35
+ /**
36
+ * The `version` of the note that the caller read.
37
+ *
38
+ * The backend writes only while the row still carries it, and answers 409
39
+ * when it does not. Send what came with the note you are editing, never a
40
+ * number of your own: it is what stops a stale tab from replacing an edit
41
+ * that landed after it was opened.
42
+ */
43
+ version: number;
44
+ };
45
+ }
46
+ export interface AnnotationIdRequest extends RequestBaseParams {
47
+ params: {
48
+ id: string;
49
+ };
50
+ }
51
+ export interface AddAnnotationTagRequest extends RequestBaseParams {
52
+ params: AnnotationTargetDto & {
53
+ tag: string;
54
+ };
55
+ }
56
+ export interface ListAnnotationTagsRequest extends RequestBaseParams {
57
+ params?: {
58
+ q?: string;
59
+ limit?: number;
60
+ offset?: number;
61
+ };
62
+ }
63
+ export interface SearchAnnotationsRequest extends RequestBaseParams {
64
+ params?: {
65
+ kind?: AnnotationKind;
66
+ entityKind?: AnnotationEntityKind;
67
+ tag?: string;
68
+ q?: string;
69
+ limit?: number;
70
+ offset?: number;
71
+ };
72
+ }
73
+ /** One page of a read, and whether the table holds more of it. */
74
+ export interface AnnotationPageInfoDto {
75
+ limit: number;
76
+ offset: number;
77
+ /** True when the next page holds more. Never read the length instead. */
78
+ hasMore: boolean;
79
+ }
80
+ export interface AnnotationPageDto {
81
+ annotations: AnnotationDto[];
82
+ page: AnnotationPageInfoDto;
83
+ }
84
+ export interface AnnotationTagPageDto {
85
+ tags: AnnotationTagDto[];
86
+ page: AnnotationPageInfoDto;
87
+ }
88
+ export declare class ControlAnnotationsService {
89
+ /**
90
+ * Every live note and tag of one entity.
91
+ *
92
+ * An entity that carries nothing answers with two empty lists, and never
93
+ * 404. The call also answers for an entity that the fleet no longer holds,
94
+ * which is what keeps a link out of a search result working.
95
+ */
96
+ static forEntity(params: GetEntityAnnotationsRequest): Promise<EntityAnnotationsDto>;
97
+ /**
98
+ * The tags, the note count and the newest note of many entities.
99
+ *
100
+ * A POST, because thousands of ids do not fit a request line. The answer
101
+ * holds only the entities that carry something.
102
+ */
103
+ static lookup(params: LookupAnnotationsRequest): Promise<AnnotationLookupDto>;
104
+ /** Writes a note on an entity. Any signed-in Camundi can. */
105
+ static addNote(params: AddAnnotationNoteRequest): Promise<AnnotationDto>;
106
+ /** Changes the text of a note. Only the person who wrote it can. */
107
+ static editNote(params: EditAnnotationNoteRequest): Promise<AnnotationDto>;
108
+ /**
109
+ * Takes a note or a tag off an entity.
110
+ *
111
+ * The row stays: the removal is a soft delete, so who wrote it and who took
112
+ * it off both stay readable. A note can only be removed by its author; a tag
113
+ * can be removed by anybody.
114
+ */
115
+ static remove(params: AnnotationIdRequest): Promise<void>;
116
+ /**
117
+ * Attaches a tag to an entity, and adds it to the dictionary when it is new.
118
+ *
119
+ * The name is normalized by the backend, so `Partner` and `partner` reach
120
+ * one tag. A second attach of one tag on one entity answers 409.
121
+ */
122
+ static addTag(params: AddAnnotationTagRequest): Promise<AnnotationDto>;
123
+ /**
124
+ * The tag dictionary, with the live entities of each tag counted, most used
125
+ * first.
126
+ *
127
+ * The autocomplete of the tag field reads this. It is what stops one idea
128
+ * from becoming three spellings.
129
+ *
130
+ * It answers one PAGE, because the dictionary only grows. Read `page.hasMore`
131
+ * to learn whether another follows.
132
+ */
133
+ static listTags(params: ListAnnotationTagsRequest): Promise<AnnotationTagPageDto>;
134
+ /**
135
+ * One page of the annotations that match, newest first.
136
+ *
137
+ * The answer holds annotations of entities that no longer exist, which is
138
+ * the point: the note about a deleted cluster is often the only record of
139
+ * why it was there.
140
+ */
141
+ static search(params: SearchAnnotationsRequest): Promise<AnnotationPageDto>;
142
+ /**
143
+ * The shared half of every call above.
144
+ *
145
+ * Seven routes differ only in the verb, the path and the body. One private
146
+ * helper keeps the metrics context and the status check in one place.
147
+ */
148
+ private static send;
149
+ }
@@ -0,0 +1,193 @@
1
+ import { request } from "../../utils/api.js";
2
+ import { HttpError } from "../../utils/errors.js";
3
+ const BASE = { id: "control" };
4
+ function contextFor(name, url, normalizedUrl, type, source) {
5
+ return {
6
+ method: {
7
+ name: `ControlAnnotationsService.${name}`,
8
+ url,
9
+ normalizedUrl,
10
+ type
11
+ },
12
+ source,
13
+ target: { name: "control", type: "internal" }
14
+ };
15
+ }
16
+ function queryOf(params = {}) {
17
+ const query = new URLSearchParams();
18
+ for (const [key, value] of Object.entries(params)) {
19
+ if (value !== void 0) {
20
+ query.set(key, String(value));
21
+ }
22
+ }
23
+ return query.size > 0 ? `?${query.toString()}` : "";
24
+ }
25
+ class ControlAnnotationsService {
26
+ /**
27
+ * Every live note and tag of one entity.
28
+ *
29
+ * An entity that carries nothing answers with two empty lists, and never
30
+ * 404. The call also answers for an entity that the fleet no longer holds,
31
+ * which is what keeps a link out of a search result working.
32
+ */
33
+ static async forEntity(params) {
34
+ const url = `api/annotations${queryOf(params.params)}`;
35
+ return ControlAnnotationsService.send(
36
+ params,
37
+ "forEntity",
38
+ url,
39
+ "api/annotations",
40
+ "get"
41
+ );
42
+ }
43
+ /**
44
+ * The tags, the note count and the newest note of many entities.
45
+ *
46
+ * A POST, because thousands of ids do not fit a request line. The answer
47
+ * holds only the entities that carry something.
48
+ */
49
+ static async lookup(params) {
50
+ return ControlAnnotationsService.send(
51
+ params,
52
+ "lookup",
53
+ "api/annotations/lookup",
54
+ "api/annotations/lookup",
55
+ "post",
56
+ params.params
57
+ );
58
+ }
59
+ /** Writes a note on an entity. Any signed-in Camundi can. */
60
+ static async addNote(params) {
61
+ const { annotation } = await ControlAnnotationsService.send(
62
+ params,
63
+ "addNote",
64
+ "api/annotations/notes",
65
+ "api/annotations/notes",
66
+ "post",
67
+ params.params
68
+ );
69
+ return annotation;
70
+ }
71
+ /** Changes the text of a note. Only the person who wrote it can. */
72
+ static async editNote(params) {
73
+ const url = `api/annotations/notes/${params.params.id}`;
74
+ const { annotation } = await ControlAnnotationsService.send(params, "editNote", url, "api/annotations/notes/:id", "patch", {
75
+ body: params.params.body,
76
+ version: params.params.version
77
+ });
78
+ return annotation;
79
+ }
80
+ /**
81
+ * Takes a note or a tag off an entity.
82
+ *
83
+ * The row stays: the removal is a soft delete, so who wrote it and who took
84
+ * it off both stay readable. A note can only be removed by its author; a tag
85
+ * can be removed by anybody.
86
+ */
87
+ static async remove(params) {
88
+ const url = `api/annotations/${params.params.id}`;
89
+ await ControlAnnotationsService.send(
90
+ params,
91
+ "remove",
92
+ url,
93
+ "api/annotations/:id",
94
+ "delete"
95
+ );
96
+ }
97
+ /**
98
+ * Attaches a tag to an entity, and adds it to the dictionary when it is new.
99
+ *
100
+ * The name is normalized by the backend, so `Partner` and `partner` reach
101
+ * one tag. A second attach of one tag on one entity answers 409.
102
+ */
103
+ static async addTag(params) {
104
+ const { annotation } = await ControlAnnotationsService.send(
105
+ params,
106
+ "addTag",
107
+ "api/annotations/tags",
108
+ "api/annotations/tags",
109
+ "post",
110
+ params.params
111
+ );
112
+ return annotation;
113
+ }
114
+ /**
115
+ * The tag dictionary, with the live entities of each tag counted, most used
116
+ * first.
117
+ *
118
+ * The autocomplete of the tag field reads this. It is what stops one idea
119
+ * from becoming three spellings.
120
+ *
121
+ * It answers one PAGE, because the dictionary only grows. Read `page.hasMore`
122
+ * to learn whether another follows.
123
+ */
124
+ static async listTags(params) {
125
+ const url = `api/annotations/tags${queryOf(params.params)}`;
126
+ return ControlAnnotationsService.send(
127
+ params,
128
+ "listTags",
129
+ url,
130
+ "api/annotations/tags",
131
+ "get"
132
+ );
133
+ }
134
+ /**
135
+ * One page of the annotations that match, newest first.
136
+ *
137
+ * The answer holds annotations of entities that no longer exist, which is
138
+ * the point: the note about a deleted cluster is often the only record of
139
+ * why it was there.
140
+ */
141
+ static async search(params) {
142
+ const url = `api/annotations/search${queryOf(params.params)}`;
143
+ return ControlAnnotationsService.send(
144
+ params,
145
+ "search",
146
+ url,
147
+ "api/annotations/search",
148
+ "get"
149
+ );
150
+ }
151
+ /**
152
+ * The shared half of every call above.
153
+ *
154
+ * Seven routes differ only in the verb, the path and the body. One private
155
+ * helper keeps the metrics context and the status check in one place.
156
+ */
157
+ static async send(params, name, url, normalizedUrl, method, body) {
158
+ const serviceContext = contextFor(
159
+ name,
160
+ url,
161
+ normalizedUrl,
162
+ method,
163
+ params.config.metrics?.source || "unset"
164
+ );
165
+ const response = await request({
166
+ // `...params` first, so `cacheControl` and `onCachedResponse` reach the
167
+ // request. Every shape above extends `RequestBaseParams`, so a caller
168
+ // may pass them, and `request` really reads `onCachedResponse` for its
169
+ // stale-while-revalidate path. Passing `config` alone advertised two
170
+ // options in the type and then dropped them both. The named fields
171
+ // below are written after, so they still win.
172
+ ...params,
173
+ config: params.config,
174
+ base: BASE,
175
+ url,
176
+ method,
177
+ metricsDetails: serviceContext,
178
+ body
179
+ });
180
+ if (!response.ok) {
181
+ throw new HttpError(
182
+ `${serviceContext.method.name}: ${response.status} ${response.statusText}`,
183
+ response.status,
184
+ response.statusText
185
+ );
186
+ }
187
+ return response.body;
188
+ }
189
+ }
190
+ export {
191
+ ControlAnnotationsService
192
+ };
193
+ //# sourceMappingURL=control.annotations.service.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/services/control/control.annotations.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 AnnotationDto,\n AnnotationEntityKind,\n AnnotationKind,\n AnnotationLookupDto,\n AnnotationTagDto,\n EntityAnnotationsDto,\n} from '@camunda/ccma-shared-types'\nimport type {\n RequestBaseMethodType,\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 annotation surface of control-backend: the notes and the tags that a\n * Camundi attaches to an entity of the fleet.\n *\n * One service for every kind of entity, and not one for each. The routes carry\n * the kind as a value, which is what lets one panel in control-frontend sit on\n * the cluster page, the organization page and every other entity page without\n * a call of its own.\n *\n * Every route answers 404 while the feature flag `control.annotations` is off\n * on the stage, and while the service reaches no stage at all.\n */\n\nconst BASE = { id: 'control' } as const\n\n/** What names the entity. Every write and the entity read carry it. */\nexport interface AnnotationTargetDto {\n entityKind: AnnotationEntityKind\n entityId: string\n /**\n * What the entity is called now. The backend stores it as a COPY, so a\n * search result still reads after the entity is deleted. Send it when the\n * caller knows the name.\n */\n entityLabel?: string\n}\n\nexport interface GetEntityAnnotationsRequest extends RequestBaseParams {\n params: { entityKind: AnnotationEntityKind; entityId: string }\n}\n\nexport interface LookupAnnotationsRequest extends RequestBaseParams {\n params: { entityKind: AnnotationEntityKind; entityIds: string[] }\n}\n\nexport interface AddAnnotationNoteRequest extends RequestBaseParams {\n params: AnnotationTargetDto & { body: string }\n}\n\nexport interface EditAnnotationNoteRequest extends RequestBaseParams {\n params: {\n id: string\n body: string\n /**\n * The `version` of the note that the caller read.\n *\n * The backend writes only while the row still carries it, and answers 409\n * when it does not. Send what came with the note you are editing, never a\n * number of your own: it is what stops a stale tab from replacing an edit\n * that landed after it was opened.\n */\n version: number\n }\n}\n\nexport interface AnnotationIdRequest extends RequestBaseParams {\n params: { id: string }\n}\n\nexport interface AddAnnotationTagRequest extends RequestBaseParams {\n params: AnnotationTargetDto & { tag: string }\n}\n\nexport interface ListAnnotationTagsRequest extends RequestBaseParams {\n params?: { q?: string; limit?: number; offset?: number }\n}\n\nexport interface SearchAnnotationsRequest extends RequestBaseParams {\n params?: {\n kind?: AnnotationKind\n entityKind?: AnnotationEntityKind\n tag?: string\n q?: string\n limit?: number\n offset?: number\n }\n}\n\n/** One page of a read, and whether the table holds more of it. */\nexport interface AnnotationPageInfoDto {\n limit: number\n offset: number\n /** True when the next page holds more. Never read the length instead. */\n hasMore: boolean\n}\n\nexport interface AnnotationPageDto {\n annotations: AnnotationDto[]\n page: AnnotationPageInfoDto\n}\n\nexport interface AnnotationTagPageDto {\n tags: AnnotationTagDto[]\n page: AnnotationPageInfoDto\n}\n\nfunction contextFor(\n name: string,\n url: string,\n normalizedUrl: string,\n type: RequestBaseMethodType,\n source: string,\n): RequestBaseServiceContext {\n return {\n method: {\n name: `ControlAnnotationsService.${name}`,\n url,\n normalizedUrl,\n type,\n },\n source,\n target: { name: 'control', type: 'internal' },\n }\n}\n\n/** Builds `?a=1&b=2` from the values that are set, and `''` from none. */\nfunction queryOf(params: Record<string, unknown> = {}): string {\n const query = new URLSearchParams()\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined) {\n query.set(key, String(value))\n }\n }\n return query.size > 0 ? `?${query.toString()}` : ''\n}\n\nexport class ControlAnnotationsService {\n /**\n * Every live note and tag of one entity.\n *\n * An entity that carries nothing answers with two empty lists, and never\n * 404. The call also answers for an entity that the fleet no longer holds,\n * which is what keeps a link out of a search result working.\n */\n public static async forEntity(\n params: GetEntityAnnotationsRequest,\n ): Promise<EntityAnnotationsDto> {\n const url = `api/annotations${queryOf(params.params)}`\n return ControlAnnotationsService.send<EntityAnnotationsDto>(\n params,\n 'forEntity',\n url,\n 'api/annotations',\n 'get',\n )\n }\n\n /**\n * The tags, the note count and the newest note of many entities.\n *\n * A POST, because thousands of ids do not fit a request line. The answer\n * holds only the entities that carry something.\n */\n public static async lookup(\n params: LookupAnnotationsRequest,\n ): Promise<AnnotationLookupDto> {\n return ControlAnnotationsService.send<AnnotationLookupDto>(\n params,\n 'lookup',\n 'api/annotations/lookup',\n 'api/annotations/lookup',\n 'post',\n params.params,\n )\n }\n\n /** Writes a note on an entity. Any signed-in Camundi can. */\n public static async addNote(\n params: AddAnnotationNoteRequest,\n ): Promise<AnnotationDto> {\n const { annotation } = await ControlAnnotationsService.send<{\n annotation: AnnotationDto\n }>(\n params,\n 'addNote',\n 'api/annotations/notes',\n 'api/annotations/notes',\n 'post',\n params.params,\n )\n return annotation\n }\n\n /** Changes the text of a note. Only the person who wrote it can. */\n public static async editNote(\n params: EditAnnotationNoteRequest,\n ): Promise<AnnotationDto> {\n const url = `api/annotations/notes/${params.params.id}`\n const { annotation } = await ControlAnnotationsService.send<{\n annotation: AnnotationDto\n }>(params, 'editNote', url, 'api/annotations/notes/:id', 'patch', {\n body: params.params.body,\n version: params.params.version,\n })\n return annotation\n }\n\n /**\n * Takes a note or a tag off an entity.\n *\n * The row stays: the removal is a soft delete, so who wrote it and who took\n * it off both stay readable. A note can only be removed by its author; a tag\n * can be removed by anybody.\n */\n public static async remove(params: AnnotationIdRequest): Promise<void> {\n const url = `api/annotations/${params.params.id}`\n await ControlAnnotationsService.send<undefined>(\n params,\n 'remove',\n url,\n 'api/annotations/:id',\n 'delete',\n )\n }\n\n /**\n * Attaches a tag to an entity, and adds it to the dictionary when it is new.\n *\n * The name is normalized by the backend, so `Partner` and `partner` reach\n * one tag. A second attach of one tag on one entity answers 409.\n */\n public static async addTag(\n params: AddAnnotationTagRequest,\n ): Promise<AnnotationDto> {\n const { annotation } = await ControlAnnotationsService.send<{\n annotation: AnnotationDto\n }>(\n params,\n 'addTag',\n 'api/annotations/tags',\n 'api/annotations/tags',\n 'post',\n params.params,\n )\n return annotation\n }\n\n /**\n * The tag dictionary, with the live entities of each tag counted, most used\n * first.\n *\n * The autocomplete of the tag field reads this. It is what stops one idea\n * from becoming three spellings.\n *\n * It answers one PAGE, because the dictionary only grows. Read `page.hasMore`\n * to learn whether another follows.\n */\n public static async listTags(\n params: ListAnnotationTagsRequest,\n ): Promise<AnnotationTagPageDto> {\n const url = `api/annotations/tags${queryOf(params.params)}`\n return ControlAnnotationsService.send<AnnotationTagPageDto>(\n params,\n 'listTags',\n url,\n 'api/annotations/tags',\n 'get',\n )\n }\n\n /**\n * One page of the annotations that match, newest first.\n *\n * The answer holds annotations of entities that no longer exist, which is\n * the point: the note about a deleted cluster is often the only record of\n * why it was there.\n */\n public static async search(\n params: SearchAnnotationsRequest,\n ): Promise<AnnotationPageDto> {\n const url = `api/annotations/search${queryOf(params.params)}`\n return ControlAnnotationsService.send<AnnotationPageDto>(\n params,\n 'search',\n url,\n 'api/annotations/search',\n 'get',\n )\n }\n\n /**\n * The shared half of every call above.\n *\n * Seven routes differ only in the verb, the path and the body. One private\n * helper keeps the metrics context and the status check in one place.\n */\n private static async send<TResponse>(\n params: RequestBaseParams,\n name: string,\n url: string,\n normalizedUrl: string,\n method: RequestBaseMethodType,\n // `object` and not `Record<string, unknown>`: an interface has no index\n // signature, so the request shapes above would not be assignable to one,\n // and the alternative is `any`.\n body?: object,\n ): Promise<TResponse> {\n const serviceContext = contextFor(\n name,\n url,\n normalizedUrl,\n method,\n params.config.metrics?.source || 'unset',\n )\n const response = await request<TResponse>({\n // `...params` first, so `cacheControl` and `onCachedResponse` reach the\n // request. Every shape above extends `RequestBaseParams`, so a caller\n // may pass them, and `request` really reads `onCachedResponse` for its\n // stale-while-revalidate path. Passing `config` alone advertised two\n // options in the type and then dropped them both. The named fields\n // below are written after, so they still win.\n ...params,\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 // `remove` answers 204, which carries no body. Its caller declares\n // `undefined` and reads nothing, so the assertion below is safe for it\n // too: every other route here answers a body.\n return response.body as TResponse\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -1,4 +1,4 @@
1
- import type { ApprovalJobDto, ApprovalJobStatus, ApprovalUseCaseDto } from '@camunda/ccma-shared-types';
1
+ import type { ApprovalEntityType, ApprovalJobDto, ApprovalJobStatus, ApprovalSortField, ApprovalSortOrder, ApprovalStatusCountsDto, ApprovalUseCaseDto, ClusterBulkOperationPayloadDto, ClusterBulkOperationType } from '@camunda/ccma-shared-types';
2
2
  import type { RequestBaseParams } from '../../types/general.types.js';
3
3
  export interface ApprovalPageDto {
4
4
  approvals: ApprovalJobDto[];
@@ -9,14 +9,66 @@ export interface ApprovalPageDto {
9
9
  hasMore: boolean;
10
10
  };
11
11
  }
12
+ /**
13
+ * What narrows the queue. The list and the counts read the same fields.
14
+ *
15
+ * `entityType` and `entityId` are ONE filter, not two. control-backend answers
16
+ * 400 for one without the other, so the type carries both or neither.
17
+ */
18
+ export type ApprovalFacetParams = {
19
+ type?: string;
20
+ /**
21
+ * One organization, matched whole against `orgId` of the payload.
22
+ *
23
+ * It reads only the payload, so `entityType`/`entityId` is wider. That filter
24
+ * reads the entity link, so it finds a request that touches the organization
25
+ * through another field. The organization page of control-frontend asks the
26
+ * entity-link filter, not this one. `orgId` is also not `q`: the free text
27
+ * reads the whole payload as text.
28
+ */
29
+ orgId?: string;
30
+ /**
31
+ * Free text. control-backend matches it in the reason, the person who
32
+ * opened the request, the action and its label, and the payload that the
33
+ * summary shows.
34
+ */
35
+ q?: string;
36
+ /**
37
+ * The queue of one person: what they opened, or what waits for them.
38
+ *
39
+ * `actionable` is the rule of the Approve button asked in SQL — pending,
40
+ * opened by somebody else, not approved by this person yet, and guarded by
41
+ * permissions that they hold.
42
+ */
43
+ mine?: 'created' | 'actionable';
44
+ } & ({
45
+ /**
46
+ * The kind of entity a request must touch. Read with `entityId`.
47
+ *
48
+ * It reads the entity link of control-backend, so it is wider and truer
49
+ * than `orgId`: a cluster-move request answers for its source
50
+ * organization, its target organization AND the cluster. The organization
51
+ * and cluster detail pages of control-frontend ask this.
52
+ */
53
+ entityType: ApprovalEntityType;
54
+ /** The id of the entity a request must touch. Read with `entityType`. */
55
+ entityId: string;
56
+ } | {
57
+ entityType?: never;
58
+ entityId?: never;
59
+ });
12
60
  export interface ListApprovalsRequest extends RequestBaseParams {
13
- params?: {
14
- type?: string;
61
+ params?: ApprovalFacetParams & {
15
62
  status?: ApprovalJobStatus;
63
+ sort?: ApprovalSortField;
64
+ order?: ApprovalSortOrder;
16
65
  limit?: number;
17
66
  offset?: number;
18
67
  };
19
68
  }
69
+ export interface CountApprovalsRequest extends RequestBaseParams {
70
+ params?: ApprovalFacetParams;
71
+ }
20
72
  export interface ApprovalIdRequest extends RequestBaseParams {
21
73
  params: {
22
74
  id: string;
@@ -28,6 +80,13 @@ export interface RejectApprovalRequest extends RequestBaseParams {
28
80
  reason: string;
29
81
  };
30
82
  }
83
+ /** A withdrawal says why, or says nothing. The reason is not demanded. */
84
+ export interface WithdrawApprovalRequest extends RequestBaseParams {
85
+ params: {
86
+ id: string;
87
+ reason?: string;
88
+ };
89
+ }
31
90
  /** The envelope that every create route of every use case shares. */
32
91
  export interface CreateApprovalRequest<TPayload> extends RequestBaseParams {
33
92
  params: {
@@ -54,6 +113,14 @@ export interface ExtendTrialPayloadDto {
54
113
  */
55
114
  days: number;
56
115
  }
116
+ export interface MoveClusterOrgPayloadDto {
117
+ clusterId: string;
118
+ /** A copy, so a list shows a name that a person knows. Nothing reads it. */
119
+ clusterName?: string;
120
+ targetOrgId: string;
121
+ /** A copy, for the same reason as `clusterName`. */
122
+ targetOrgName?: string;
123
+ }
57
124
  export declare class ControlApprovalsService {
58
125
  /**
59
126
  * The registered privileged actions, with the rules that guard each one.
@@ -70,13 +137,51 @@ export declare class ControlApprovalsService {
70
137
  * the caller cannot act on, unless the caller opened it.
71
138
  */
72
139
  static list(params: ListApprovalsRequest): Promise<ApprovalPageDto>;
140
+ /**
141
+ * How many requests each status holds, for the status filters of a queue.
142
+ *
143
+ * It is the same question as `list` without the status, so a filter that
144
+ * shows a number never opens an empty page.
145
+ */
146
+ static counts(params: CountApprovalsRequest): Promise<ApprovalStatusCountsDto>;
73
147
  static get(params: ApprovalIdRequest): Promise<ApprovalJobDto>;
148
+ /**
149
+ * What a create route answers, plus whether it opened a NEW request.
150
+ *
151
+ * The route answers 201 for a fresh request and 200 for the one already
152
+ * open for the same target — `create` deduplicates. A caller that must
153
+ * tell the two apart (a floor-of-one use case, where 200 is the ordinary
154
+ * path and not a failure) reads `created`.
155
+ */
156
+ static createMoveClusterOrg(params: CreateApprovalRequest<MoveClusterOrgPayloadDto>): Promise<{
157
+ approval: ApprovalJobDto;
158
+ created: boolean;
159
+ }>;
160
+ /**
161
+ * Opens a cluster bulk operation of one type.
162
+ *
163
+ * Each type is its own use case with its own route, because each one has
164
+ * its own permission. The floor of approvals is the same for every type,
165
+ * and it depends on the stage: 2 on prod, 1 on dev, int and local. The
166
+ * route is `api/approvals/<use case>`, as for every other use case. A 200
167
+ * means that the same operation over the same clusters is already open.
168
+ */
169
+ static createClusterBulkOperation<T extends ClusterBulkOperationType>(type: T, params: CreateApprovalRequest<ClusterBulkOperationPayloadDto<T>>): Promise<{
170
+ approval: ApprovalJobDto;
171
+ created: boolean;
172
+ }>;
74
173
  /** Adds the approval of the caller. The last one can run the action. */
75
174
  static acknowledge(params: ApprovalIdRequest): Promise<ApprovalJobDto>;
76
175
  /** Closes a request without running the action. The reason is necessary. */
77
176
  static reject(params: RejectApprovalRequest): Promise<ApprovalJobDto>;
78
- /** Takes back a request. Only the person who opened it can. */
79
- static withdraw(params: ApprovalIdRequest): Promise<ApprovalJobDto>;
177
+ /**
178
+ * Takes back a request. Only the person who opened it can.
179
+ *
180
+ * The reason is optional, and it lands in the same field that a rejection
181
+ * writes. The creator cannot reject their own request, so this is where they
182
+ * say why they closed it.
183
+ */
184
+ static withdraw(params: WithdrawApprovalRequest): Promise<ApprovalJobDto>;
80
185
  /**
81
186
  * Runs a request that holds its approvals.
82
187
  *
@@ -102,6 +207,13 @@ export declare class ControlApprovalsService {
102
207
  * unwrapping in one place.
103
208
  */
104
209
  private static readOne;
105
- /** The shared half of every create route. See `approvalRequestBody`. */
210
+ /**
211
+ * The shared half of every create route. See `approvalRequestBody`.
212
+ *
213
+ * The route answers 201 for a fresh request and 200 for the one already
214
+ * open for the same target (`create` deduplicates). `created` carries that
215
+ * difference. `createSendToExpiredTrial` and `createExtendTrial` do not
216
+ * need it, so they read `approval` alone off this result.
217
+ */
106
218
  private static create;
107
219
  }