API Commands demo

Register time and material "Save feature"

Objective: Save multiple limeobjects in one request without the need of copy-pasting another custom endpoint

POST /company/
POST /person/
POST /person/
POST /messaging/sms/
Client
Save
200 OK
200 OK
400 BAD REQUEST
404 NOT FOUND
Side-effects!
Side-effects!
Side-effects: commit unit of work, publish to mq, call apis
Retry and post once more?

So, how about a custom endpoint?

Sure, we can copy paste some code every time we need to do more than one request, but...

How about a generic endpoint?

API Commands demo

POC POC, what's there?
Endpoint.
What endpoint?

Commands endpoint!

​POST /api/commands in Lime Field

  1. built for integrations, but adopted into all apps in the product
  2. takes a list of "commands"
  3. flat matrix schema, like a dict, or hal+json, easily extendable to new "commands", can be used to bulk import data from spreadsheets
  4. allows a client to save data with relations maintained without know​ing record ids, eliminating the need of using multiple requests combined
  5. "idempotent" - if the client sends the same batch of commands again due to a timeout,
    it won't publish new events on the MQ (and similar side-effects) by using client side UUIDs on records
  6. runs by default in "batch mode" but also "continue on error" (bulk imports)
POST /commands/
[
  create-company -name 'Cool Runnings' -orgnr 1234
  create-person -name ... -phone ... -company { orgnr: 1234 }
  create-person -name ... -phone ... -company 1023
]


Response:

[
  ok: create-company -name 'Cool Runnings' -orgnr 1234
  data: id=1010, limetype=company

  ok: create-person -name ... -phone ... -company { orgnr: 1234 }
  data: .....
]
Client
Save
200 OK
Side-effects!
POST /commands/
[
  create-company -name 'Cool Runnings' -orgnr 1234
  create-person -name ... -phone ... -company { orgnr: 1234 }
  create-person -name ... -phone ... -company 1023
]

Response:

[
  fail: create-company -name 'Cool Runnings' -orgnr 1234
  error: code=PARAMETER
             message=parameter buyingstatus is required


  fail: create-person -name ... -phone ... -company { ... }
  error: code=NOT_EXECUTED
]
Client
Save
400 BAD REQUEST
...or, no side-effects when it fails:

Example payload

fetch("/solution-workorder/limepkg-workorder/commands/", ... { commands:[{ name:'upsert-limeobject', parameters: { limetype: 'workorder', title: 'commands 2 you', state:'Started' } }, { name:'upsert-limeobject', parameters: { limetype: 'workorder', title: 'commands 4 for demo' } }] })

Example successful response

{ "successful": true, "id": "", "commands": [ { "command": { "name": "upsert-limeobject", "parameters": { "limetype": "workorder", "title": "commands 2 you", "state": "Started" } }, "successful": true, "data": { "limetype": "workorder", "id": 1012 } }, ...

Example unsuccessful response

{ "successful": false, "id": "", "commands": [ { "command": { "name": "upsert-limeobject", "parameters": { "limetype": "workorder", "title": "commands 2 you", "state": "FriyayDemo" } }, "successful": false, "data": {}, "error": { "code": "PARAMETER", "message": "The option \"FriyayDemo\" does not exist." } }, ...

Frontend service interface

this.platform.get("ApiCommandsService").post_commands([ {name:'upsert-limeobject', parameters: { ... } }, {name:'do-demo-stuff', parameters: { ... } }, ... ]);

Backend "mechanics"

endpoints/ tasks/ translations/ limeobject_classes/ event_handlers/ ... command_handlers/ __init__.py <-- auto-imported on 1st command req from all plugins like the above mycommand.py mycommand_test.py

@commandhandler('command-name') similar to @task

def test_post_valid_command(post_commands, testcontext): @commandhandler('do-stuff') def handleStuff(ctx: CommandContext, command: Command) -> CommandResult: return CommandResult(command, True, CommandResultData(id=1001)) @commandhandler('do-more-stuff') def handleMore(ctx: CommandContext, command: Command) -> CommandResult: return CommandResult(command, True, CommandResultData(id=9090)) command1 = { 'name': 'do-stuff', 'parameters': { 'with': 'commands' } } result = post_commands([command1], 'batchid-123')

Probably Asked Questions

  1. Is it ready? Nope :)
  2. How can I use it? We should extract it to limepkg-apicommands or something before using it outside limepkg-workorder
  3. Is it async? Nope, but it should be able to return a redirect to a task if async=True in payload
  4. Does it use distributed transactions? Nope, it just coordinates a shared UoW among a list of commands​