UNPKG

@camunda8/sdk

Version:

[![NPM](https://nodei.co/npm/@camunda8/sdk.png)](https://www.npmjs.com/package/@camunda8/sdk)

973 lines 41 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.CamundaRestClient = void 0; const node_fs_1 = __importDefault(require("node:fs")); const debug_1 = require("debug"); const form_data_1 = __importDefault(require("form-data")); const got_1 = __importDefault(require("got")); const lossless_json_1 = require("lossless-json"); const lib_1 = require("../../lib"); const types_1 = require("../../zeebe/types"); const C8Dto_1 = require("./C8Dto"); const C8Logger_1 = require("./C8Logger"); const CamundaJobWorker_1 = require("./CamundaJobWorker"); const RestApiJobClassFactory_1 = require("./RestApiJobClassFactory"); const RestApiProcessInstanceClassFactory_1 = require("./RestApiProcessInstanceClassFactory"); const trace = (0, debug_1.debug)('camunda:zeebe-rest'); const CAMUNDA_REST_API_VERSION = 'v2'; class DefaultLosslessDto extends lib_1.LosslessDto { } /** * The client for the unified Camunda 8 REST API. * * Logging: to enable debug tracing during development, you can set `DEBUG=camunda:zeebe-rest`. * * For production, you can pass in an instance of [winston.Logger](https://github.com/winstonjs/winston) to the constructor as `logger`. * * `CAMUNDA_LOG_LEVEL` in the environment or the constructor options can be used to set the log level to one of 'error', 'warn', 'info', 'http', 'verbose', 'debug', or 'silly'. * * @since 8.6.0 */ class CamundaRestClient { /** * All constructor parameters for configuration are optional. If no configuration is provided, the SDK will use environment variables to configure itself. */ constructor(options) { /** * Helper method to add the default job methods to a job * @param job The job to add the methods to * @returns The job with the added methods */ this.addJobMethods = (job) => { return { ...job, cancelWorkflow: () => { this.cancelProcessInstance({ processInstanceKey: job.processInstanceKey, }); throw new Error('Not Implemented'); }, complete: (variables = {}) => this.completeJob({ jobKey: job.jobKey, variables, }), error: (error) => this.errorJob({ ...error, jobKey: job.jobKey, }), fail: (failJobRequest) => this.failJob({ jobKey: job.jobKey, errorMessage: failJobRequest.errorMessage, retries: failJobRequest.retries ?? job.retries - 1, retryBackOff: failJobRequest.retryBackOff ?? 0, variables: failJobRequest.variables, }), /* This has an effect in a Job Worker, decrementing the currently active job count */ forward: () => types_1.JOB_ACTION_ACKNOWLEDGEMENT, modifyJobTimeout: ({ newTimeoutMs }) => this.updateJob({ jobKey: job.jobKey, timeout: newTimeoutMs }), }; }; const config = lib_1.CamundaEnvironmentConfigurator.mergeConfigWithEnvironment(options?.config ?? {}); this.config = config; this.log = (0, C8Logger_1.getLogger)(config); this.log.debug(`Using REST API version ${CAMUNDA_REST_API_VERSION}`); trace('options.config', options?.config); trace('config', config); this.oAuthProvider = options?.oAuthProvider ?? (0, lib_1.constructOAuthProvider)(config); this.userAgentString = (0, lib_1.createUserAgentString)(config); this.tenantId = config.CAMUNDA_TENANT_ID; const baseUrl = (0, lib_1.RequireConfiguration)(config.ZEEBE_REST_ADDRESS, 'ZEEBE_REST_ADDRESS'); this.prefixUrl = `${baseUrl}/${CAMUNDA_REST_API_VERSION}`; this.rest = (0, lib_1.GetCustomCertificateBuffer)(config).then((certificateAuthority) => got_1.default.extend({ prefixUrl: this.prefixUrl, retry: lib_1.GotRetryConfig, https: { certificateAuthority, }, handlers: [lib_1.gotErrorHandler], hooks: { beforeRetry: [ (0, lib_1.makeBeforeRetryHandlerFor401TokenRetry)(this.getHeaders.bind(this)), ], beforeError: [lib_1.gotBeforeErrorHook], beforeRequest: [ (options) => { const body = options.body; const path = options.url.href; const method = options.method; trace(`${method} ${path}`); trace(body); this.log.debug(`${method} ${path}`); this.log.trace(body?.toString()); }, ...(config.middleware ?? []), ], }, })); } async getHeaders() { const token = await this.oAuthProvider.getToken('ZEEBE'); const headers = { 'content-type': 'application/json', authorization: token, 'user-agent': this.userAgentString, accept: '*/*', }; const safeHeaders = { ...headers, authorization: headers.authorization.substring(0, 15) + (headers.authorization.length > 8) ? '...' : '', }; trace('headers', safeHeaders); return headers; } /** * Manage the permissions assigned to authorization. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/patch-authorization/ * * @since 8.6.0 */ async modifyAuthorization(req) { const headers = await this.getHeaders(); const { ownerKey, ...request } = req; return this.rest.then((rest) => rest .patch(`authorizations/${ownerKey}`, { headers, body: (0, lossless_json_1.stringify)(request), }) .json()); } /** * Broadcast a signal. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/broadcast-signal/ * * @since 8.6.0 */ async broadcastSignal(req) { const headers = await this.getHeaders(); const request = this.addDefaultTenantId(req); return this.rest.then((rest) => rest .post(`signals/broadcast`, { headers, body: (0, lossless_json_1.stringify)(request), parseJson: (text) => (0, lib_1.losslessParse)(text, C8Dto_1.BroadcastSignalResponse), }) .json()); } /* Get the topology of the Zeebe cluster. */ async getTopology() { const headers = await this.getHeaders(); return this.rest.then((rest) => rest.get('topology', { headers }).json()); } /** * Complete a user task with the given key. The method either completes the task or throws 400, 404, or 409. * * Documentation: https://docs.camunda.io/docs/apis-tools/zeebe-api-rest/specifications/complete-a-user-task/ * * @since 8.6.0 */ async completeUserTask({ userTaskKey, variables = {}, action = 'complete', }) { const headers = await this.getHeaders(); return this.rest.then((rest) => rest .post(`user-tasks/${userTaskKey}/completion`, { body: (0, lib_1.losslessStringify)({ variables, action, }), headers, }) .json()); } /** * Assign a user task with the given key to the given assignee. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/assign-user-task/ * * @since 8.6.0 * @deprecated use `assignUserTask` */ async assignTask({ userTaskKey, assignee, allowOverride = true, action = 'assign', }) { return this.assignUserTask({ userTaskKey, assignee, allowOverride, action }); } /** * Assign a user task with the given key to the given assignee. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/assign-user-task/ * * @since 8.6.0 */ async assignUserTask({ userTaskKey, assignee, allowOverride = true, action = 'assign', }) { const headers = await this.getHeaders(); const req = { allowOverride, action, assignee, }; return this.rest.then((rest) => rest .post(`user-tasks/${userTaskKey}/assignment`, { body: (0, lib_1.losslessStringify)(req), headers, }) .json()); } /** * Update a user task with the given key. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/update-user-task/ * * @since 8.6.0 * @deprecated use `updateUserTask` */ async updateTask({ userTaskKey, changeset, }) { return this.updateUserTask({ userTaskKey, changeset }); } /** * Update a user task with the given key. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/update-user-task/ * * @since 8.6.0 */ async updateUserTask({ userTaskKey, changeset, }) { const headers = await this.getHeaders(); return this.rest.then((rest) => rest .patch(`user-tasks/${userTaskKey}/update`, { body: (0, lib_1.losslessStringify)(changeset), headers, }) .json()); } /** * Remove the assignee of a task with the given key. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/unassign-user-task/ * * @since 8.6.0 * @deprecated use `unassignUserTask` */ async unassignTask({ userTaskKey, }) { return this.unassignUserTask({ userTaskKey }); } /** * Remove the assignee of a task with the given key. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/unassign-user-task/ * * @since 8.6.0 */ async unassignUserTask({ userTaskKey, }) { const headers = await this.getHeaders(); return this.rest.then((rest) => rest.delete(`user-tasks/${userTaskKey}/assignee`, { headers }).json()); } /** * Search for user tasks based on given criteria. * * Documentation: https://docs.camunda.io/docs/8.7/apis-tools/camunda-api-rest/specifications/find-user-tasks/ * * @since 8.8.0 - alpha status in 8.6 and 8.7 */ async searchUserTasks(request) { const headers = await this.getHeaders(); const page = request.page ?? { from: 0, limit: 100, }; const sort = request.sort ?? [{ field: 'creationDate', order: 'asc' }]; const response = await this.rest.then((rest) => rest .post(`user-tasks/search`, { headers, body: (0, lib_1.losslessStringify)({ ...request, page, sort }), }) .json()); /** * The 8.6 and 8.7 API have different key names for the userTaskKey. This code block normalizes the key names. */ return { ...response, items: response.items.map((item) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any if (!item.userTaskKey && item.key) { // eslint-disable-next-line @typescript-eslint/no-explicit-any item.userTaskKey = item.key; } return item; }), }; } /** * Get the user task by the user task key. * * Documentation: https://docs.camunda.io/docs/next/apis-tools/camunda-api-rest/specifications/get-user-task/ * * @since 8.8.0 */ async getUserTask(userTaskKey) { const headers = await this.getHeaders(); return this.rest.then((rest) => rest.get(`user-tasks/${userTaskKey}`, { headers }).json()); } /** * * Search for user task variables based on given criteria. * * Documentation: https://docs.camunda.io/docs/next/apis-tools/camunda-api-rest/specifications/find-user-task-variables/ * * @since 8.8.0 */ async searchUserTaskVariables(request) { const { userTaskKey, ...req } = request; const headers = await this.getHeaders(); return this.rest.then((rest) => rest .post(`user-tasks/${userTaskKey}/variables/search`, { headers, body: (0, lib_1.losslessStringify)(req), }) .json()); } /** * Create a user. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/create-user/ * * @since 8.6.0 */ async createUser(newUserInfo) { const headers = await this.getHeaders(); return this.rest.then((rest) => rest .post(`users`, { body: JSON.stringify(newUserInfo), headers, }) .json()); } /** * Search for user tasks based on given criteria. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/query-user-tasks-alpha/ * @since 8.8.0 */ // public async searchUserTasks() {} /** * Publish a Message and correlates it to a subscription. If correlation is successful it will return the first process instance key the message correlated with. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/correlate-a-message/ * * @since 8.6.0 */ async correlateMessage(message) { const headers = await this.getHeaders(); const req = this.addDefaultTenantId(message); const body = (0, lib_1.losslessStringify)(req); return this.rest.then((rest) => rest .post(`messages/correlation`, { body, headers, parseJson: (text) => (0, lib_1.losslessParse)(text, C8Dto_1.CorrelateMessageResponse), }) .json()); } /** * Publish a single message. Messages are published to specific partitions computed from their correlation keys. This method does not wait for a correlation result. Use `correlateMessage` for such use cases. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/publish-a-message/ * * @since 8.6.0 */ async publishMessage(publishMessageRequest) { const headers = await this.getHeaders(); const req = this.addDefaultTenantId(publishMessageRequest); const body = (0, lib_1.losslessStringify)(req); return this.rest.then((rest) => rest .post(`messages/publication`, { headers, body, parseJson: (text) => (0, lib_1.losslessParse)(text, C8Dto_1.PublishMessageResponse), }) .json()); } /** * Obtains the status of the current Camunda license. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/get-status-of-camunda-license/ * * @since 8.6.0 */ async getLicenseStatus() { return this.rest.then((rest) => rest.get(`license`).json()); } /** * Create a new polling Job Worker. * You can pass in an optional winston.Logger instance as `logger`. This enables you to have distinct logging levels for different workers. * * Polling: The worker polls periodically. If no jobs are available, the poll stays open for 10 seconds. * If no jobs become available in that time, the poll is closed, and the worker polls again. * When jobs are available, they are returned, and the worker polls again for more jobs as soon as it has capacity for more jobs. * * @since 8.6.0 */ createJobWorker(config) { const worker = new CamundaJobWorker_1.CamundaJobWorker(config, this); return worker; } /** * Iterate through all known partitions and activate jobs up to the requested maximum. * * The parameter `inputVariablesDto` is a Dto to decode the job payload. The `customHeadersDto` parameter is a Dto to decode the custom headers. * Pass in a Dto class that extends LosslessDto to provide both type information in your code, * and safe interoperability with applications that use the `int64` type in variables. * * @since 8.6.0 */ async activateJobs(request) { const headers = await this.getHeaders(); const { inputVariableDto = lib_1.LosslessDto, customHeadersDto = lib_1.LosslessDto, tenantIds = this.tenantId ? [this.tenantId] : undefined, ...req } = request; /** * The ActivateJobs endpoint can take multiple tenantIds, and activate jobs for multiple tenants at once. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/activate-jobs/ */ const body = (0, lib_1.losslessStringify)({ ...req, tenantIds, }); const jobDto = (0, RestApiJobClassFactory_1.createSpecializedRestApiJobClass)(inputVariableDto, customHeadersDto); return this.rest.then((rest) => rest .post(`jobs/activation`, { body, headers, parseJson: (text) => (0, lib_1.losslessParse)(text, jobDto, 'jobs'), }) .json() .then((activatedJobs) => activatedJobs.map(this.addJobMethods))); } /** * Fails a job using the provided job key. This method sends a POST request to the endpoint '/jobs/{jobKey}/fail' with the failure reason and other details specified in the failJobRequest object. * * Documentation: https://docs.camunda.io/docs/next/apis-tools/camunda-api-rest/specifications/fail-job/ * * @since 8.6.0 */ async failJob(failJobRequest) { const { jobKey } = failJobRequest; const headers = await this.getHeaders(); return this.rest.then((rest) => rest .post(`jobs/${jobKey}/failure`, { body: (0, lib_1.losslessStringify)(failJobRequest), headers, }) .then(() => types_1.JOB_ACTION_ACKNOWLEDGEMENT)); } /** * Report a business error (i.e. non-technical) that occurs while processing a job. * * Documentation: https://docs.camunda.io/docs/next/apis-tools/camunda-api-rest/specifications/report-error-for-job/ * * @since 8.6.0 */ async errorJob(errorJobRequest) { const { jobKey, ...request } = errorJobRequest; const headers = await this.getHeaders(); return this.rest.then((rest) => rest .post(`jobs/${jobKey}/error`, { body: (0, lib_1.losslessStringify)(request), headers, parseJson: (text) => (0, lib_1.losslessParse)(text), }) .then(() => types_1.JOB_ACTION_ACKNOWLEDGEMENT)); } /** * Complete a job with the given payload, which allows completing the associated service task. * * Documentation: https://docs.camunda.io/docs/next/apis-tools/camunda-api-rest/specifications/complete-job/ * * @since 8.6.0 */ async completeJob(completeJobRequest) { const { jobKey } = completeJobRequest; const headers = await this.getHeaders(); const req = { variables: completeJobRequest.variables }; return this.rest.then((rest) => rest .post(`jobs/${jobKey}/completion`, { body: (0, lib_1.losslessStringify)(req), headers, }) .then(() => types_1.JOB_ACTION_ACKNOWLEDGEMENT)); } /** * Update a job with the given key. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/update-a-job/ * * @since 8.6.0 */ async updateJob(jobChangeset) { const { jobKey, ...changeset } = jobChangeset; const headers = await this.getHeaders(); return this.rest.then((rest) => rest.patch(`jobs/${jobKey}`, { body: JSON.stringify(changeset), headers, })); } /** * Marks the incident as resolved; most likely a call to Update job will be necessary to reset the job's retries, followed by this call. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/resolve-incident/ * * @since 8.6.0 */ async resolveIncident(incidentKey) { const headers = await this.getHeaders(); return this.rest.then((rest) => rest.post(`incidents/${incidentKey}/resolution`, { headers, })); } async createProcessInstance(request) { const headers = await this.getHeaders(); const outputVariablesDto = request.outputVariablesDto ?? DefaultLosslessDto; const CreateProcessInstanceResponseWithVariablesDto = (0, RestApiProcessInstanceClassFactory_1.createSpecializedCreateProcessInstanceResponseClass)(outputVariablesDto); return this.rest.then((rest) => rest .post(`process-instances`, { body: (0, lib_1.losslessStringify)(this.addDefaultTenantId(request)), headers, parseJson: (text) => (0, lib_1.losslessParse)(text, CreateProcessInstanceResponseWithVariablesDto), }) .json()); } async createProcessInstanceWithResult(request) { /** * We override the type system to make `awaitCompletion` hidden from end-users. This has been done because supporting the permutations of * creating a process with/without awaiting the result and with/without an outputVariableDto in a single method is complex. I could not get all * the cases to work with intellisense for the end-user using either generics or with signature overloads. * * To address this, createProcessInstance has all the functionality, but hides the `awaitCompletion` attribute from the signature. This method * is a wrapper around createProcessInstance that sets `awaitCompletion` to true, and explicitly informs the type system via signature overloads. * * This is not ideal, but it is the best solution I could come up with. */ return this.createProcessInstance({ ...request, awaitCompletion: true, outputVariablesDto: request.outputVariablesDto, }); } /** * Cancel an active process instance */ async cancelProcessInstance({ processInstanceKey, operationReference, }) { const headers = await this.getHeaders(); return this.rest.then((rest) => rest.post(`process-instances/${processInstanceKey}/cancellation`, { body: JSON.stringify({ operationReference }), headers, })); } /** * Migrates a process instance to a new process definition. * This request can contain multiple mapping instructions to define mapping between the active process instance's elements and target process definition elements. * Use this to upgrade a process instance to a new version of a process or to a different process definition, e.g. to keep your running instances up-to-date with the latest process improvements. * * Documentation: https://docs.camunda.io/docs/next/apis-tools/camunda-api-rest/specifications/migrate-process-instance/ * * @since 8.6.0 */ async migrateProcessInstance(req) { const headers = await this.getHeaders(); const { processInstanceKey, ...request } = req; this.log.debug(`Migrating process instance ${processInstanceKey}`, { component: 'C8RestClient', }); return this.rest.then((rest) => rest.post(`process-instances/${processInstanceKey}/migration`, { headers, body: (0, lib_1.losslessStringify)(request), })); } /** * Query process instances * * Documentation: https://docs.camunda.io/docs/8.7/apis-tools/camunda-api-rest/specifications/query-process-instances-alpha/ * * @since 8.8.0 */ async searchProcessInstances(request) { const headers = await this.getHeaders(); const page = request.page ?? { from: 0, limit: 100, }; return this.rest.then((rest) => rest .post(`process-instances/search`, { headers, body: (0, lib_1.losslessStringify)({ ...request, page }), }) .json()); } /** * Deploy resources to the broker. * @param resources - An array of binary data strings representing the resources to deploy. * @param tenantId - Optional tenant ID to deploy the resources to. If not provided, the default tenant ID is used. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/deploy-resources/ * * @since 8.6.0 */ async deployResources(resources, tenantId) { const headers = await this.getHeaders(); const formData = new form_data_1.default(); resources.forEach((resource) => { formData.append(`resources`, resource.content, { filename: resource.name, }); }); if (tenantId || this.tenantId) { formData.append('tenantId', tenantId ?? this.tenantId); } this.log.debug(`Deploying ${resources.length} resources`); const res = await this.rest.then((rest) => rest .post('deployments', { body: formData, headers: { ...headers, ...formData.getHeaders(), Accept: 'application/json', }, parseJson: (text) => (0, lossless_json_1.parse)(text), // we parse the response with LosslessNumbers, with no Dto }) .json()); /** * Now we need to examine the response and parse the deployments to lossless Dtos * We dynamically construct the response object for the caller, by examining the lossless response * and re-parsing each of the deployments with the correct Dto. */ const deploymentResponse = new C8Dto_1.DeployResourceResponse(); deploymentResponse.deploymentKey = res.deploymentKey.toString(); deploymentResponse.tenantId = res.tenantId; deploymentResponse.deployments = []; deploymentResponse.processes = []; deploymentResponse.decisions = []; deploymentResponse.decisionRequirements = []; deploymentResponse.forms = []; /** * Type-guard assertions to correctly type the deployments. The API returns an array with mixed types. */ const isProcessDeployment = (deployment) => !!deployment.processDefinition; const isDecisionDeployment = (deployment) => !!deployment.decisionDefinition; const isDecisionRequirementsDeployment = (deployment) => !!deployment.decisionRequirements; const isFormDeployment = (deployment) => !!deployment.form; /** * Here we examine each of the deployments returned from the API, and create a correctly typed * object for each one. We also populate subkeys per type. This allows SDK users to work with * types known ahead of time. */ res.deployments.forEach((deployment) => { if (isProcessDeployment(deployment)) { const processDeployment = (0, lib_1.losslessParse)((0, lossless_json_1.stringify)(deployment.processDefinition), C8Dto_1.ProcessDeployment); deploymentResponse.deployments.push({ processDefinition: processDeployment, }); deploymentResponse.processes.push(processDeployment); } if (isDecisionDeployment(deployment)) { const decisionDeployment = (0, lib_1.losslessParse)((0, lossless_json_1.stringify)(deployment), C8Dto_1.DecisionDeployment); deploymentResponse.deployments.push({ decisionDefinition: decisionDeployment, }); deploymentResponse.decisions.push(decisionDeployment); } if (isDecisionRequirementsDeployment(deployment)) { const decisionRequirementsDeployment = (0, lib_1.losslessParse)((0, lossless_json_1.stringify)(deployment), C8Dto_1.DecisionRequirementsDeployment); deploymentResponse.deployments.push({ decisionRequirements: decisionRequirementsDeployment, }); deploymentResponse.decisionRequirements.push(decisionRequirementsDeployment); } if (isFormDeployment(deployment)) { const formDeployment = (0, lib_1.losslessParse)((0, lossless_json_1.stringify)(deployment), C8Dto_1.FormDeployment); deploymentResponse.deployments.push({ form: formDeployment }); deploymentResponse.forms.push(formDeployment); } }); return deploymentResponse; } /** * Deploy resources to Camunda 8 from files * @param files an array of file paths * * @since 8.6.0 */ async deployResourcesFromFiles(files, { tenantId } = {}) { const resources = []; for (const file of files) { resources.push({ content: node_fs_1.default.readFileSync(file, { encoding: 'binary' }), name: file, }); } return this.deployResources(resources, tenantId ?? this.tenantId); } /** * Deletes a deployed resource. This can be a process definition, decision requirements definition, or form definition deployed using the deploy resources endpoint. Specify the resource you want to delete in the resourceKey parameter. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/delete-resource/ * * @since 8.6.0 */ async deleteResource(req) { const headers = await this.getHeaders(); const { resourceKey, operationReference } = req; return this.rest.then((rest) => rest.post(`resources/${resourceKey}/deletion`, { headers, body: (0, lossless_json_1.stringify)({ operationReference }), })); } /** * Set a precise, static time for the Zeebe engine's internal clock. * When the clock is pinned, it remains at the specified time and does not advance. * To change the time, the clock must be pinned again with a new timestamp, or reset. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/pin-internal-clock/ * * @since 8.6.0 */ async pinInternalClock(epochMs) { const headers = await this.getHeaders(); return this.rest.then((rest) => rest.put(`clock`, { headers, body: JSON.stringify({ timestamp: epochMs }), })); } /** * Resets the Zeebe engine's internal clock to the current system time, enabling it to tick in real-time. * This operation is useful for returning the clock to normal behavior after it has been pinned to a specific time. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/reset-internal-clock/ * * @since 8.6.0 */ async resetClock() { const headers = await this.getHeaders(); return this.rest.then((rest) => rest.post(`clock/reset`, { headers })); } /** * Updates all the variables of a particular scope (for example, process instance, flow element instance) with the given variable data. * Specify the element instance in the elementInstanceKey parameter. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/update-element-instance-variables/ * * @since 8.6.0 */ async updateElementInstanceVariables(req) { const headers = await this.getHeaders(); const { elementInstanceKey, ...request } = req; return this.rest.then((rest) => rest.post(`element-instances/${elementInstanceKey}/variables`, { headers, body: (0, lossless_json_1.stringify)(request), })); } getConfig() { return this.config; } /** * Search for process and local variables based on given criteria. * * Documentation: https://docs.camunda.io/docs/next/apis-tools/camunda-api-rest/specifications/find-variables/ * @since 8.8.0 */ async searchVariables(req) { const headers = await this.getHeaders(); return this.rest.then((rest) => rest .post(`variables/search`, { headers, body: (0, lossless_json_1.stringify)(req), }) .json()); } /** * Download a document from the Camunda 8 cluster. * * Note that this is currently supported for document stores of type: AWS, GCP, in-memory, local * Documentation: https://docs.camunda.io/docs/8.7/apis-tools/camunda-api-rest/specifications/get-document/ * * @since 8.7.0 */ async downloadDocument(request) { const headers = await this.getHeaders(); return this.rest.then((rest) => rest .get(`documents/${request.documentId}`, { headers: { ...headers, // we need the headers to be passed in accept: '*/*', }, searchParams: { contentHash: request.contentHash, storeId: request.storeId, }, }) .buffer()); } /** * Upload a document to the Camunda 8 cluster. * Note that this is currently supported for document stores of type: AWS, GCP, in-memory, local * * Documentation: https://docs.camunda.io/docs/8.7/apis-tools/camunda-api-rest/specifications/create-document/ * @since 8.7.0 */ async uploadDocument(request) { const headers = await this.getHeaders(); const formData = new form_data_1.default(); const options = request.metadata?.contentType || request.metadata?.fileName ? { contentType: request.metadata?.contentType, filename: request.metadata?.fileName, } : {}; formData.append('file', request.file, options); // Add other form fields if (request.metadata) { formData.append('metadata', JSON.stringify(request.metadata), { contentType: 'application/json', }); } return this.rest.then((rest) => rest .post('documents', { searchParams: { storeId: request.storeId, documentId: request.documentId, }, headers: { ...headers, ...formData.getHeaders(), accept: 'application/json', }, body: formData, parseJson: (text) => (0, lib_1.losslessParse)(text, C8Dto_1.UploadDocumentResponse), }) .json()); } /** * Delete a document from the Camunda 8 cluster. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/delete-document/ * * @since 8.7.0 */ async deleteDocument({ documentId, storeId, }) { const headers = await this.getHeaders(); return this.rest.then((rest) => rest .delete(`documents/${documentId}`, { headers, searchParams: storeId ? { storeId } : undefined, }) .json()); } /** * * Upload multiple documents to the Camunda 8 cluster. * The caller must provide a file name for each document, which will be used in case of a multi-status response to identify which documents failed to upload. * The file name can be provided in the Content-Disposition header of the file part or in the fileName field of the metadata part. * If both are provided, the fileName field takes precedence. * * In case of a multi-status response, the response body will contain a list of DocumentBatchProblemDetail objects, * each of which contains the file name of the document that failed to upload and the reason for the failure. * The client can choose to retry the whole batch or individual documents based on the response. * * Note that this is currently supported for document stores of type: AWS, GCP, in-memory (non-production), local (non-production) * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/create-documents/ * @since 8.7.0 */ async uploadDocuments(request) { const headers = await this.getHeaders(); const formData = new form_data_1.default(); for (const file of request.files) { formData.append('files', file); } return this.rest.then((rest) => rest .post('documents/batch', { searchParams: { storeId: request.storeId ? request.storeId : undefined, }, headers: { ...headers, ...formData.getHeaders(), accept: 'application/json', }, body: formData, parseJson: (text) => (0, lib_1.losslessParse)(text, C8Dto_1.UploadDocumentsResponse), }) .json()); } /** * Create document link * * Create a link to a document in the Camunda 8 cluster. * Note that this is currently supported for document stores of type: AWS, GCP * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/create-document-link/ * @since 8.7.0 */ async createDocumentLink(request) { const headers = await this.getHeaders(); return this.rest.then((rest) => rest .post(`documents/${request.documentId}/link`, { headers, searchParams: { storeId: request.storeId ? request.storeId : undefined, contentHash: request.contentHash ? request.contentHash : undefined, }, body: (0, lib_1.losslessStringify)({ timeToLive: request.timeToLive }), }) .json()); } /** * Modify process instance * * Modifies a running process instance. This request can contain multiple instructions to activate an element of the process or to terminate an active instance of an element. * Use this to repair a process instance that is stuck on an element or took an unintended path. For example, because an external system is not available or doesn't respond as expected. * * Documentation https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/modify-process-instance/ * @since 8.6.0 */ async modifyProcessInstance(request) { const headers = await this.getHeaders(); const { processInstanceKey, ...req } = request; return this.rest.then((rest) => rest .post(`process-instances/${processInstanceKey}/modification`, { headers, body: (0, lib_1.losslessStringify)(req), }) .json()); } /** * Evaluate decision * * Evaluates a decision. You specify the decision to evaluate either by using its unique key (as returned by DeployResource), or using the decision ID. * When using the decision ID, the latest deployed version of the decision is used. * * Documentation: https://docs.camunda.io/docs/apis-tools/camunda-api-rest/specifications/evaluate-decision/ * @since 8.6.0 */ async evaluateDecision(request) { const headers = await this.getHeaders(); return this.rest.then((rest) => rest .post(`decision-definitions/evaluation`, { headers, body: (0, lib_1.losslessStringify)(this.addDefaultTenantId(request)), }) .json()); } /** * Helper method to add the default tenantIds if we are not passed explicit tenantIds */ addDefaultTenantId(request) { const tenantId = request.tenantId ?? this.tenantId; return { ...request, tenantId }; } } exports.CamundaRestClient = CamundaRestClient; //# sourceMappingURL=CamundaRestClient.js.map