Skip to content

Commit

Permalink
feat(zeebe): implement deleteResource (#73)
Browse files Browse the repository at this point in the history
  • Loading branch information
jwulf authored Mar 20, 2024
1 parent 6cb98f0 commit 0cd08b7
Show file tree
Hide file tree
Showing 19 changed files with 240 additions and 66 deletions.
57 changes: 57 additions & 0 deletions .github/workflows/multitenancy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Run Local Integration Tests Multitenant

on: [push]

jobs:
local_integration:
runs-on: ubuntu-latest
environment:
name: selfhosted
steps:
- name: Check out the repo
uses: actions/checkout@v3

- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: "18" # Specify a Node.js version

# This is to force nx arch-specific packages to correctly install
# This is to work around https://github.com/npm/cli/issues/4828
- name: Remove package-lock.json
run: rm -f package-lock.json

- name: Install dependencies
run: npm install

# Workaround for https://github.com/actions/runner-images/issues/2821
- name: Remove mono blocking 8084 port
run: sudo kill -9 $(sudo lsof -t -i:8084)

- name: Set up Docker
run: |
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} registry.camunda.cloud
- name: Set up Docker Compose
run: |
docker-compose -f docker/docker-compose-multitenancy.yml -f docker/docker-compose-modeler.yaml up -d
- name: Run Integration Tests
run: |
npm run test:multitenancy
env:
CAMUNDA_SECURE_CONNECTION: false
ZEEBE_ADDRESS: localhost:26500
ZEEBE_CLIENT_ID: zeebe
ZEEBE_CLIENT_SECRET: zecret
CAMUNDA_OAUTH_URL: http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token
CAMUNDA_TASKLIST_BASE_URL: http://localhost:8082
CAMUNDA_OPERATE_BASE_URL: http://localhost:8081
CAMUNDA_OPTIMIZE_BASE_URL: http://localhost:8083
CAMUNDA_MODELER_BASE_URL: http://localhost:8070/api
# Needed for Multi-Tenancy
CAMUNDA_TENANT_ID: <default>

- name: Cleanup
if: always()
run: docker-compose -f docker/docker-compose-multitenancy.yml -f docker/docker-compose-modeler.yaml down
File renamed without changes.
File renamed without changes.
6 changes: 3 additions & 3 deletions docker/.env
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
## Image versions ##
# CAMUNDA_CONNECTORS_VERSION=0.23.2
CAMUNDA_CONNECTORS_VERSION=8.4.1
CAMUNDA_OPTIMIZE_VERSION=8.4.1
CAMUNDA_PLATFORM_VERSION=8.4.1
CAMUNDA_CONNECTORS_VERSION=8.5.0-alpha2
CAMUNDA_OPTIMIZE_VERSION=8.5.0-alpha2
CAMUNDA_PLATFORM_VERSION=8.5.0-alpha2
CAMUNDA_WEB_MODELER_VERSION=8.4.1
ELASTIC_VERSION=8.9.0
KEYCLOAK_SERVER_VERSION=22.0.3
Expand Down
10 changes: 5 additions & 5 deletions docker/docker-compose-multitenancy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ services:
- CAMUNDA_OPERATE_IDENTITY_CLIENTID=operate
- CAMUNDA_OPERATE_IDENTITY_CLIENTSECRET=XALaRPl5qwTEItdwCMiPS62nVpKs7dL7
- CAMUNDA_OPERATE_IDENTITY_AUDIENCE=operate-api
- SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI=http://${HOST}:18080/auth/realms/camunda-platform
- SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI=http://${HOST}:18080/auth/realms/camunda-platform/protocol/openid-connect/certs
- SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI=http://keycloak:8080/auth/realms/camunda-platform
- SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI=http://keycloak:8080/auth/realms/camunda-platform/protocol/openid-connect/certs
- CAMUNDA_OPERATE_IDENTITY_RESOURCEPERMISSIONSENABLED=${RESOURCE_AUTHORIZATIONS_ENABLED}
- CAMUNDA_OPERATE_MULTITENANCY_ENABLED=true
- management.endpoints.web.exposure.include=health
Expand Down Expand Up @@ -114,8 +114,8 @@ services:
- CAMUNDA_TASKLIST_IDENTITY_CLIENTID=tasklist
- CAMUNDA_TASKLIST_IDENTITY_CLIENTSECRET=XALaRPl5qwTEItdwCMiPS62nVpKs7dL7
- CAMUNDA_TASKLIST_IDENTITY_AUDIENCE=tasklist-api
- SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI=http://${HOST}:18080/auth/realms/camunda-platform
- SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI=http://${HOST}:18080/auth/realms/camunda-platform/protocol/openid-connect/certs
- SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI=http://keycloak:8080/auth/realms/camunda-platform
- SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI=http://keycloak:8080/auth/realms/camunda-platform/protocol/openid-connect/certs
- CAMUNDA_TASKLIST_IDENTITY_RESOURCE_PERMISSIONS_ENABLED=${RESOURCE_AUTHORIZATIONS_ENABLED}
- CAMUNDA_TASKLIST_MULTITENANCY_ENABLED=true
- management.endpoints.web.exposure.include=health
Expand Down Expand Up @@ -316,7 +316,7 @@ services:
IDENTITY_ENVIRONMENT_TENANTS_2_NAME: red
IDENTITY_ENVIRONMENT_TENANTS_2_TENANTID: red
RESOURCE_PERMISSIONS_ENABLED: ${RESOURCE_AUTHORIZATIONS_ENABLED}
MULTITENANCY_ENABLED: true
MULTITENANCY_ENABLED: "true"
healthcheck:
test:
[
Expand Down
8 changes: 4 additions & 4 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ services:
- CAMUNDA_OPERATE_IDENTITY_CLIENTID=operate
- CAMUNDA_OPERATE_IDENTITY_CLIENTSECRET=XALaRPl5qwTEItdwCMiPS62nVpKs7dL7
- CAMUNDA_OPERATE_IDENTITY_AUDIENCE=operate-api
- SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI=http://${HOST}:18080/auth/realms/camunda-platform
- SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI=http://${HOST}:18080/auth/realms/camunda-platform/protocol/openid-connect/certs
- SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI=http://keycloak:8080/auth/realms/camunda-platform
- SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI=http://keycloak:8080/auth/realms/camunda-platform/protocol/openid-connect/certs
- CAMUNDA_OPERATE_IDENTITY_RESOURCEPERMISSIONSENABLED=${RESOURCE_AUTHORIZATIONS_ENABLED}
- management.endpoints.web.exposure.include=health
- management.endpoint.health.probes.enabled=true
Expand Down Expand Up @@ -110,8 +110,8 @@ services:
- CAMUNDA_TASKLIST_IDENTITY_CLIENTID=tasklist
- CAMUNDA_TASKLIST_IDENTITY_CLIENTSECRET=XALaRPl5qwTEItdwCMiPS62nVpKs7dL7
- CAMUNDA_TASKLIST_IDENTITY_AUDIENCE=tasklist-api
- SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI=http://${HOST}:18080/auth/realms/camunda-platform
- SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI=http://${HOST}:18080/auth/realms/camunda-platform/protocol/openid-connect/certs
- SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI=http://keycloak:8080/auth/realms/camunda-platform
- SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI=http://keycloak:8080/auth/realms/camunda-platform/protocol/openid-connect/certs
- CAMUNDA_TASKLIST_IDENTITY_RESOURCE_PERMISSIONS_ENABLED=${RESOURCE_AUTHORIZATIONS_ENABLED}
- management.endpoints.web.exposure.include=health
- management.endpoint.health.probes.enabled=true
Expand Down
50 changes: 50 additions & 0 deletions src/__tests__/testdata/DeleteResource.bpmn
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?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:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:zeebe="http://camunda.org/schema/zeebe/1.0" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_0gtgqqw" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.21.0" modeler:executionPlatform="Camunda Cloud" modeler:executionPlatformVersion="8.4.0">
<bpmn:process id="delete-me" name="DeleteResource" isExecutable="true">
<bpmn:startEvent id="StartEvent_1" name="Start">
<bpmn:outgoing>Flow_0fgdzij</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_0fgdzij" sourceRef="StartEvent_1" targetRef="Activity_0ui3en7" />
<bpmn:endEvent id="Event_1wzv4ji" name="End">
<bpmn:incoming>Flow_0l1o4z0</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0l1o4z0" name="End" sourceRef="Activity_0ui3en7" targetRef="Event_1wzv4ji" />
<bpmn:serviceTask id="Activity_0ui3en7" name="Never Executed">
<bpmn:extensionElements>
<zeebe:taskDefinition type="never-executed" />
</bpmn:extensionElements>
<bpmn:incoming>Flow_0fgdzij</bpmn:incoming>
<bpmn:outgoing>Flow_0l1o4z0</bpmn:outgoing>
</bpmn:serviceTask>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="delete-me">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="179" y="99" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="185" y="142" width="24" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1wzv4ji_di" bpmnElement="Event_1wzv4ji">
<dc:Bounds x="432" y="99" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="440" y="142" width="20" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_05jxyb8_di" bpmnElement="Activity_0ui3en7">
<dc:Bounds x="270" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_0fgdzij_di" bpmnElement="Flow_0fgdzij">
<di:waypoint x="215" y="117" />
<di:waypoint x="270" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0l1o4z0_di" bpmnElement="Flow_0l1o4z0">
<di:waypoint x="370" y="117" />
<di:waypoint x="432" y="117" />
<bpmndi:BPMNLabel>
<dc:Bounds x="391" y="99" width="20" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>
39 changes: 39 additions & 0 deletions src/__tests__/zeebe/integration/Client-DeleteResource.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { restoreZeebeLogging, suppressZeebeLogging } from 'lib'
import { ZeebeGrpcClient } from 'zeebe'

jest.setTimeout(20000)

suppressZeebeLogging()

afterAll(() => restoreZeebeLogging())

test('can delete a resource', async () => {
let thrown = false
const zbc = new ZeebeGrpcClient()

const deployment = await zbc.deployResource({
processFilename: './src/__tests__/testdata/DeleteResource.bpmn',
})

const resourceKey = deployment.deployments[0].process.processDefinitionKey

const process = await zbc.createProcessInstance({
bpmnProcessId: 'delete-me',
variables: {},
})

await zbc.cancelProcessInstance(process.processInstanceKey)

await zbc.deleteResource({ resourceKey })
try {
/** This should now throw, as the resource has been deleted */
const process2 = await zbc.createProcessInstance({
bpmnProcessId: 'delete-me',
variables: {},
})
await zbc.cancelProcessInstance(process2.processInstanceKey)
} catch (e) {
thrown = true
}
expect(thrown).toBe(true)
})
43 changes: 25 additions & 18 deletions src/__tests__/zeebe/multitenancy/signalbroadcast-mt.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { restoreZeebeLogging, suppressZeebeLogging } from 'lib'
import { delay, restoreZeebeLogging, suppressZeebeLogging } from 'lib'
import { OperateApiClient } from 'operate'
import { ZeebeGrpcClient } from 'zeebe'

jest.setTimeout(7000)
suppressZeebeLogging()

afterAll(() => restoreZeebeLogging())
Expand Down Expand Up @@ -46,22 +48,30 @@ test('Multi-tenant Zeebe: No error if tenantId is provided when broadcasting a s
})

test('Signal broadcast to red tenant is received', async () => {
const redClient = new ZeebeGrpcClient({
config: {
CAMUNDA_TENANT_ID: 'red',
ZEEBE_CLIENT_ID: 'redzeebe',
ZEEBE_CLIENT_SECRET: 'redzecret',
},
})
const operate = new OperateApiClient()
/**
* Make sure that there are no pending instances of the test process running
*/
await Promise.all(
await operate
.searchProcessInstances({ filter: { bpmnProcessId: 'red-tenant' } })
.then((res) =>
res.items.map((i) => redClient.cancelProcessInstance(i.key))
)
)
return new Promise((resolve) => {
const redClient = new ZeebeGrpcClient({
config: {
CAMUNDA_TENANT_ID: 'red',
ZEEBE_CLIENT_ID: 'redzeebe',
ZEEBE_CLIENT_SECRET: 'redzecret',
},
})
redClient
.deployResource({
processFilename: './src/__tests__/testdata/Red-Tenant-Process.bpmn',
})
.then((p) => {
console.log(
`Deployed process with bpmnProcessId: ${p.deployments[0].process.bpmnProcessId}`
)
.then(() => {
redClient.broadcastSignal({
signalName: 'red-tenant-signal',
variables: { test: 'valid' },
Expand All @@ -73,20 +83,17 @@ test('Signal broadcast to red tenant is received', async () => {
variables: {},
})
.then((res) => {
console.log('Finished process on red tenant')
expect(res.bpmnProcessId).toBe('red-tenant')
expect(res.variables.test).toBe('valid')
resolve(null)
})
console.log(`Started process with bpmnProcessId: red-tenant`)
redClient
.broadcastSignal({
delay(2000).then(() => {
redClient.broadcastSignal({
signalName: 'red-tenant-signal',
variables: { test: 'valid' },
tenantId: 'red',
})
.then((res) => console.log(res))
console.log(`Broadcast signal with name: red-tenant-signal`)
})
})
})
})
2 changes: 2 additions & 0 deletions src/lib/BigIntSerializer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const bigIntToString = (bigInt: bigint) => bigInt.toString()
export const stringToBigInt = (str: string) => BigInt(str)
1 change: 1 addition & 0 deletions src/lib/Delay.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const delay = (ms: number) => new Promise((res) => setTimeout(res, ms))
1 change: 1 addition & 0 deletions src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from './CertificateAuthority'
export * from './ClientConstructor'
export * from './Configuration'
export * from './ConstructOAuthProvider'
export * from './Delay'
export * from './EnvironmentSetup'
export { packageVersion } from './GetPackageVersion'
export { RequireConfiguration } from './RequireConfiguration'
Expand Down
3 changes: 3 additions & 0 deletions src/operate/lib/APIObjects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ export interface ProcessInstance {
processVersion: number
bpmnProcessId: string
parentKey?: number
parentFlowNodeInstanceKey: number
/* yyyy-MM-dd'T'HH:mm:ss.SSSZZ */
startDate: string
/* yyyy-MM-dd'T'HH:mm:ss.SSSZZ */
endDate: string
state: 'ACTIVE' | 'COMPLETED' | 'CANCELED'
processDefinitionKey: number
tenantId: string | undefined
parentProcessInstanceKey: number
}

export interface Incident {
Expand Down
10 changes: 9 additions & 1 deletion src/operate/lib/OperateApiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export class OperateApiClient {
private userAgentString: string
private oAuthProvider: IOAuthProvider
private rest: typeof got
private tenantId: string | undefined

/**
* @example
Expand Down Expand Up @@ -72,6 +73,7 @@ export class OperateApiClient {
certificateAuthority,
},
})
this.tenantId = config.CAMUNDA_TENANT_ID
}

private async getHeaders() {
Expand Down Expand Up @@ -170,9 +172,15 @@ export class OperateApiClient {
query: Query<ProcessInstance> = {}
): Promise<SearchResults<ProcessInstance>> {
const headers = await this.getHeaders()
const json = this.tenantId
? {
...query,
tenantId: this.tenantId,
}
: query
return this.rest
.post('process-instances/search', {
json: query,
json,
headers,
})
.json()
Expand Down
7 changes: 6 additions & 1 deletion src/zeebe/lib/GrpcClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,14 +365,19 @@ export class GrpcClient extends EventEmitter {
return new Promise(async (resolve, reject) => {
try {
const metadata = (await this.getAuthToken()) || {}
debug(methodName, 'timeNormalisedRequest', timeNormalisedRequest)

client[methodName](
timeNormalisedRequest,
metadata,
(err, dat) => {
// This will error on network or business errors
if (err) {
debug(`${methodName}Sync error: ${err.code}`)
const isNetworkError = err.code === GrpcError.UNAVAILABLE
debug(err.message)
const isNetworkError =
err.code === GrpcError.UNAVAILABLE &&
!err.message.includes('partition')
if (isNetworkError) {
this.setNotReady()
} else {
Expand Down
Loading

0 comments on commit 0cd08b7

Please sign in to comment.