@camunda8/sdk
Version:
[](https://www.npmjs.com/package/@camunda8/sdk)
973 lines • 41 kB
JavaScript
;
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