"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const chalk_1 = require("chalk");
const fs = require("fs");
const GRPCClient = require("node-grpc-client");
const path = require("path");
const uuid_1 = require("uuid");
const lib_1 = require("../lib");
const ZBWorker_1 = require("./ZBWorker");
const idColors = [
    chalk_1.default.yellow,
    chalk_1.default.green,
    chalk_1.default.cyan,
    chalk_1.default.magenta,
    chalk_1.default.blue,
];
class ZBClient {
    constructor(gatewayAddress, options = {}) {
        this.closing = false;
        this.workerCount = 0;
        this.workers = [];
        if (!gatewayAddress) {
            throw new Error('Must provide a gateway address string to constructor');
        }
        this.options = options || {};
        this.options.loglevel =
            process.env.ZB_NODE_LOG_LEVEL ||
                options.loglevel ||
                'INFO';
        if (gatewayAddress.indexOf(':') === -1) {
            gatewayAddress += ':26500';
        }
        this.gatewayAddress = gatewayAddress;
        this.gRPCClient = new GRPCClient(path.join(__dirname, '../../proto/zeebe.proto'), 'gateway_protocol', 'Gateway', gatewayAddress);
    }
    /**
     *
     * @param id - A unique identifier for this worker.
     * @param taskType - The BPMN Zeebe task type that this worker services.
     * @param taskHandler - A handler for activated jobs.
     * @param options - Configuration options for the worker.
     */
    createWorker(id, taskType, taskHandler, options = {}, onConnectionError) {
        if (this.closing) {
            throw new Error('Client is closing. No worker creation allowed!');
        }
        const idColor = idColors[this.workerCount++ % idColors.length];
        const worker = new ZBWorker_1.ZBWorker({
            gRPCClient: this.gRPCClient,
            id,
            idColor,
            onConnectionError,
            options: { ...this.options, ...options },
            taskHandler,
            taskType,
            zbClient: this,
        });
        this.workers.push(worker);
        return worker;
    }
    /**
     * Gracefully shut down all workers, draining existing tasks, and return when it is safe to exit.
     * @returns Promise
     * @memberof ZBClient
     */
    close() {
        if (this.closePromise) {
            return this.closePromise;
        }
        // Prevent the creation of more workers
        this.closing = true;
        this.closePromise = Promise.all(this.workers.map(w => w.close()));
        return this.closePromise;
    }
    /**
     * Return the broker cluster topology
     */
    topology() {
        return this.gRPCClient.topologySync();
    }
    /**
     *
     * @param workflow - A path or array of paths to .bpmn files.
     * @param {redeploy?: boolean} - Redeploy workflow. Defaults to true.
     * If set false, will not redeploy a workflow that exists.
     */
    async deployWorkflow(workflow, { redeploy = true } = {}) {
        const workflows = Array.isArray(workflow) ? workflow : [workflow];
        let deployedWorkflows = [];
        if (!redeploy) {
            deployedWorkflows = (await this.listWorkflows()).workflows.map((wf) => wf.bpmnProcessId);
        }
        const workFlowRequests = workflows
            .map(wf => ({
            definition: fs.readFileSync(wf),
            name: path.basename(wf),
            type: 1,
        }))
            .filter(wfr => !deployedWorkflows.includes(lib_1.BpmnParser.getProcessId(wfr.definition.toString())));
        if (workFlowRequests.length > 0) {
            return this.gRPCClient.deployWorkflowSync({
                workflows: workFlowRequests,
            });
        }
        else {
            return {
                key: -1,
                workflows: [],
            };
        }
    }
    /**
     * Return an array of task-types specified in a BPMN file.
     * @param file - Path to bpmn file.
     */
    getServiceTypesFromBpmn(files) {
        if (typeof files === 'string') {
            files = [files];
        }
        return lib_1.BpmnParser.getTaskTypes(lib_1.BpmnParser.parseBpmn(files));
    }
    /**
     * Publish a message to the broker for correlation with a workflow instance.
     * @param publishMessageRequest - The message to publish.
     */
    publishMessage(publishMessageRequest) {
        return this.gRPCClient.publishMessageSync(lib_1.stringifyVariables(publishMessageRequest));
    }
    /**
     * Publish a message to the broker for correlation with a workflow message start event.
     * @param publishStartMessageRequest - The message to publish.
     */
    publishStartMessage(publishStartMessageRequest) {
        /**
         * The hash of the correlationKey is used to determine the partition where this workflow will start.
         * So we assign a random uuid to balance workflow instances created via start message across partitions.
         *
         * We make the correlationKey optional, because the caller can specify a correlationKey + messageId
         * to guarantee an idempotent message.
         *
         * Multiple messages with the same correlationKey + messageId combination will only start a workflow once.
         * See: https://github.com/zeebe-io/zeebe/issues/1012 and https://github.com/zeebe-io/zeebe/issues/1022
         */
        const publishMessageRequest = {
            correlationKey: uuid_1.v4(),
            ...publishStartMessageRequest,
        };
        return this.gRPCClient.publishMessageSync(lib_1.stringifyVariables(publishMessageRequest));
    }
    updateJobRetries(updateJobRetriesRequest) {
        return this.gRPCClient.updateJobRetriesSync(updateJobRetriesRequest);
    }
    failJob(failJobRequest) {
        return this.gRPCClient.failJobSync(failJobRequest);
    }
    /**
     *
     * Create and start execution of a workflow instance.
     * @param {string} bpmnProcessId
     * @param {Variables} variables - payload to pass in to the workflow
     * @param {number} [version] - version of the workflow to run. Optional: defaults to latest if not present
     * @returns {Promise<CreateWorkflowInstanceResponse>}
     * @memberof ZBClient
     */
    createWorkflowInstance(bpmnProcessId, variables, version) {
        version = version || -1;
        const createWorkflowInstanceRequest = {
            bpmnProcessId,
            variables: variables,
            version,
        };
        return this.gRPCClient.createWorkflowInstanceSync(lib_1.stringifyVariables(createWorkflowInstanceRequest));
    }
    async cancelWorkflowInstance(workflowInstanceKey) {
        return this.gRPCClient.cancelWorkflowInstanceSync({
            workflowInstanceKey,
        });
    }
    setVariables(request) {
        /*
        We allow developers to interact with variables as a native JS object, but the Zeebe server needs it as a JSON document
        So we stringify it here.
        */
        if (typeof request.variables === 'object') {
            request.variables = JSON.stringify(request.variables);
        }
        return this.gRPCClient.setVariablesSync(request);
    }
    listWorkflows(bpmnProcessId) {
        return this.gRPCClient.listWorkflowsSync({ bpmnProcessId });
    }
    getWorkflow(getWorkflowRequest) {
        if (this.hasBpmnProcessId(getWorkflowRequest)) {
            getWorkflowRequest.version = getWorkflowRequest.version || -1;
        }
        return this.gRPCClient.getWorkflowSync(getWorkflowRequest);
    }
    resolveIncident(incidentKey) {
        return this.gRPCClient.resolveIncidentSync(incidentKey);
    }
    hasBpmnProcessId(request) {
        return (request
            .bpmnProcessId !== undefined);
    }
}
exports.ZBClient = ZBClient;
//# sourceMappingURL=ZBClient.js.map