-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
new example: extended connector runtime (#571)
* wip * adjusted details, created readme, added to build.yaml * adjusted the function to use durations to align with the jobError function
- Loading branch information
1 parent
eca7343
commit 0e27f4c
Showing
11 changed files
with
434 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# Extended Connector Runtime | ||
|
||
As the connector runtime lies outside the engine, it can be adjusted. | ||
|
||
This example shows how a custom feel function can be introduced. | ||
|
||
This feel function can then be used on a connector runtime side feel expressions (for example result expression and error expression on outbound connectors). | ||
|
||
## How this works | ||
|
||
As the connector runtime uses a standardized way to bootstrap the feel engine and this way includes using the SPI function provider, this can be used to extend the functions being used. | ||
|
||
The [SPI file](./src/main/resources/META-INF/services/org.camunda.feel.context.CustomFunctionProvider) points to the function provider that determines on how the function is loaded. | ||
|
||
The provider then resolves the function by its name. | ||
|
||
The function itself is instantiated and gets a static reference to a spring bean injected. This workaround is chosen as there is no way to access the SPI context from within spring. | ||
|
||
## What kind of example is implemented here | ||
|
||
This example adds a function: | ||
|
||
``` | ||
nextExecutionBackoff(backoff: days-time-duration): days-time-duration | ||
``` | ||
|
||
This function then uses the `schedulingService` bean to determine the next timestamp where the execution could happen according to a defined schedule. | ||
|
||
## How can I run it | ||
|
||
As the example comes as spring boot application containing the rest connector, you can configure the connection to camunda and start the application using | ||
|
||
```bash | ||
mvn spring-boot:run | ||
``` | ||
|
||
>Tip: Disable the default connectors coming with your Camunda 8 installation to test out this runtime. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<groupId>io.camunda</groupId> | ||
<artifactId>extended-connector-runtime</artifactId> | ||
<version>1.0-SNAPSHOT</version> | ||
|
||
<properties> | ||
<maven.compiler.source>21</maven.compiler.source> | ||
<maven.compiler.target>21</maven.compiler.target> | ||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
<version.connectors>8.6.5</version.connectors> | ||
<version.camunda>8.5.9</version.camunda> | ||
<version.spring-boot>3.4.0</version.spring-boot> | ||
</properties> | ||
|
||
<dependencyManagement> | ||
<dependencies> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-dependencies</artifactId> | ||
<version>${version.spring-boot}</version> | ||
<type>pom</type> | ||
<scope>import</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>io.camunda</groupId> | ||
<artifactId>zeebe-bom</artifactId> | ||
<version>${version.camunda}</version> | ||
</dependency> | ||
</dependencies> | ||
</dependencyManagement> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>io.camunda.connector</groupId> | ||
<artifactId>spring-boot-starter-camunda-connectors</artifactId> | ||
<version>${version.connectors}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>io.camunda</groupId> | ||
<artifactId>zeebe-client-java</artifactId> | ||
<version>${version.camunda}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>io.camunda.connector</groupId> | ||
<artifactId>connector-http-json</artifactId> | ||
<version>${version.connectors}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-test</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>io.camunda</groupId> | ||
<artifactId>camunda-process-test-spring</artifactId> | ||
<version>8.6.6</version> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
<build> | ||
<pluginManagement> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-compiler-plugin</artifactId> | ||
<version>3.13.0</version> | ||
<configuration> | ||
<compilerArgs> | ||
<arg>-parameters</arg> | ||
</compilerArgs> | ||
</configuration> | ||
</plugin> | ||
<plugin> | ||
<groupId>com.diffplug.spotless</groupId> | ||
<artifactId>spotless-maven-plugin</artifactId> | ||
<version>2.43.0</version> | ||
<configuration> | ||
<formats> | ||
<format> | ||
<includes> | ||
<include>*.md</include> | ||
<include>.gitignore</include> | ||
</includes> | ||
<trimTrailingWhitespace/> | ||
<endWithNewline/> | ||
<indent> | ||
<spaces>true</spaces> | ||
<spacesPerTab>2</spacesPerTab> | ||
</indent> | ||
</format> | ||
</formats> | ||
<java> | ||
<googleJavaFormat/> | ||
</java> | ||
<pom/> | ||
</configuration> | ||
</plugin> | ||
</plugins> | ||
</pluginManagement> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-surefire-plugin</artifactId> | ||
<version>3.5.2</version> | ||
</plugin> | ||
<plugin> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-maven-plugin</artifactId> | ||
<version>${version.spring-boot}</version> | ||
<executions> | ||
<execution> | ||
<goals> | ||
<goal>repackage</goal> | ||
</goals> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
<profiles> | ||
<!-- profile to auto format --> | ||
<profile> | ||
<id>autoFormat</id> | ||
<activation> | ||
<activeByDefault>true</activeByDefault> | ||
</activation> | ||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>com.diffplug.spotless</groupId> | ||
<artifactId>spotless-maven-plugin</artifactId> | ||
<executions> | ||
<execution> | ||
<id>spotless-format</id> | ||
<goals> | ||
<goal>apply</goal> | ||
</goals> | ||
<phase>process-sources</phase> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
</profile> | ||
|
||
<!-- profile to perform strict validation checks --> | ||
<profile> | ||
<id>checkFormat</id> | ||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>com.diffplug.spotless</groupId> | ||
<artifactId>spotless-maven-plugin</artifactId> | ||
<executions> | ||
<execution> | ||
<id>spotless-check</id> | ||
<goals> | ||
<goal>check</goal> | ||
</goals> | ||
<phase>validate</phase> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
</profile> | ||
</profiles> | ||
</project> |
18 changes: 18 additions & 0 deletions
18
extended-connector-runtime/src/main/java/com/camunda/consulting/example/App.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package com.camunda.consulting.example; | ||
|
||
import org.springframework.boot.SpringApplication; | ||
import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
import org.springframework.context.ApplicationContext; | ||
|
||
@SpringBootApplication | ||
public class App { | ||
public static ApplicationContext applicationContext; | ||
|
||
public static void main(String[] args) { | ||
applicationContext = SpringApplication.run(App.class, args); | ||
} | ||
|
||
public static ApplicationContext getApplicationContext() { | ||
return applicationContext; | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
...ctor-runtime/src/main/java/com/camunda/consulting/example/CustomFeelFunctionProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package com.camunda.consulting.example; | ||
|
||
import java.util.Collection; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import org.camunda.feel.context.JavaFunction; | ||
import org.camunda.feel.context.JavaFunctionProvider; | ||
|
||
public class CustomFeelFunctionProvider extends JavaFunctionProvider { | ||
|
||
@Override | ||
public Optional<JavaFunction> resolveFunction(String functionName) { | ||
if (NextExecutionTimeslotFeelFunction.NAME.equals(functionName)) { | ||
return Optional.of( | ||
new JavaFunction( | ||
NextExecutionTimeslotFeelFunction.PARAMS, | ||
NextExecutionTimeslotFeelFunction.getInstance())); | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
@Override | ||
public Collection<String> getFunctionNames() { | ||
return List.of(NextExecutionTimeslotFeelFunction.NAME); | ||
} | ||
} |
53 changes: 53 additions & 0 deletions
53
...ntime/src/main/java/com/camunda/consulting/example/NextExecutionTimeslotFeelFunction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package com.camunda.consulting.example; | ||
|
||
import java.time.Duration; | ||
import java.time.ZonedDateTime; | ||
import java.time.temporal.ChronoUnit; | ||
import java.util.List; | ||
import java.util.function.Function; | ||
import org.camunda.feel.syntaxtree.Val; | ||
import org.camunda.feel.syntaxtree.ValDayTimeDuration; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
public class NextExecutionTimeslotFeelFunction implements Function<List<Val>, Val> { | ||
public static final List<String> PARAMS = List.of("backoff"); | ||
public static final String NAME = "nextExecutionBackoff"; | ||
|
||
/** | ||
* This field is required to access a bean from outside the spring context as spi is used for the | ||
* function provider | ||
*/ | ||
private static NextExecutionTimeslotFeelFunction instance; | ||
|
||
private final SchedulingService schedulingService; | ||
|
||
public NextExecutionTimeslotFeelFunction(SchedulingService schedulingService) { | ||
this.schedulingService = schedulingService; | ||
instance = this; | ||
} | ||
|
||
public static NextExecutionTimeslotFeelFunction getInstance() { | ||
return instance; | ||
} | ||
|
||
@Override | ||
public Val apply(List<Val> vals) { | ||
Val scheduledExecution = vals.get(0); | ||
if (scheduledExecution instanceof ValDayTimeDuration duration) { | ||
Duration value = duration.value(); | ||
return new ValDayTimeDuration(calculateNextExecution(value)); | ||
} else { | ||
throw new IllegalStateException( | ||
"Param 'scheduledExecution' expected to be of type 'date and time'"); | ||
} | ||
} | ||
|
||
private Duration calculateNextExecution(Duration duration) { | ||
// we use the same "now" for both transformations to prevent inconsistencies | ||
ZonedDateTime now = ZonedDateTime.now(); | ||
ZonedDateTime scheduledExecution = now.plus(duration); | ||
return Duration.between(now, schedulingService.schedule(scheduledExecution)) | ||
.truncatedTo(ChronoUnit.SECONDS); | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
...ded-connector-runtime/src/main/java/com/camunda/consulting/example/SchedulingService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.camunda.consulting.example; | ||
|
||
import java.time.ZonedDateTime; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
public class SchedulingService { | ||
public ZonedDateTime schedule(ZonedDateTime scheduledExecution) { | ||
// you can call your scheduling system here | ||
return scheduledExecution.plusMinutes(10); | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
...time/src/main/resources/META-INF/services/org.camunda.feel.context.CustomFunctionProvider
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
com.camunda.consulting.example.CustomFeelFunctionProvider |
8 changes: 8 additions & 0 deletions
8
extended-connector-runtime/src/main/resources/application.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
camunda: | ||
connector: | ||
polling: | ||
enabled: false | ||
webhook: | ||
enabled: false | ||
client: | ||
mode: simple |
40 changes: 40 additions & 0 deletions
40
extended-connector-runtime/src/test/java/com/camunda/consulting/example/AppTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package com.camunda.consulting.example; | ||
|
||
import static org.assertj.core.api.Assertions.*; | ||
import static org.mockito.Mockito.*; | ||
|
||
import io.camunda.process.test.api.CamundaSpringProcessTest; | ||
import io.camunda.zeebe.client.ZeebeClient; | ||
import io.camunda.zeebe.client.api.response.ProcessInstanceResult; | ||
import java.time.Duration; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.context.SpringBootTest; | ||
|
||
@SpringBootTest | ||
@CamundaSpringProcessTest | ||
public class AppTest { | ||
@Autowired ZeebeClient zeebeClient; | ||
|
||
@BeforeEach | ||
public void setup() { | ||
zeebeClient.newDeployResourceCommand().addResourceFromClasspath("test.bpmn").send().join(); | ||
} | ||
|
||
@Test | ||
void shouldExecute() { | ||
Duration nextExecutionBackoff = Duration.ofMinutes(20); | ||
ProcessInstanceResult result = | ||
zeebeClient | ||
.newCreateInstanceCommand() | ||
.bpmnProcessId("test") | ||
.latestVersion() | ||
.withResult() | ||
.send() | ||
.join(); | ||
assertThat(result).isNotNull(); | ||
assertThat(Duration.parse((CharSequence) result.getVariable("nextExecutionBackoff"))) | ||
.isEqualTo(nextExecutionBackoff); | ||
} | ||
} |
Oops, something went wrong.