paed01 / bpmn-engine

BPMN 2.0 execution engine. Open source javascript workflow engine.
MIT License
860 stars 166 forks source link

bpmn-engine as an express service #156

Closed rusaym closed 1 year ago

rusaym commented 2 years ago

Hi! Can you please give me a suggestion, I am trying to implement a workflow server, using "express" and "bpmn-engine". So let's consider a process: image

  1. I have a userTask, that the user can "signal" and the process will end. 
  2. I have a sequenseFlow with a timer, that has to be taken if a user hasn't "signal" in 2 hours and than the process will end.

Here is how I am trying to implement my server:

  1. Implementing endpoint that creates and executes an engine, saving state to the database.
app.post('/new', async function (req, res) {
  const engine = new Engine({
    name: 'Test proccess',
    source: fs.readFileSync('./app8.bpmn'),
    moddleOptions: {
      camunda: require('camunda-bpmn-moddle/resources/camunda'),
    },
  })

  const listener = new EventEmitter()

  listener.once('activity.wait', async (activity) => {
    engine.stop() // Do I need to stop the engine before getting state?
    const state = await engine.getState()

    const data = await saveState(JSON.stringify(state))
    res.json(data)
  })

  engine.execute({
    listener: listener,
  })
})
  1. Implementing endpoint that “signals” the UserTask:

    app.post('/resume', async function (req, res) {
    const { id, decision } = req.body
    
    const { rows } = await readState(id)
    
    let state = JSON.parse(rows[0].state)
    
    const engine = new Engine({
    moddleOptions: {
      camunda: require('camunda-bpmn-moddle/resources/camunda'),
    },
    }).recover(state)
    
    const listener = new EventEmitter()
    
    listener.once('activity.wait', async (activity) => {
    activity.signal({
      decision,
    })
    })
    
    engine.resume(
    {
      listener,
    },
    async (err, execution) => {
      if (err) throw err
      state = await execution.getState()
      await updateState(id, JSON.stringify(state))
    
      console.log('PROCESS ENDED')
      res.send()
    }
    )
    })

    So everything is ok if the user is “pushing the button”, but how to deal with timer logic using “express” endpoints? Do I maybe have to resume all my processes when express starting, something like this?(I don’t think that it’s a good idea):

const app = express()

const states = getAllProcessStates()

for (const state of state) {
  const engine = new Engine({
    moddleOptions: {
      camunda: require('camunda-bpmn-moddle/resources/camunda'),
    },
  }).recover(state)

  const listener = new EventEmitter() 

  engine.resume(
    {
      listener,
    }
  )
}

app.listen(5000)

Or maybe there is some proper approach? Maybe you have some server examples to provide? Thank you!

mrpotato3 commented 1 year ago

I have the same problem. I'm working on a "cron" service that checks every X minutes if the timer has completed. To do this, I save the state to a database.