Skip to content

Nexus handler near complete implementation #1708

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

bergundy
Copy link
Member

@bergundy bergundy commented May 8, 2025

  • This PR is blocked on publishing an initial version of the nexus-rpc package.
  • There is a TODO to figure out HandlerError and OperationError message rehydration, pending discussion.
  • Interceptors not yet implemented.
  • WorkflowRunOperation and getClient() not implemented for the @temporalio/nexus package.
  • Tests use the HTTP API directly in lieu of a workflow caller or strongly typed client, we can refactor those later.

- This PR is blocked on publishing an initial version of the `nexus-rpc` package.
- There is a TODO to figure out HandlerError and OperationError message rehydration, pending discussion.
- Interceptors not yet implemented.
- `WorkflowRunOperation` and `getClient()` not implemented for the `@temporalio/nexus` package.
- Tests use the HTTP API directly in lieu of a workflow caller or strongly typed client, we can refactor those later.
@bergundy bergundy requested a review from a team as a code owner May 8, 2025 01:26
@@ -91,6 +91,17 @@ export async function decodeArrayFromPayloads(
return arrayFromPayloads(payloadConverter, await decodeOptional(payloadCodecs, payloads));
}

/**
* Decode `payloads` and then return {@link arrayFromPayloads}`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring is wrong

if (nexusServices.has(s.name)) {
throw new TypeError(`Duplicate registration of nexus service ${s.name}`);
}
const ops = new Map<string, nexus.OperationHandler<any, any> | nexus.SyncOperationHandler<any, any>>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should that be part of the Nexus SDK? Logic is trivial at present, but there I expect there will be some normalization work to be done at a later point (e.g. once we add decorators) which we wouldn't to diverge from one Nexus implementation to another. This also seems to be what we did in Java, and kind-of what we did in Go.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we discussed moving this to the Nexus SDK with Chad yesterday. I will do that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I missed that part of the conversation.

I will do that.

I only wanted to confirm that we agree on direction. I can do it myself if you don't have time/prefer not to do it (same apply to all suggestions I make on this PR).

import getPort from 'get-port';
import * as nexus from 'nexus-rpc';
import * as protoJsonSerializer from 'proto3-json-serializer';
import * as temporalnexus from '@temporalio/nexus';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dependency is missing in package.json and tsconfig.json

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will fix.

});

Runtime.install({ logger });
t.context.httpPort = await getPort();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO(JWH): This will be flaky in CI due to port collisions, and users will often need to do that themselves. Move this to Core.

test.beforeEach(async (t) => {
const taskQueue = t.title + randomUUID();
const { env } = t.context;
const response = await env.connection.operatorService.createNexusEndpoint({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand this is the way for now, but what's the intent regarding this? Do we expect users to have to do the same in their own Temporal+Nexus tests?

const input = await decodeFromPayload(this.dataConverter, payload);
if (typeof this.handler === 'function') {
const handler = this.handler as nexus.SyncOperationHandler<unknown, unknown>;
const output = await this.invokeUserCode('startOperation', handler.bind(undefined, input, options));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be preferable to add some defensive checks here to prevent a user from returning a HandlerStartOperationResultSync or HandlerStartOperationResultAsync from their SyncOperationHandler. Sounds like an easy error to make. But I don't think there's currently a safe way of doing this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants