[BPMN] Service Task with REST-API (PATCH) Example

หลังจาก Blog ตอนก่อน ผมได้ลอง Service Task เชื่อมกับ Web Service ผ่านวิธีการ GET เพื่อที่ดึงข้อมูลมาแสดงผลครับ คราวนี้หลังจาก GET ข้อมูลไปแล้วคราวนี้ เราลองมาทำการแก้ไขข้อมูลครับ ซึ่งการแก้ไขข้อมูลบางส่วน อันนี้ทาง Web Service (REST API) เค้ามีวิธีการที่เรียกว่า PATCH ครับ ส่วนจะทำอย่างไรนั้นมาลุยกันเลยครับ

เตรียมตัวครับ

กระบวนการที่สร้างกันก่อนครับ

  • สำหรับกระบวนการที่สร้างคราวนี้ผมทั้ง JSON Server ขึ้นมาเองครับ โดยใช้ข้อมูล Request ซึ่งผู้ใช้ต้องใส่ ID เพื่อให้ระบบดึงข้อมูล Request ขึ่นมาครับ หลังจากดูเสร็จ แล้วกด Complete ข้อมูลของ Request ในช่อง "requeststatus" ถูกแก้ไขค่าจาก "wait-for-review" เป็น "completed" ครับ

ลองมือทำ โดยมี Task ที่เกี่ยวข้อง ดังนี้

  • Task "Enter Request Id" เป็น User Task เอาไว้สำหรับกรอก Id ของ Request ครับ
  • Task "Test REST-API (GET)" เป็น Service Task ที่ติดต่อกับ Web Service โดยมี Config ดังนี้
    • Connector Id = "http-connector"
    • Input มีค่าตามตารางด้านล่างครับ
      ์NameTypeScript FormatData
      urlscriptgroovy  "http://localhost:3000/requests/${RequestId}".toString()
      methodtext-  GET
      headermap-  Key = "Accept"
      Value = "application/json"
    • Output เนื่องจากข้อมูลที่ได้เป็นอยู่ในรูปแบบ JSON จึงต้องมีการแปลงค่า และยัดลง Process ของ BPMN ได้แก่ id, projectid, requestid, requestby และ requeststatus ครับ โดยมี Code ดังนี้
      import groovy.json.JsonSlurper;
      
      jsonResponse= connector.getVariable("response");
      out:println "jsonResponse :" + jsonResponse.getClass();
      
      JsonSlurper jsonSlurper = new JsonSlurper();
      Object result = jsonSlurper.parseText(jsonResponse);
      //Convert to MAP
      Map jsonResponseMap  = (Map)result ;
      
      //Step Value to Process Engine
      connector.setVariable('id', jsonResponseMap.get("id"));
      connector.setVariable('projectId', jsonResponseMap.get("projectId"));
      connector.setVariable('requestId', jsonResponseMap.get("requestId"));
      connector.setVariable('requestby', jsonResponseMap.get("requestby"));
      connector.setVariable('requeststatus', jsonResponseMap.get("requeststatus"));
      
      //Return JSON to outpur variable
      S(result);
  • Task "View Result" เป็น User Task ที่เอาผลลัพธ์ที่ได้จาก Task "Test REST-API (GET)" มาแสดงผลครับ
    • Connector Id = "http-connector"
    • Input มีค่าตามตาราง
      ์NameTypeScript FormatData
      urlscriptgroovy  "http://localhost:3000/requests/${RequestId}".toString()
      methodtext-  GET
      headermap-  Key = "Accept"
      Value = "application/json"
        Key ="Content-Type"
      Value = "application/json"
      payloadscriptgroovy
      import groovy.json.JsonOutput;
      def payload = [ 'requeststatus': 'completed' ];
      JsonOutput.toJson(payload);
  • Task "REST-API(PATCH) request status" เป็น Service Task ที่ทำหน้าที่เปลี่ยน "requeststatus" ถูกแก้ไขค่าจาก "wait-for-review" เป็น "completed" ครับ โดยมี Config ดังนี้ครับ
  • เมื่อสร้าง Task เสร็จแล้ว นำไฟล์ BPMN ที่ได้เตรียมไป Deploy ครับ
    <?xml version="1.0" encoding="UTF-8"?>
    <bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="1.11.3">
    <bpmn:process id="HttpConnectorRequestPatch" name="HttpConnectorRequestPatch" isExecutable="true" camunda:versionTag="1">
    <bpmn:startEvent id="StartEvent_1">
    <bpmn:outgoing>SequenceFlow_1435fpg</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:sequenceFlow id="SequenceFlow_1435fpg" sourceRef="StartEvent_1" targetRef="Task_0zq32cv" />
    <bpmn:endEvent id="EndEvent_186wp1n">
    <bpmn:incoming>SequenceFlow_1vry191</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="SequenceFlow_03p4qxe" sourceRef="Task_1dkkumo" targetRef="Task_1sfjjen" />
    <bpmn:serviceTask id="Task_1dkkumo" name="Test &#10;REST-API (GET)&#10;">
    <bpmn:extensionElements>
    <camunda:connector>
    <camunda:inputOutput>
    <camunda:inputParameter name="url">
    <camunda:script scriptFormat="groovy"><![CDATA["http://localhost:3000/requests/${RequestId}".toString()
    ]]></camunda:script>
    </camunda:inputParameter>
    <camunda:inputParameter name="method">GET</camunda:inputParameter>
    <camunda:inputParameter name="header">
    <camunda:map>
    <camunda:entry key="Accept">application/json</camunda:entry>
    </camunda:map>
    </camunda:inputParameter>
    <camunda:outputParameter name="postResult">
    <camunda:script scriptFormat="groovy"><![CDATA[import groovy.json.JsonSlurper;
    jsonResponse= connector.getVariable("response");
    out:println "jsonResponse :" + jsonResponse.getClass();
    JsonSlurper jsonSlurper = new JsonSlurper();
    Object result = jsonSlurper.parseText(jsonResponse);
    //Convert to MAP
    Map jsonResponseMap = (Map)result ;
    //Step Value to Process Engine
    connector.setVariable('id', jsonResponseMap.get("id"));
    connector.setVariable('projectId', jsonResponseMap.get("projectId"));
    connector.setVariable('requestId', jsonResponseMap.get("requestId"));
    connector.setVariable('requestby', jsonResponseMap.get("requestby"));
    connector.setVariable('requeststatus', jsonResponseMap.get("requeststatus"));
    //Return JSON to outpur variable
    postResult = result;]]></camunda:script>
    </camunda:outputParameter>
    </camunda:inputOutput>
    <camunda:connectorId>http-connector</camunda:connectorId>
    </camunda:connector>
    </bpmn:extensionElements>
    <bpmn:incoming>SequenceFlow_13f8xbw</bpmn:incoming>
    <bpmn:outgoing>SequenceFlow_03p4qxe</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:userTask id="Task_1sfjjen" name="View Result" camunda:assignee="mary">
    <bpmn:extensionElements>
    <camunda:formData>
    <camunda:formField id="projectId" label="projectId" type="string" />
    <camunda:formField id="requestId" label="requestId" type="string" />
    <camunda:formField id="requestby" label="requestby" type="string" />
    <camunda:formField id="requeststatus" label="requeststatus" type="string" />
    <camunda:formField id="id" label="id" type="string" />
    </camunda:formData>
    </bpmn:extensionElements>
    <bpmn:incoming>SequenceFlow_03p4qxe</bpmn:incoming>
    <bpmn:outgoing>SequenceFlow_1x13z2l</bpmn:outgoing>
    </bpmn:userTask>
    <bpmn:sequenceFlow id="SequenceFlow_13f8xbw" sourceRef="Task_0zq32cv" targetRef="Task_1dkkumo" />
    <bpmn:userTask id="Task_0zq32cv" name="Enter Request Id" camunda:assignee="mary">
    <bpmn:extensionElements>
    <camunda:formData>
    <camunda:formField id="RequestId" label="RequestId" type="long" defaultValue="2" />
    </camunda:formData>
    </bpmn:extensionElements>
    <bpmn:incoming>SequenceFlow_1435fpg</bpmn:incoming>
    <bpmn:outgoing>SequenceFlow_13f8xbw</bpmn:outgoing>
    </bpmn:userTask>
    <bpmn:serviceTask id="Task_1y1oxu5" name="REST-API&#10;(PATCH)&#10;request status">
    <bpmn:extensionElements>
    <camunda:connector>
    <camunda:inputOutput>
    <camunda:inputParameter name="url">
    <camunda:script scriptFormat="groovy"><![CDATA["http://localhost:3000/requests/${id}".toString()]]></camunda:script>
    </camunda:inputParameter>
    <camunda:inputParameter name="method">PATCH</camunda:inputParameter>
    <camunda:inputParameter name="header">
    <camunda:map>
    <camunda:entry key="accept">application/json</camunda:entry>
    <camunda:entry key="Content-Type">application/json</camunda:entry>
    </camunda:map>
    </camunda:inputParameter>
    <camunda:inputParameter name="payload">
    <camunda:script scriptFormat="groovy"><![CDATA[import groovy.json.JsonOutput;
    def payload = [ 'requeststatus': 'completed' ];
    //JsonOutput.prettyPrint(JsonOutput.toJson(payload));
    JsonOutput.toJson(payload);]]></camunda:script>
    </camunda:inputParameter>
    <camunda:outputParameter name="statusCode">
    <camunda:script scriptFormat="groovy"><![CDATA[out:println "response:" + response;
    out:println "statusCode:" + statusCode;]]></camunda:script>
    </camunda:outputParameter>
    </camunda:inputOutput>
    <camunda:connectorId>http-connector</camunda:connectorId>
    </camunda:connector>
    </bpmn:extensionElements>
    <bpmn:incoming>SequenceFlow_1x13z2l</bpmn:incoming>
    <bpmn:outgoing>SequenceFlow_1vry191</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:sequenceFlow id="SequenceFlow_1x13z2l" sourceRef="Task_1sfjjen" targetRef="Task_1y1oxu5" />
    <bpmn:sequenceFlow id="SequenceFlow_1vry191" sourceRef="Task_1y1oxu5" targetRef="EndEvent_186wp1n" />
    </bpmn:process>
    <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="HttpConnectorRequestPatch">
    <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
    <dc:Bounds x="-101" y="94" width="36" height="36" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="-128" y="130" width="90" height="20" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNShape>
    <bpmndi:BPMNEdge id="SequenceFlow_1435fpg_di" bpmnElement="SequenceFlow_1435fpg">
    <di:waypoint xsi:type="dc:Point" x="-65" y="112" />
    <di:waypoint xsi:type="dc:Point" x="-35" y="112" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="-95" y="91" width="90" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNEdge>
    <bpmndi:BPMNShape id="EndEvent_186wp1n_di" bpmnElement="EndEvent_186wp1n">
    <dc:Bounds x="491" y="94" width="36" height="36" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="464" y="134" width="90" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNShape>
    <bpmndi:BPMNEdge id="SequenceFlow_03p4qxe_di" bpmnElement="SequenceFlow_03p4qxe">
    <di:waypoint xsi:type="dc:Point" x="192" y="112" />
    <di:waypoint xsi:type="dc:Point" x="225" y="112" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="163.5" y="91" width="90" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNEdge>
    <bpmndi:BPMNShape id="ServiceTask_01g1tty_di" bpmnElement="Task_1dkkumo">
    <dc:Bounds x="92" y="72" width="100" height="80" />
    </bpmndi:BPMNShape>
    <bpmndi:BPMNShape id="UserTask_0dz9bh6_di" bpmnElement="Task_1sfjjen">
    <dc:Bounds x="225" y="72" width="100" height="80" />
    </bpmndi:BPMNShape>
    <bpmndi:BPMNEdge id="SequenceFlow_13f8xbw_di" bpmnElement="SequenceFlow_13f8xbw">
    <di:waypoint xsi:type="dc:Point" x="65" y="112" />
    <di:waypoint xsi:type="dc:Point" x="92" y="112" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="33.5" y="91" width="90" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNEdge>
    <bpmndi:BPMNShape id="UserTask_03xo9g6_di" bpmnElement="Task_0zq32cv">
    <dc:Bounds x="-35" y="72" width="100" height="80" />
    </bpmndi:BPMNShape>
    <bpmndi:BPMNShape id="ServiceTask_07lxnv4_di" bpmnElement="Task_1y1oxu5">
    <dc:Bounds x="352" y="72" width="100" height="80" />
    </bpmndi:BPMNShape>
    <bpmndi:BPMNEdge id="SequenceFlow_1x13z2l_di" bpmnElement="SequenceFlow_1x13z2l">
    <di:waypoint xsi:type="dc:Point" x="325" y="112" />
    <di:waypoint xsi:type="dc:Point" x="352" y="112" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="338.5" y="91" width="0" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNEdge>
    <bpmndi:BPMNEdge id="SequenceFlow_1vry191_di" bpmnElement="SequenceFlow_1vry191">
    <di:waypoint xsi:type="dc:Point" x="452" y="112" />
    <di:waypoint xsi:type="dc:Point" x="491" y="112" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="426.5" y="91" width="90" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
    </bpmndi:BPMNDiagram>
    </bpmn:definitions>

ทดสอบ

  • เตรียมข้อมูลสำหรับ Mock Server กันก่อนครับ โดยข้อมูลที่ได้มีหน้าตา ดังนี้ (ถูกเก็บไว้ในไฟล์ db.json ครับ)
    {
      "requests": [
        {
          "id": 1,
          "projectId": 123,
          "requestId": "CUQ-5248",
          "requestby": "mary",
          "requesttime": "2018-05-23T18:25:43.511Z",
          "requeststatus": "wait-for-review",
        },
        {
          "id": 2,
          "projectId": 124,
          "requestId": "CUQ-5249",
          "requestby": "mary",
          "requesttime": "2018-05-23T18:25:43.511Z",
          "requeststatus": "wait-for-review",
        },
        {
          "id": 3,
          "projectId": 124,
          "requestId": "CUQ-52489",
          "requestby": "mary",
          "requesttime": "2018-05-23T18:25:43.511Z",
          "requeststatus": "wait-for-review",
        }
      ]
    }
  • Start JSON Server ด้วยคำสั่ง json-server --watch db.json
  • เข้าไปที่ Camunda Task List จากนั้น Execute Task ชื่อ "HttpConnectorRequestPatch"
    • กรอก Request Id = 2
    • ดูรายการ Request จากนั้นกดปุ่ม Complete
    • ลองไปดูที่ Console ครับ พบว่า ช่อง "requeststatus" ถูกแก้ไขค่าจาก "wait-for-review" เป็น "completed" แล้วครับ