Project Development

The following chapters describe the tasks to support the project development.

We provide a helper.scala script that helps you with the most common tasks.

In general, you can type ./helper.scala x to get a list of available commands.

And then you can type ./helper.scala <command> to get help for a specific command.

The version is optional and defaults to 1.

helper.scala

This file will be replaced with ./helper.scala update. However, you need to set there the subprojects you want to use.

#!/usr/bin/env -S scala shebang
// DO NOT ADJUST. This file is replaced by `./helper.scala update`.
//> using dep mycompany::mycompany-orchescala-helper:0.1.0-SNAPSHOT

import mycompany.orchescala.helper.*

lazy val projectName: String = "mycompany-myProject"
lazy val subProjects = Seq(
  "accounting",
  "hr"
)

@main
def run(command: String, arguments: String*): Unit =
  CompanyDevHelper(projectName, subProjects).run(command, arguments*)

subProjects

Compile time can be optimized by using subprojects - this makes the project a bit more complex, as for each subProject, a SBT module is created.

./helper.scala update will generate this file but preserve the project name and subprojects.

update

Whenever you have changes in the company-orchescala project or in one of your dependencies, you can update the project with the following command:

Usage / example:

./helper.scala update

This will create or update your project with the latest changes.

Files that contain the DO NOT ADJUST comment will be replaced. If you do adjust them, remove this comment. You will get a warning, but the file will not be replaced.

publish

Creates a new Release for the BPMN project and publishes to the repository(e.g. Artifactory)

If you want to provide the documentation on a WebDAV server, you need a CompanyDevHelper.devConfig.publishConfig configuration.

Usage:

./helper.scala publish <VERSION>

Example:

./helper.scala publish 0.2.5

The following steps are executed:

deploy

Deploys the BPMN project to the local Camunda server and runs the Simulation you're working on.

Be aware that CompanyDevHelper.devConfig.postmanConfig must be defined.

At the moment, only deployment via Postman Collection is supported (using Camunda REST API to deploy).

Usage:

./helper.scala deploy [simulation]

Example:

./helper.scala deploy MyProcessSimulation

The following steps are executed:

Generate Process/-Elements

To handle name conventions and to avoid errors, we generate as much as possible.

So it is essential, not to change the generated names.

The following generators are provided:

process

Creates a new Process.

Usage:

./helper.scala process <processName> [version: Int]

Example:

./helper.scala process myProcess 1

This creates the following files:

// the BPMN
src           - main -> myproject-myProcessV1.bpmn
// the domain In -> Out
02-bpmn       - main -> mycompany.myproject.bpmn.myProcess.v1.MyProcess      
// the Simulation        
03-simulation - test -> mycompany.myproject.simulation.MyProcessSimulation           
// the InitWorker    
03-worker     - main -> mycompany.myproject.worker.myProcess.v1.MyProcessWorker  
              - test -> mycompany.myproject.worker.myProcess.v1.MyProcessWorkerTest 

processFromSpec

Creates a Process from the exports of Orch Spec - the BPMN and the Scala classes.

Usage:

./helper.scala processFromSpec [bpmnExport] [scalaExport] | orchspec:…

The simplest way: in Orch Spec Export > Process from Spec - it copies the whole command with the BPMN and the Scala classes in one argument (orchspec: + base64url(gzip(JSON))). Paste it in the terminal of your project and press Enter:

./helper.scala processFromSpec orchspec:H4sIAPO9vGoCA-0cW3IbN_IqKCW1ll…

No files and no SharePoint login needed - the command contains everything.

Without arguments, the helper asks for the exports - copy them in Orch Spec (Export > BPMN / Scala > Kopieren):

./helper.scala processFromSpec
1/2 BPMN - in Orch Spec: Export > BPMN > Kopieren, then press Enter here (or paste it here):
  ✓ myproject-myProcessV1 (Camunda 7)
2/2 Scala classes - in Orch Spec: Export > Scala > Kopieren, then press Enter here (or paste it here - finish with a line END):
  ✓ 5 files

Enter takes the export from the clipboard (macOS: pbpaste, Windows: Get-Clipboard, Linux: wl-paste/xclip). You can also paste it into the terminal - but a terminal may cut long lines of a paste, so the clipboard is safer.

Or with the exported files:

./helper.scala processFromSpec ~/Downloads/myproject-myProcess-bpmn-c7.bpmn ~/Downloads/myproject-myProcess-scala.scala

The names (process, version, object) come from the process id of the BPMN - the same way Orch Spec derives them (myproject-myProcessV2 -> myProcess, v2, MyProcessV2).

This creates the same files as process, but with the content of the specification:

// the BPMN - to camunda8 if it is a Camunda 8 diagram; without BPMN export the template
src           - main -> myproject-myProcessV1.bpmn
// the domain - In, Out, InConfig, InitIn of the specification (In gets the inConfig)
01-domain     - main -> mycompany.myproject.domain.myProcess.v1.MyProcess
// the interactions (UserTasks, CustomTasks, Signals, Messages) and the classes in schema/
01-domain     - main -> mycompany.myproject.domain.myProcess.v1.MyUserTaskUT
                        mycompany.myproject.domain.myProcess.v1.schema.MyClass
// the Simulation
03-simulation - test -> mycompany.myproject.simulation.MyProcessSimulation
// the InitWorker and a Worker for each CustomTask, Signal and Message
03-worker     - main -> mycompany.myproject.worker.myProcess.v1.MyProcessWorker
              - test -> mycompany.myproject.worker.myProcess.v1.MyProcessWorkerTest

In gets the inConfig (extends WithConfig[InConfig]) - an enum In in each of its cases.

Defaults are only in the InConfig. An optional field of In with a default in Orch Spec stays an Option - the InitIn gets the same field as required, and the InitWorker sets it in customInit:

override def customInit(in: In): InitIn =
  InitIn(
    fee = in.fee.getOrElse(90)
  )

The process is registered in the WorkerApp and in the ApiProjectCreator:

object WorkerApp extends CompanyWorkerApp:
  workers(
    myProcessWorkers,
    ..
  )
  ..
  private lazy val myProcessWorkers =
    import mycompany.myproject.worker.myProcess.v1.*
    Seq(
      MyProcessWorker(),
      MyCustomTaskWorker(),
    )
  end myProcessWorkers
end WorkerApp
object ApiProjectCreator extends CompanyApiCreator:
  ..
  document(
    myProcessApi,
    ..
  )
  ..
  private lazy val myProcessApi =
    import mycompany.myproject.domain.myProcess.v1.*
    api(MyProcess.example)(
      MyUserTaskUT.example,
      MyCustomTask.example,
    )
  end myProcessApi
end ApiProjectCreator

The entry and the block go in alphabetically - next to their alphabetical neighbour, the order of the others stays (in document(…) it is the order of the API documentation).

If one of them has not this form (workers( / document( and end WorkerApp / end ApiProjectCreator), the snippet is printed - add it manually.

The package of the Scala classes must be <projectPackage>.domain.<processName>.v<version> - so company and project of the process in Orch Spec must match the project. If it differs from the names of the BPMN, you get a warning (the Scala classes win).

Existing files are never overwritten - so you can run it again after changes in Orch Spec:

To take a new version of a class or of the BPMN: delete it and run the command again - or merge it manually.

customTask

Creates a new Custom Task.

Usage:

./helper.scala customTask <processName> <bpmnName> [version: Int]

Example:

./helper.scala customTask myProcess MyCustomTask 1

This creates the following files:

// the domain In -> Out
02-bpmn   - main -> mycompany.myproject.bpmn.myProcess.v1.MyCustomTask   
// the CustomWorker
03-worker - main -> mycompany.myproject.worker.myProcess.v1.MyCustomTaskWorker  
          - test -> mycompany.myproject.worker.myProcess.v1.MyCustomTaskWorkerTest    

serviceTask

Creates a new Service Task.

Usage:

./helper.scala serviceTask <processName> <bpmnName> [version: Int]

Example:

./helper.scala serviceTask myProcess MyServiceTask 1

This creates the following files:

// the domain In -> Out (ServiceIn -> ServiceOut)
02-bpmn   - main -> mycompany.myproject.bpmn.myProcess.v1.MyServiceTask
// the ServiceWorker
03-worker - main -> mycompany.myproject.worker.myProcess.v1.MyServiceTaskWorker  
          - test -> mycompany.myproject.worker.myProcess.v1.MyServiceTaskWorkerTest    

userTask

Creates a new User Task.

Usage:

./helper.scala userTask <processName> <bpmnName> [version: Int]

Example:

./helper.scala userTask myProcess MyUserTask 1

This creates the following files:

// the domain In -> Out
02-bpmn - main -> mycompany.myproject.bpmn.myProcess.v1.MyUserTask      

decision

Creates a new Decision.

Usage:

./helper.scala decision <processName> <bpmnName> [version: Int]

Example:

./helper.scala decision myProcess MyDecision 1

This creates the following files:

// the domain In -> Out
02-bpmn - main -> mycompany.myproject.bpmn.myProcess.v1.MyDecision      

signalEvent

Creates a new Signal Event.

Usage:

./helper.scala signalEvent <processName> <bpmnName> [version: Int]

Example:

./helper.scala signalEvent myProcess MySignalEvent 1

This creates the following files:

// the domain In -> NoOutput
02-bpmn - main -> mycompany.myproject.bpmn.myProcess.v1.MySignalEvent   
// the ValidationWorker    
03-worker     - main -> mycompany.myproject.worker.myProcess.v1.MySignalEventWorker  
              - test -> mycompany.myproject.worker.myProcess.v1.MySignalEventWorkerTest 

messageEvent

Creates a new Message Event.

Usage:

./helper.scala messageEvent <processName> <bpmnName> [version: Int]

Example:

./helper.scala messageEvent myProcess MyMessageEvent 1

This creates the following files:

// the domain In -> NoOutput
02-bpmn - main -> mycompany.myproject.bpmn.myProcess.v1.MyMessageEvent   
// the ValidationWorker    
03-worker     - main -> mycompany.myproject.worker.myProcess.v1.MyMessageEventWorker  
              - test -> mycompany.myproject.worker.myProcess.v1.MyMessageEventWorkerTest 

timerEvent

Creates a new Timer Event.

Usage:

./helper.scala timerEvent <processName> <bpmnName> [version: Int]

Example:

./helper.scala timerEvent myProcess MyTimerEvent 1

This creates the following files:

// the domain NoInput -> NoOutput
02-bpmn - main -> mycompany.myproject.bpmn.myProcess.v1.MyTimerEvent      

Docker

To run the Camunda Server locally, you can use docker-compose.

Precondition:

  • You have to have docker and docker-compose installed.
  • You need to have a docker-compose.yml in dev-company/docker directory.
  • Adjust the CompanyDevHelper.devConfig.dockerConfig configuration.

dockerUp

Starts the server with docker-compose.

Check your company settings in DockerHelper before running this command.

Usage / example:

./helper.scala dockerUp

dockerStop

Stops the server with docker-compose.

Usage / example:

./helper.scala dockerStop

dockerDown

Stops and removes the server with docker-compose.

Be aware that all data will be lost - you have to deploy again.

Usage / example:

./helper.scala dockerDown