Create a deploy using OpenAPI

Greetings all, I am currently using Camunda 7 with OpenAPI and I want to integrate it with a backend made in NestJs, TypeScript and SqlServer. Currently this is my code:

    async createDeployment( context: ExecutionContext, res: Deployment ): Promise<void> {
        try {
            const camundaClient = await this.camundaService.camundaClient();

            const { tenantId, deployChangedOnly, deploymentActivationTime, deploymentName, deploymentSource, enableDuplicateFiltering, file } = res;

            const resolverdPromise = await file;
            
            const { id: fileId } = resolverdPromise;

            const tempFile = fileId ? (await this.filesService.findBuffer({user:undefined}, fileId )).toString() : undefined
            
            const camundaData = {
                "tenant-id": tenantId,
                "deploy-changed-only": deployChangedOnly,
                "deployment-activation-time": deploymentActivationTime,
                "deployment-name": deploymentName,
                "deployment-source": deploymentSource,
                "enable-duplicate-filtering": enableDuplicateFiltering,
                data: tempFile
                // data: fileId ? (await this.filesService.findBuffer({user:undefined}, fileId )).toString() : undefined
            }

            console.log({camundaData}) 

            await camundaClient.createDeployment(null, camundaData, {
                headers: {
                    "Content-Type": "multipart/form-data"
                }
            });
        } catch (error) {
            console.log({error})

            throw new InternalServerErrorException(
                'Camunda: ' + (error.response.message ? error.response.message : error.response.data.message),
            );
        }
    }

I get this in the console.log({camundaData}):

{
  camundaData: {
    'tenant-id': null,
    'deploy-changed-only': null,
    'deployment-activation-time': null,
    'deployment-name': 'NGG',
    'deployment-source': null,
    'enable-duplicate-filtering': null,
    data: '<?xml version="1.0" encoding="UTF-8"?>\n' +
      '<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_0zwqm3h" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.16.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.20.0">\n' +
      '  <bpmn:collaboration id="Collaboration_0wd5l4r">\n' +
      '    <bpmn:participant id="Participant_0tj95nj" processRef="probando_cargue" />\n' +
      '  </bpmn:collaboration>\n' +
      '  <bpmn:process id="probando_cargue" isExecutable="true" camunda:historyTimeToLive="180">\n' +
      '    <bpmn:startEvent id="StartEvent_1">\n' +
      '      <bpmn:outgoing>Flow_1aslf5z</bpmn:outgoing>\n' +
      '    </bpmn:startEvent>\n' +
      '    <bpmn:sequenceFlow id="Flow_1aslf5z" sourceRef="StartEvent_1" targetRef="Activity_1edo5u3" />\n' +
      '    <bpmn:endEvent id="Event_14hz1od">\n' +
      '      <bpmn:incoming>Flow_0ns6j4c</bpmn:incoming>\n' +
      '    </bpmn:endEvent>\n' +
      '    <bpmn:sequenceFlow id="Flow_0ns6j4c" sourceRef="Activity_1edo5u3" targetRef="Event_14hz1od" />\n' +
      '    <bpmn:userTask id="Activity_1edo5u3" name="Tarea de prueba">\n' +
      '      <bpmn:extensionElements>\n' +
      '        <camunda:formData>\n' +
      '          <camunda:formField id="name" label="Nombre" type="string" />\n' +
      '          <camunda:formField id="lastName" label="Apellido" type="string" />\n' +
      '        </camunda:formData>\n' +
      '      </bpmn:extensionElements>\n' +
      '      <bpmn:incoming>Flow_1aslf5z</bpmn:incoming>\n' +
      '      <bpmn:outgoing>Flow_0ns6j4c</bpmn:outgoing>\n' +
      '    </bpmn:userTask>\n' +
      '  </bpmn:process>\n' +
      '  <bpmndi:BPMNDiagram id="BPMNDiagram_1">\n' +
      '    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Collaboration_0wd5l4r">\n' +
      '      <bpmndi:BPMNShape id="Participant_0tj95nj_di" bpmnElement="Participant_0tj95nj" isHorizontal="true">\n' +
      '        <dc:Bounds x="129" y="70" width="361" height="200" />\n' +
      '      </bpmndi:BPMNShape>\n' +
      '      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">\n' +
      '        <dc:Bounds x="179" y="159" width="36" height="36" />\n' +
      '      </bpmndi:BPMNShape>\n' +
      '      <bpmndi:BPMNShape id="Event_14hz1od_di" bpmnElement="Event_14hz1od">\n' +
      '        <dc:Bounds x="432" y="159" width="36" height="36" />\n' +
      '      </bpmndi:BPMNShape>\n' +
      '      <bpmndi:BPMNShape id="Activity_04a14bh_di" bpmnElement="Activity_1edo5u3">\n' +
      '        <dc:Bounds x="270" y="137" width="100" height="80" />\n' +
      '        <bpmndi:BPMNLabel />\n' +
      '      </bpmndi:BPMNShape>\n' +
      '      <bpmndi:BPMNEdge id="Flow_1aslf5z_di" bpmnElement="Flow_1aslf5z">\n' +
      '        <di:waypoint x="215" y="177" />\n' +
      '        <di:waypoint x="270" y="177" />\n' +
      '      </bpmndi:BPMNEdge>\n' +
      '      <bpmndi:BPMNEdge id="Flow_0ns6j4c_di" bpmnElement="Flow_0ns6j4c">\n' +
      '        <di:waypoint x="370" y="177" />\n' +
      '        <di:waypoint x="432" y="177" />\n' +
      '      </bpmndi:BPMNEdge>\n' +
      '    </bpmndi:BPMNPlane>\n' +
      '  </bpmndi:BPMNDiagram>\n' +
      '</bpmn:definitions>\n'
  }
}

When I try to deploy I get this error:

message: "Camunda: No file name found in the deployment resource described by form parameter 'null'.",

I understand that I am not passing you a file as such; but the xml and therefore I have no name but how could I solve this problem?

Thank you in advance

Provide a unique filename for the file to be known as during the deployment
For example, generate a UUID and append .bpmn to the end of it.

Ok but in wich part? Sorry, I’m newby

Can’t tell you how to do it in the JS that you’re using…
But for C7 (which is where you’ve posted this topic) you’ll want to look at

Content-Disposition: form-data; name="data"; filename="test.bpmn"

Which toolset / client library are you using to create the “camundaClient.createDeployment” ?
The underlying REST library must have a way to support manually specifying the filename…

I’m currently using a NestJs backend with TypeScript (also TypeOrm) but the most relevant thing in the case is that I’m using OpenAPI. This is the code snippet where I have the camundaClient:

export class CamundaService {
    private client: Camunda;

    async initCamundaClient(): Promise<void> {
        try {
            const api = new OpenAPIClientAxios({ definition: 'http://localhost:8080/swaggerui/openapi.json' });
            api.init();
            this.client = await api
                .getClient<Camunda>()
                .catch((error) => {
                    throw new InternalServerErrorException(error);
                });
        } catch (error) {
            throw new InternalServerErrorException(error);
        }
    }

    async camundaClient(): Promise<Client> {
        await this.initCamundaClient();
        if (!this.client) {
            throw new InternalServerErrorException('Camunda client not initialized. Call initCamundaClient first.');
        }
        return this.client;
    }
}

The biggest step to digging down into what is going on is tracing the files that you’re importing (which appears to perhaps be OpenAPI-Client-Axios)

Then the next step is figuring out how to define the filename within that library… but really that’s not a Camunda thing, but something at the library level.

Also, where does this.filesService.findBuffer come from?
Is it possible that Axios is able to cast this to the appropriate type directly if it’s passed as the data argument, rather than calling a toString on it?

this.filesService.findBuffer is a function used to obtain the buffer of a stored file. In my case I manage to retrieve the xml. You can see in data property:

{
  camundaData: {
    'tenant-id': null,
    'deploy-changed-only': null,
    'deployment-activation-time': null,
    'deployment-name': 'NGG',
    'deployment-source': null,
    'enable-duplicate-filtering': null,
    data: '<?xml version="1.0" encoding="UTF-8"?>\n' +
      '<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_0zwqm3h" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.16.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.20.0">\n' +
      '  <bpmn:collaboration id="Collaboration_0wd5l4r">\n' +
      '    <bpmn:participant id="Participant_0tj95nj" processRef="probando_cargue" />\n' +
      '  </bpmn:collaboration>\n' +
      '  <bpmn:process id="probando_cargue" isExecutable="true" camunda:historyTimeToLive="180">\n' +
      '    <bpmn:startEvent id="StartEvent_1">\n' +
      '      <bpmn:outgoing>Flow_1aslf5z</bpmn:outgoing>\n' +
      '    </bpmn:startEvent>\n' +
      '    <bpmn:sequenceFlow id="Flow_1aslf5z" sourceRef="StartEvent_1" targetRef="Activity_1edo5u3" />\n' +
      '    <bpmn:endEvent id="Event_14hz1od">\n' +
      '      <bpmn:incoming>Flow_0ns6j4c</bpmn:incoming>\n' +
      '    </bpmn:endEvent>\n' +
      '    <bpmn:sequenceFlow id="Flow_0ns6j4c" sourceRef="Activity_1edo5u3" targetRef="Event_14hz1od" />\n' +
      '    <bpmn:userTask id="Activity_1edo5u3" name="Tarea de prueba">\n' +
      '      <bpmn:extensionElements>\n' +
      '        <camunda:formData>\n' +
      '          <camunda:formField id="name" label="Nombre" type="string" />\n' +
      '          <camunda:formField id="lastName" label="Apellido" type="string" />\n' +
      '        </camunda:formData>\n' +
      '      </bpmn:extensionElements>\n' +
      '      <bpmn:incoming>Flow_1aslf5z</bpmn:incoming>\n' +
      '      <bpmn:outgoing>Flow_0ns6j4c</bpmn:outgoing>\n' +
      '    </bpmn:userTask>\n' +
      '  </bpmn:process>\n' +
      '  <bpmndi:BPMNDiagram id="BPMNDiagram_1">\n' +
      '    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Collaboration_0wd5l4r">\n' +
      '      <bpmndi:BPMNShape id="Participant_0tj95nj_di" bpmnElement="Participant_0tj95nj" isHorizontal="true">\n' +
      '        <dc:Bounds x="129" y="70" width="361" height="200" />\n' +
      '      </bpmndi:BPMNShape>\n' +
      '      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">\n' +
      '        <dc:Bounds x="179" y="159" width="36" height="36" />\n' +
      '      </bpmndi:BPMNShape>\n' +
      '      <bpmndi:BPMNShape id="Event_14hz1od_di" bpmnElement="Event_14hz1od">\n' +
      '        <dc:Bounds x="432" y="159" width="36" height="36" />\n' +
      '      </bpmndi:BPMNShape>\n' +
      '      <bpmndi:BPMNShape id="Activity_04a14bh_di" bpmnElement="Activity_1edo5u3">\n' +
      '        <dc:Bounds x="270" y="137" width="100" height="80" />\n' +
      '        <bpmndi:BPMNLabel />\n' +
      '      </bpmndi:BPMNShape>\n' +
      '      <bpmndi:BPMNEdge id="Flow_1aslf5z_di" bpmnElement="Flow_1aslf5z">\n' +
      '        <di:waypoint x="215" y="177" />\n' +
      '        <di:waypoint x="270" y="177" />\n' +
      '      </bpmndi:BPMNEdge>\n' +
      '      <bpmndi:BPMNEdge id="Flow_0ns6j4c_di" bpmnElement="Flow_0ns6j4c">\n' +
      '        <di:waypoint x="370" y="177" />\n' +
      '        <di:waypoint x="432" y="177" />\n' +
      '      </bpmndi:BPMNEdge>\n' +
      '    </bpmndi:BPMNPlane>\n' +
      '  </bpmndi:BPMNDiagram>\n' +
      '</bpmn:definitions>\n'
  }
}

Yes, but what library provides it?
The object produced by that library might embed the filename, which Axios might just handle correctly. But again - this is WAY outside the Camunda scope

Looking at the Axios/OpenAPI doc you can replace your FormData JSON block with a FormData element, which would allow you to use a formData.append(data, filecontent, ‘filename.bpmn’) to specify the filename.

Hello @GotnOGuts, I have tried with this code:

            const formData = new FormData();

            formData.append('tenant-id', tenantId);
            formData.append('deploy-changed-only', deployChangedOnly ? deployChangedOnly.toString(): null);
            formData.append('deployment-activation-time', deploymentActivationTime);
            formData.append('deployment-name', deploymentName);
            formData.append('deployment-source', deploymentSource);
            formData.append('enable-duplicate-filtering', enableDuplicateFiltering ? enableDuplicateFiltering.toString() : null);

            if(fileId) {
                const fileContent = (await this.filesService.findBuffer({ user: undefined }, fileId));

                const blob = new Blob ([fileContent]);

                formData.append('data', blob, 'filename.bpmn20.xml');
            }
            
            await camundaClient.createDeployment(null, formData as any, {
                headers: {
                    "Content-Type": "multipart/form-data",
                }
            })

But it does not work. I got this new error:

{
  type: 'IllegalArgumentException',
  message: 'Invalid format: "null"',
  code: null
}

Maybe the error is that he expects a string and I am passing him a Blob:


        export interface MultiFormDeploymentDto {
            /**
             * The tenant id for the deployment to be created.
             */
            "tenant-id"?: string | null;
            /**
             * The source for the deployment to be created.
             */
            "deployment-source"?: string | null;
            /**
             * A flag indicating whether the process engine should perform duplicate checking on a per-resource basis.
             * If set to true, only those resources that have actually changed are deployed.
             * Checks are made against resources included previous deployments of the same name and only against the latest versions of those resources.
             * If set to true, the option enable-duplicate-filtering is overridden and set to true.
             */
            "deploy-changed-only"?: boolean | null;
            /**
             * A flag indicating whether the process engine should perform duplicate checking for the deployment or not.
             * This allows you to check if a deployment with the same name and the same resouces already exists and
             * if true, not create a new deployment but instead return the existing deployment. The default value is false.
             */
            "enable-duplicate-filtering"?: boolean | null;
            /**
             * The name for the deployment to be created.
             */
            "deployment-name"?: string | null;
            /**
             * Sets the date on which the process definitions contained in this deployment will be activated. This means that all process
             * definitions will be deployed as usual, but they will be suspended from the start until the given activation date.
             * By [default](https://docs.camunda.org/manual/7.19/reference/rest/overview/date-format/),
             * the date must have the format `yyyy-MM-dd'T'HH:mm:ss.SSSZ`, e.g., `2013-01-23T14:42:45.000+0200`.
             */
            "deployment-activation-time"?: string | null; // date-time
            /**
             * The binary data to create the deployment resource.
             * It is possible to have more than one form part with different form part names for the binary data to create a deployment.
             */
            data?: string | null; // binary
        }

Hello everyone!

I did not manage to perform deployments using OpenAPI. However, I solved the situation by using the API provided by Camunda and sending the information through FormData.

I hope this experience will be useful for everyone.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.