Skip to content

Commit

Permalink
feat(zeebe): implement lossless parsing of job payload (#95)
Browse files Browse the repository at this point in the history
* feat(zeebe): implement lossless parsing of job payload

FIxes #81

* feat(zeebe): implement decisive parser strategy

* feat(repo): implement lossless stringify
  • Loading branch information
jwulf authored Mar 28, 2024
1 parent c1c4211 commit 57f3ea8
Show file tree
Hide file tree
Showing 20 changed files with 690 additions and 259 deletions.
54 changes: 50 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ Install the SDK as a dependency:
npm i @camunda8/sdk
```

## A note on entity key types in the JavaScript SDK

Entity keys in Camunda 8 are stored and represented as int64 numbers. The range of int64 extends to numbers that cannot be represented by the JavaScript `number` type. To deal with this, int64 keys are serialised by the SDK as the JavaScript `string` type. See [this issue](https://github.com/camunda-community-hub/camunda-8-js-sdk/issues/78) for more details.

## Usage

In this release, the functionality of the Camunda Platform 8 is exposed via dedicated clients for the component APIs.
Expand All @@ -40,6 +36,14 @@ Any configuration passed in to the `Camunda8` constructor is merged over any con

The configuration object fields and the environment variables have exactly the same names. See the file `src/lib/Configuration.ts` for a complete list of configuration.

## A note on how int64 is handled in the JavaScript SDK

Entity keys in Camunda 8 are stored and represented as `int64` numbers. The range of `int64` extends to numbers that cannot be represented by the JavaScript `number` type. To deal with this, `int64` keys are serialised by the SDK to the JavaScript `string` type. See [this issue](https://github.com/camunda-community-hub/camunda-8-js-sdk/issues/78) for more details.

Some number values - for example: "_total returned results_ " - may be specified as `int64` in the API specifications. Although these numbers will usually not contain unsafe values, they are always serialised to `string`.

For `int64` values whose type is not known ahead of time, such as job variables, you can pass an annotated Dto object to decode them reliably. If no Dto is specified, the default behaviour of the SDK is to serialise all numbers to JavaScript `number`, and if a number value is detected at runtime that cannot be accurately stored as `number`, to throw an Exception.

## OAuth

Calls to APIs are authorized using a token that is obtained via a client id/secret pair exchange and then passes as an authorisation header on API calls. The SDK handles this transparently for you.
Expand Down Expand Up @@ -189,3 +193,45 @@ The SDK uses the [`debug`](https://github.com/debug-js/debug) library. To enable
| `camunda:grpc` | Zeebe gRPC channel |
| `camunda:worker` | Zeebe Worker |
| `camunda:zeebeclient` | Zeebe Client |

## Typing of Zeebe Worker Variables

The variable payload in a Zeebe Worker task handler is available as an object `job.variables`. By default, this is of type `any`.

The `ZBClient.createWorker()` method accepts an `inputVariableDto` to control the parsing of number values and provide design-time type information. Passing an `inputVariableDto` class to a Zeebe worker is optional. If a Dto class is passed to the Zeebe Worker, it is used for two purposes:

1. To provide design-time type information on the `job.variables` object.
2. To specify the parsing of JSON number fields. These can potentially represent `int64` values that cannot be represented accurately by the JavaScript `number` type. With a Dto, you can specify that a specific JSON number fields be parsed losslessly to a `string` or `BigInt`.

With no Dto specified, there is no design-time type safety. At run-time, all JSON numbers are converted to the JavaScript `number` type. If a variable field has a number value that cannot be safely represented using the JavaScript number type (a value greater than 2^53 -1) then an exception is thrown.

To provide a Dto, extend the `LosslessDto` class, like so:

```typescript
class MyVariableDto extends LosslessDto {
name!: string
maybeAge?: number
@Int64String
veryBigNumber!: string
@BigIntValue
veryBigInteger!: bigint
}
```

In this case, `veryBigNumber` is an `int64` value. It is transferred as a JSON number on the wire, but the parser will parse it into a `string` so that no loss of precision occurs. Similarly, `veryBigInteger` is a very large integer value. In this case, we direct the parser to parse this variable field as a `bigint`.

You can nest Dtos like this:

```typescript
class MyLargerDto extends LosslessDto {
id!: string
@ChildDto(MyVariableDto)
entry!: MyVariableDto
}
```

## Typing of Custom Headers

The Zeebe worker receives custom headers as `job.customHeaders`. The `ZBClient.createWorker()` method accepts a `customHeadersDto` to control the behaviour of custom header parsing of number values and provide design-time type information.

This follows the same strategy as the job variables, as previously described.
10 changes: 5 additions & 5 deletions src/__tests__/config/jest.cleanup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import path from 'path'
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('tsconfig-paths').register()

import { OAuthProvider } from 'oauth'
import { OperateApiClient } from 'operate'
import { BpmnParser, ZeebeGrpcClient } from 'zeebe'

export const cleanUp = async () => {
console.log('_dirname', __dirname)
console.log(process.cwd())
// Your cleanup process here.
console.log(
'Running global setup: cleanup test process instances before all tests...'
)
console.log('Removing all cached OAuth tokens...')
const o = new OAuthProvider()
o.flushFileCache()
console.log('Removing any running test process instances...')
const filePath = path.join(__dirname, '..', 'testdata')
const files = fs
.readdirSync(filePath)
Expand Down
5 changes: 4 additions & 1 deletion src/__tests__/config/jest.globalSetup.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { cleanUp } from './jest.cleanup'

export default async () => cleanUp()
export default async () => {
console.log('Running global setup...')
cleanUp()
}
5 changes: 4 additions & 1 deletion src/__tests__/config/jest.globalTeardown.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { cleanUp } from './jest.cleanup'

export default async () => cleanUp()
export default async () => {
console.log('Running global teardown...')
cleanUp()
}
Loading

0 comments on commit 57f3ea8

Please sign in to comment.