Skip to content

Commit

Permalink
Add Nexus sample (#686)
Browse files Browse the repository at this point in the history
Add Nexus samples
  • Loading branch information
Quinn-With-Two-Ns authored Oct 16, 2024
1 parent ec1c615 commit bf1a405
Show file tree
Hide file tree
Showing 17 changed files with 888 additions and 1 deletion.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ subprojects {
ext {
otelVersion = '1.30.1'
otelVersionAlpha = "${otelVersion}-alpha"
javaSDKVersion = '1.25.0'
javaSDKVersion = '1.26.0'
camelVersion = '3.22.1'
jarVersion = '1.0.0'
}
Expand Down
1 change: 1 addition & 0 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dependencies {
implementation group: 'io.cloudevents', name: 'cloudevents-api', version: '4.0.1'
implementation group: 'io.cloudevents', name: 'cloudevents-json-jackson', version: '3.0.0'
implementation group: 'net.thisptr', name: 'jackson-jq', version: '1.0.0-preview.20240207'
implementation group: 'commons-cli', name: 'commons-cli', version: '1.9.0'

// we don't update it to 2.1.0 because 2.1.0 requires Java 11
implementation 'com.codingrodent:jackson-json-crypto:1.1.0'
Expand Down
103 changes: 103 additions & 0 deletions core/src/main/java/io/temporal/samples/nexus/README.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Nexus

Temporal Nexus is a new feature of the Temporal platform designed to connect durable executions across team, namespace,
region, and cloud boundaries. It promotes a more modular architecture for sharing a subset of your team’s capabilities
via well-defined service API contracts for other teams to use, that abstract underlying Temporal primitives, like
Workflows, or execute arbitrary code.

Learn more at [temporal.io/nexus](https://temporal.io/nexus).

This sample shows how to use Temporal for authoring a Nexus service and call it from a workflow.

### Sample directory structure

- [service](./service) - shared service definition
- [caller](./caller) - caller workflows, worker, and starter
- [handler](./handler) - handler workflow, operations, and worker
- [options](./options) - command line argument parsing utility

## Getting started locally

### Get `temporal` CLI to enable local development

1. Follow the instructions on the [docs
site](https://learn.temporal.io/getting_started/go/dev_environment/#set-up-a-local-temporal-service-for-development-with-temporal-cli)
to install Temporal CLI.

> NOTE: Required version is at least v1.1.0.
### Spin up environment

#### Start temporal server

> HTTP port is required for Nexus communications
```
temporal server start-dev --http-port 7243 --dynamic-config-value system.enableNexus=true
```

### Initialize environment

In a separate terminal window

#### Create caller and target namespaces

```
temporal operator namespace create --namespace my-target-namespace
temporal operator namespace create --namespace my-caller-namespace
```

#### Create Nexus endpoint

```
temporal operator nexus endpoint create \
--name my-nexus-endpoint-name \
--target-namespace my-target-namespace \
--target-task-queue my-handler-task-queue \
--description-file ./service/description.md
```

## Getting started with a self-hosted service or Temporal Cloud

Nexus is currently available as
[Public Preview](https://docs.temporal.io/evaluate/development-production-features/release-stages).

Self hosted users can [try Nexus
out](https://github.com/temporalio/temporal/blob/main/docs/architecture/nexus.md#trying-nexus-out) in single cluster
deployments with server version 1.25.0.

### Make Nexus calls across namespace boundaries

> Instructions apply for local development, for Temporal Cloud or a self-hosted setups, supply the relevant [CLI
> flags](./options/ClientOptions.java) to properly set up the connection.
In separate terminal windows:

### Nexus handler worker

```
./gradlew -q execute -PmainClass=io.temporal.samples.nexus.handler.HandlerWorker \
--args="-target-host localhost:7233 -namespace my-target-namespace"
```

### Nexus caller worker

```
./gradlew -q execute -PmainClass=io.temporal.samples.nexus.caller.CallerWorker \
--args="-target-host localhost:7233 -namespace my-caller-namespace"
```

### Start caller workflow

```
./gradlew -q execute -PmainClass=io.temporal.samples.nexus.caller.CallerStarter \
--args="-target-host localhost:7233 -namespace my-caller-namespace"
```

### Output

which should result in:
```
[main] INFO i.t.s.nexus.caller.CallerStarter - Workflow result: Nexus Echo 👋
[main] INFO i.t.s.nexus.caller.CallerStarter - Workflow result: ¡Hola! Nexus 👋
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved
*
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Modifications copyright (C) 2017 Uber Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
* use this file except in compliance with the License. A copy of the License is
* located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package io.temporal.samples.nexus.caller;

import io.temporal.client.WorkflowClient;
import io.temporal.client.WorkflowOptions;
import io.temporal.client.WorkflowStub;
import io.temporal.samples.nexus.options.ClientOptions;
import io.temporal.samples.nexus.service.NexusService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CallerStarter {
private static final Logger logger = LoggerFactory.getLogger(CallerStarter.class);

public static void main(String[] args) {
WorkflowClient client = ClientOptions.getWorkflowClient(args);

WorkflowOptions workflowOptions =
WorkflowOptions.newBuilder().setTaskQueue(CallerWorker.DEFAULT_TASK_QUEUE_NAME).build();
EchoCallerWorkflow echoWorkflow =
client.newWorkflowStub(EchoCallerWorkflow.class, workflowOptions);
logger.info("Workflow result: {}", echoWorkflow.echo("Nexus Echo 👋"));
logger.info(
"Started workflow workflowId: {} runId: {}",
WorkflowStub.fromTyped(echoWorkflow).getExecution().getWorkflowId(),
WorkflowStub.fromTyped(echoWorkflow).getExecution().getRunId());
HelloCallerWorkflow helloWorkflow =
client.newWorkflowStub(HelloCallerWorkflow.class, workflowOptions);
logger.info("Workflow result: {}", helloWorkflow.hello("Nexus", NexusService.Language.ES));
logger.info(
"Started workflow workflowId: {} runId: {}",
WorkflowStub.fromTyped(helloWorkflow).getExecution().getWorkflowId(),
WorkflowStub.fromTyped(helloWorkflow).getExecution().getRunId());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved
*
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Modifications copyright (C) 2017 Uber Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
* use this file except in compliance with the License. A copy of the License is
* located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package io.temporal.samples.nexus.caller;

import io.temporal.client.WorkflowClient;
import io.temporal.samples.nexus.options.ClientOptions;
import io.temporal.worker.Worker;
import io.temporal.worker.WorkerFactory;
import io.temporal.worker.WorkflowImplementationOptions;
import io.temporal.workflow.NexusServiceOptions;
import java.util.Collections;

public class CallerWorker {
public static final String DEFAULT_TASK_QUEUE_NAME = "my-caller-workflow-task-queue";

public static void main(String[] args) {
WorkflowClient client = ClientOptions.getWorkflowClient(args);

WorkerFactory factory = WorkerFactory.newInstance(client);

Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME);
worker.registerWorkflowImplementationTypes(
WorkflowImplementationOptions.newBuilder()
.setNexusServiceOptions(
Collections.singletonMap(
"NexusService",
NexusServiceOptions.newBuilder().setEndpoint("my-nexus-endpoint-name").build()))
.build(),
EchoCallerWorkflowImpl.class,
HelloCallerWorkflowImpl.class);

factory.start();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved
*
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Modifications copyright (C) 2017 Uber Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
* use this file except in compliance with the License. A copy of the License is
* located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package io.temporal.samples.nexus.caller;

import io.temporal.workflow.WorkflowInterface;
import io.temporal.workflow.WorkflowMethod;

@WorkflowInterface
public interface EchoCallerWorkflow {
@WorkflowMethod
String echo(String message);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved
*
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Modifications copyright (C) 2017 Uber Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
* use this file except in compliance with the License. A copy of the License is
* located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package io.temporal.samples.nexus.caller;

import io.temporal.samples.nexus.service.NexusService;
import io.temporal.workflow.NexusOperationOptions;
import io.temporal.workflow.NexusServiceOptions;
import io.temporal.workflow.Workflow;
import java.time.Duration;

public class EchoCallerWorkflowImpl implements EchoCallerWorkflow {
NexusService nexusService =
Workflow.newNexusServiceStub(
NexusService.class,
NexusServiceOptions.newBuilder()
.setOperationOptions(
NexusOperationOptions.newBuilder()
.setScheduleToCloseTimeout(Duration.ofSeconds(10))
.build())
.build());

@Override
public String echo(String message) {
return nexusService.echo(new NexusService.EchoInput(message)).getMessage();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved
*
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Modifications copyright (C) 2017 Uber Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
* use this file except in compliance with the License. A copy of the License is
* located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package io.temporal.samples.nexus.caller;

import io.temporal.samples.nexus.service.NexusService;
import io.temporal.workflow.WorkflowInterface;
import io.temporal.workflow.WorkflowMethod;

@WorkflowInterface
public interface HelloCallerWorkflow {
@WorkflowMethod
String hello(String message, NexusService.Language language);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved
*
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Modifications copyright (C) 2017 Uber Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
* use this file except in compliance with the License. A copy of the License is
* located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package io.temporal.samples.nexus.caller;

import io.temporal.samples.nexus.service.NexusService;
import io.temporal.workflow.NexusOperationHandle;
import io.temporal.workflow.NexusOperationOptions;
import io.temporal.workflow.NexusServiceOptions;
import io.temporal.workflow.Workflow;
import java.time.Duration;

public class HelloCallerWorkflowImpl implements HelloCallerWorkflow {
NexusService nexusService =
Workflow.newNexusServiceStub(
NexusService.class,
NexusServiceOptions.newBuilder()
.setOperationOptions(
NexusOperationOptions.newBuilder()
.setScheduleToCloseTimeout(Duration.ofSeconds(10))
.build())
.build());

@Override
public String hello(String message, NexusService.Language language) {
NexusOperationHandle<NexusService.HelloOutput> handle =
Workflow.startNexusOperation(
nexusService::hello, new NexusService.HelloInput(message, language));
// Optionally wait for the operation to be started. NexusOperationExecution will contain the
// operation ID in case this operation is asynchronous.
handle.getExecution().get();
return handle.getResult().get().getMessage();
}
}
Loading

0 comments on commit bf1a405

Please sign in to comment.