Repository navigation
fix: make a list<u8> resource method parameter the proto bytes of its field - #407
Merged
tconley1428 merged 3 commits intoNov 12, 2026
Merged
Conversation
A proto-backed record reads that field as `bytes`, so a list of integers could not fill it.
moetemp
added a commit
that referenced
this pull request
Nov 12, 2026
# Conflicts: # GUIDE.md # src/planning/resource_resolution.rs
moetemp
added a commit
that referenced
this pull request
Nov 12, 2026
#407 sits on top of the stream sample PR, so its diff shows the change to the sample.
moetemp
added a commit
that referenced
this pull request
Nov 12, 2026
The API dropped the read position, the close item and the client epoch. The `read` parameter needs #407 to be `bytes`.
# Conflicts: # GUIDE.md # src/planning/resource_resolution.rs
The stream `read` takes its `from_offset` as `bytes` in Python, TypeScript and .NET.
moetemp
added a commit
to temporalio/api
that referenced
this pull request
Nov 12, 2026
That revision makes a list<u8> parameter of a resource method the bytes of its proto field, which the read's from-offset needs.
moetemp
marked this pull request as ready for review
November 12, 2026 17:55
tconley1428
approved these changes
Nov 12, 2026
moetemp
added a commit
that referenced
this pull request
Nov 13, 2026
…#895 (#374) * fix(typescript): infer Long from protobuf resource bindings * feat: let a resource declare its type parameters A resource with `@nexus.type-parameter "<alias>"` is generic in Python, TypeScript and .NET. An operation that returns it, or that its method binds, takes a runtime type for each type parameter of its output, so a response decodes its generic values. * test: drop the low-value type parameter rejection fixtures The kept fixtures cover an undeclared type parameter, a generic resource in a record field or in a result with no proto message, and a language that refuses it. The others test directive syntax, rare name clashes or another copy of a kept check. * docs: shorten the generic resources section of the guide * test: assert the full message of each type parameter rejection * docs: say why a generic handle keeps its runtime types Co-authored-by: Tim Conley <timothy.conley@temporal.io> * feat: infer the type parameters of a resource A resource that uses a type parameter, also through its records, is generic in it, as a record is. So a resource needs no annotation, and `@nexus.type-parameter` is valid only on a type alias again. * fix(python): decode generic resources from their type arguments (#367) * fix: infer resource type parameters from results in declaration order Only a returned resource gives its type parameters, as the guide says, so a parameter or field that holds one doesn't. The declaration order doesn't change when a method moves. * fix: refuse a generic resource anywhere in a record field A list, map, tuple, result or variant case builds the resource from the record's message too, so it gets no type argument either. * fix: say that a direct generic resource result isn't supported yet The old reason said a proto message holds the type arguments, which it doesn't. * fix: refuse runtime type names that collide with each other or a method Two type parameters such as `body` and `body-t` give the same runtime type name, and a method can take it too. Either one makes a duplicate parameter or member. * fix(python): keep the runtime output type in the System Nexus dispatch The typed hook of a generic response took only the request, so the terminal decoded `ReadRecordsResponse[Turn]` with the plain class and lost `Turn`. * fix(dotnet): send generic System Nexus operations to the generic hook C# generics are invariant, so the `<object>` checks refused a `Stream<Turn>` call. The dispatch now compares generic type definitions and keeps the call's types. * fix(python): read the type argument of a subclassed generic handle A subclass such as `class TurnStream(Stream[Turn])` has no `__orig_class__`, so the handle reads `__orig_bases__` too. A factory tests `body_type is None`, since a class can be falsy. * test: cover a typed TypeScript read and a bare Python handle class The TypeScript stream test now decodes a body with a runtime type. The Python test decodes to the bare handle class, since a generic alias fails for another reason. * chore: fold the generic resource changelog and describe the fixes No release has the Python constructor change, so it belongs with the added entry. The fixed entry now describes the System Nexus dispatch fixes. * docs: reword the generic resource comments and wrap long lines A few comments used "name" as a verb, ran over two lines or over 100 columns. The guide's first paragraph read as one long sentence. * chore: fold the generic resource changelog into one entry * chore: trim the generic resource tests and docs Drop the tests that repeat another test or restate generated code, and keep the ones that guard behavior. Shorten the guide section and the comments. * feat(python): take a resource's runtime types as a subscript Python can't subscript a function, so each generic call is an object whose subscript binds the types, as in `client.get_stream[Turn](ref=ref)`. A factory's optional field defaults to `None`. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * feat: close a resource at the end of async with `@nexus.close` marks the method that ends a resource. Python calls it at the end of `async with`, and a call that returns the resource returns a `ResourceCall`. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * feat: add @nexus.varargs and flatten a list of one-field records `@nexus.varargs` makes a resource method take a list's items as positional arguments, and a list of a flattened one-field record takes that field's values. The Stream's publish takes values and returns the last offset. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * feat(python): add a Workflow resource factory Workflow code gets a handle on an existing Stream with `get_stream[T]`, from the fields of its ref, with no call. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * feat(python): add RPC options to client calls Each client call and client resource method takes `rpc_metadata` and `rpc_timeout`, and passes them to the `start_operation` of its Nexus client. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * feat: flatten the record of one request field `@nexus.flatten-in-api` on a request field makes that call take the fields of the field's record, and the models keep the record. The Stream create takes the id, the owner and the backend of its ref this way, and the other Stream calls keep `ref`. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * feat: limit `@nexus.api-omit` to some targets A field can leave the API of one target and take a source in another. A call of a target that omits a required list sends it empty. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * chore(samples): sync the stream sample with StreamWriter temporalio/api#895 moves the Producer and the withdrawal fields of a write into one StreamWriter. The sample WIT also takes the final directives of the open nexgen PRs. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * chore(samples): sync the stream sample with the owner and withdrawal removal This syncs the stream sample with the owner and withdrawal removal. The WIT and the stream protos drop the owner, the read-side withdrawal machinery and the `OWNER_*` close statuses, and add the `StreamClose` read item with the kind-oneof `Producer`. The Python, TypeScript and .NET bindings and samples are regenerated, and the hand-written support and tests follow. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * chore(samples): format the stream test with oxfmt Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Omitted an operation's public function with `@nexus.api-omit`. A top-level `func` with `@nexus.api-omit` keeps the operation registered and bound to its resource, but drops the public top-level function and its export. A caller reaches the operation through the resource. On a function the directive omits the workflow top-level function, so its `targets` must include `workflow`. The stream sample omits four operations, so `append-records`, `close-stream`, `read-records` and `describe-stream` have no public function and the `Stream` resource is the way in. `create-stream` stays public, since the Workflow has no other way to create a Stream. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Made a `list<u8>` case of a proto oneof the proto `bytes`. A variant with `@nexus.proto` reads an unnamed `list<u8>` case as the proto `bytes`, the same rule as a field of a proto-backed record. Another width still fails against a `bytes` member. The stream sample's read position is a oneof of a `bytes` offset and a start enum, which showed two more gaps. An enum case kept its proto type, so its WIT enum was pruned. It is now planned as the bound enum, like a record case. A resource method parameter named after the oneof was dropped, since the request plan walked the oneof's members. It now binds to the record's oneof field. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Synced the stream sample with the latest temporalio/api#895. The WIT and protos are those of temporalio/api#895 at b0b2657e. The WIT adds the `@nexus.api-omit` of #405 and the `@nexus.generic` of #399, which the API WIT doesn't have yet. A directive that nexgen doesn't support yet stays a TODO comment, and an offset is an inline `list<u8>`, since nexgen reads only that as `bytes`. * Synced the stream sample with the removal of `create_wait`. temporalio/api#895 at 0ac6b717 drops `create_wait` from a read and renumbers the later fields, so the WIT, the protos and their bindings follow it. * Synced the stream sample with the Stream writes and items reshape. temporalio/api#895 at 23fac086 drops `StreamWriter` for a plain `producer`, moves the offset into the read items, and makes the required fields required. The WIT is the API's as is, and the support helpers are the `producer` sources that it names. * Synced the stream sample with the close offset and namespace comments. * Synced the stream sample with the one fence rule for every Producer. * Synced the stream sample with the unknown writer removal. * Synced the stream sample with the Workflow Producer comments. The API comments now say when a Workflow Producer writes and when it only closes. * Synced the stream sample with the read and Record reshape. The API dropped the read position, the close item and the client epoch. The `read` parameter needs #407 to be `bytes`. * fix: make a `list<u8>` resource method parameter the proto `bytes` of its field (#407) * Typed a `list<u8>` method parameter of a proto `bytes` field as bytes. A proto-backed record reads that field as `bytes`, so a list of integers could not fill it. * Regenerated the stream sample with the bytes parameter. The stream `read` takes its `from_offset` as `bytes` in Python, TypeScript and .NET. * Synced the stream sample with the Store rename and the append result. The API renamed `backend` to `store` in the Stream ref and dropped the `append` result transform. So `append` returns the `AppendRecordsResponse` in every target. The two optional `producer` parameters of the API's WIT are not in this sync, since they need a generator change. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Synced the stream sample with the sequence parameter of append. The API gave `append` an optional `sequence`, so a caller can number a batch and the Store can dedupe a resent one. The optional `producer` parameter still waits for the generator change. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Synced the stream sample with the offset and Workflow Producer comments. The API now says that two offsets of one Stream compare bytewise. It also says that Workflow code on a Store writes through a Local Activity. Only comments changed, so the samples only move in docs. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Told why the stream sample offset comment matters. --------- Co-authored-by: Tim Conley <timothy.conley@temporal.io>
tconley1428
added a commit
that referenced
this pull request
Nov 13, 2026
…parameter in Python (#408) * fix(typescript): infer Long from protobuf resource bindings * feat: let a resource declare its type parameters A resource with `@nexus.type-parameter "<alias>"` is generic in Python, TypeScript and .NET. An operation that returns it, or that its method binds, takes a runtime type for each type parameter of its output, so a response decodes its generic values. * test: drop the low-value type parameter rejection fixtures The kept fixtures cover an undeclared type parameter, a generic resource in a record field or in a result with no proto message, and a language that refuses it. The others test directive syntax, rare name clashes or another copy of a kept check. * docs: shorten the generic resources section of the guide * test: assert the full message of each type parameter rejection * docs: say why a generic handle keeps its runtime types Co-authored-by: Tim Conley <timothy.conley@temporal.io> * feat: infer the type parameters of a resource A resource that uses a type parameter, also through its records, is generic in it, as a record is. So a resource needs no annotation, and `@nexus.type-parameter` is valid only on a type alias again. * fix(python): decode generic resources from their type arguments (#367) * fix: infer resource type parameters from results in declaration order Only a returned resource gives its type parameters, as the guide says, so a parameter or field that holds one doesn't. The declaration order doesn't change when a method moves. * fix: refuse a generic resource anywhere in a record field A list, map, tuple, result or variant case builds the resource from the record's message too, so it gets no type argument either. * fix: say that a direct generic resource result isn't supported yet The old reason said a proto message holds the type arguments, which it doesn't. * fix: refuse runtime type names that collide with each other or a method Two type parameters such as `body` and `body-t` give the same runtime type name, and a method can take it too. Either one makes a duplicate parameter or member. * fix(python): keep the runtime output type in the System Nexus dispatch The typed hook of a generic response took only the request, so the terminal decoded `ReadRecordsResponse[Turn]` with the plain class and lost `Turn`. * fix(dotnet): send generic System Nexus operations to the generic hook C# generics are invariant, so the `<object>` checks refused a `Stream<Turn>` call. The dispatch now compares generic type definitions and keeps the call's types. * fix(python): read the type argument of a subclassed generic handle A subclass such as `class TurnStream(Stream[Turn])` has no `__orig_class__`, so the handle reads `__orig_bases__` too. A factory tests `body_type is None`, since a class can be falsy. * test: cover a typed TypeScript read and a bare Python handle class The TypeScript stream test now decodes a body with a runtime type. The Python test decodes to the bare handle class, since a generic alias fails for another reason. * chore: fold the generic resource changelog and describe the fixes No release has the Python constructor change, so it belongs with the added entry. The fixed entry now describes the System Nexus dispatch fixes. * docs: reword the generic resource comments and wrap long lines A few comments used "name" as a verb, ran over two lines or over 100 columns. The guide's first paragraph read as one long sentence. * chore: fold the generic resource changelog into one entry * chore: trim the generic resource tests and docs Drop the tests that repeat another test or restate generated code, and keep the ones that guard behavior. Shorten the guide section and the comments. * feat(python): take a resource's runtime types as a subscript Python can't subscript a function, so each generic call is an object whose subscript binds the types, as in `client.get_stream[Turn](ref=ref)`. A factory's optional field defaults to `None`. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * feat: close a resource at the end of async with `@nexus.close` marks the method that ends a resource. Python calls it at the end of `async with`, and a call that returns the resource returns a `ResourceCall`. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * feat: add @nexus.varargs and flatten a list of one-field records `@nexus.varargs` makes a resource method take a list's items as positional arguments, and a list of a flattened one-field record takes that field's values. The Stream's publish takes values and returns the last offset. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * feat(python): add a Workflow resource factory Workflow code gets a handle on an existing Stream with `get_stream[T]`, from the fields of its ref, with no call. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * feat(python): add RPC options to client calls Each client call and client resource method takes `rpc_metadata` and `rpc_timeout`, and passes them to the `start_operation` of its Nexus client. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * feat: flatten the record of one request field `@nexus.flatten-in-api` on a request field makes that call take the fields of the field's record, and the models keep the record. The Stream create takes the id, the owner and the backend of its ref this way, and the other Stream calls keep `ref`. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * feat: limit `@nexus.api-omit` to some targets A field can leave the API of one target and take a source in another. A call of a target that omits a required list sends it empty. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * chore(samples): sync the stream sample with StreamWriter temporalio/api#895 moves the Producer and the withdrawal fields of a write into one StreamWriter. The sample WIT also takes the final directives of the open nexgen PRs. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * chore(samples): sync the stream sample with the owner and withdrawal removal This syncs the stream sample with the owner and withdrawal removal. The WIT and the stream protos drop the owner, the read-side withdrawal machinery and the `OWNER_*` close statuses, and add the `StreamClose` read item with the kind-oneof `Producer`. The Python, TypeScript and .NET bindings and samples are regenerated, and the hand-written support and tests follow. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * chore(samples): format the stream test with oxfmt Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Omitted an operation's public function with `@nexus.api-omit`. A top-level `func` with `@nexus.api-omit` keeps the operation registered and bound to its resource, but drops the public top-level function and its export. A caller reaches the operation through the resource. On a function the directive omits the workflow top-level function, so its `targets` must include `workflow`. The stream sample omits four operations, so `append-records`, `close-stream`, `read-records` and `describe-stream` have no public function and the `Stream` resource is the way in. `create-stream` stays public, since the Workflow has no other way to create a Stream. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Made a `list<u8>` case of a proto oneof the proto `bytes`. A variant with `@nexus.proto` reads an unnamed `list<u8>` case as the proto `bytes`, the same rule as a field of a proto-backed record. Another width still fails against a `bytes` member. The stream sample's read position is a oneof of a `bytes` offset and a start enum, which showed two more gaps. An enum case kept its proto type, so its WIT enum was pruned. It is now planned as the bound enum, like a record case. A resource method parameter named after the oneof was dropped, since the request plan walked the oneof's members. It now binds to the record's oneof field. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Synced the stream sample with the latest temporalio/api#895. The WIT and protos are those of temporalio/api#895 at b0b2657e. The WIT adds the `@nexus.api-omit` of #405 and the `@nexus.generic` of #399, which the API WIT doesn't have yet. A directive that nexgen doesn't support yet stays a TODO comment, and an offset is an inline `list<u8>`, since nexgen reads only that as `bytes`. * Synced the stream sample with the removal of `create_wait`. temporalio/api#895 at 0ac6b717 drops `create_wait` from a read and renumbers the later fields, so the WIT, the protos and their bindings follow it. * Synced the stream sample with the Stream writes and items reshape. temporalio/api#895 at 23fac086 drops `StreamWriter` for a plain `producer`, moves the offset into the read items, and makes the required fields required. The WIT is the API's as is, and the support helpers are the `producer` sources that it names. * Synced the stream sample with the close offset and namespace comments. * Synced the stream sample with the one fence rule for every Producer. * Synced the stream sample with the unknown writer removal. * Synced the stream sample with the Workflow Producer comments. The API comments now say when a Workflow Producer writes and when it only closes. * Synced the stream sample with the read and Record reshape. The API dropped the read position, the close item and the client epoch. The `read` parameter needs #407 to be `bytes`. * fix: make a `list<u8>` resource method parameter the proto `bytes` of its field (#407) * Typed a `list<u8>` method parameter of a proto `bytes` field as bytes. A proto-backed record reads that field as `bytes`, so a list of integers could not fill it. * Regenerated the stream sample with the bytes parameter. The stream `read` takes its `from_offset` as `bytes` in Python, TypeScript and .NET. * Filled an unset optional parameter from its field's source in Python. The generated code passed an optional parameter as it was and dropped the source of the field. A caller that passed nothing then sent no value for a required field. * Filled an unset optional parameter from its source in TypeScript. A resource method passed an optional parameter as it was and dropped the source of the field. A caller that passed nothing then sent no value for a required field. * Filled an unset optional parameter from its source in .NET. A resource method passed an optional parameter as it was and dropped the source of the field. The complete request also took a flattened list parameter as it was, so it did not compile. * Gave the stream sample's append and close an optional producer. The sample shows the source fallback in its generated Python, TypeScript and .NET. Its client source helper is `default_producer`, since it picks the Producer by where the code runs. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Synced the stream sample with the Store rename and the append result. The API renamed `backend` to `store` in the Stream ref and dropped the `append` result transform. So `append` returns the `AppendRecordsResponse` in every target. The two optional `producer` parameters of the API's WIT are not in this sync, since they need a generator change. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Said in the sample's Producer docs that a call can pass its own. The docs of both `producer` fields match the API's WIT, which has the two optional parameters. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Synced the stream sample with the sequence parameter of append. The API gave `append` an optional `sequence`, so a caller can number a batch and the Store can dedupe a resent one. The optional `producer` parameter still waits for the generator change. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Synced the stream sample with the offset and Workflow Producer comments. The API now says that two offsets of one Stream compare bytewise. It also says that Workflow code on a Store writes through a Local Activity. Only comments changed, so the samples only move in docs. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Imported temporalio.client only for type checking in Python clients. (#409) The SDK's `Client` inherits the generated extension, so `temporalio.client` imports the generated package. A run-time import back made importing the package first a circular import. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Filled an unset optional parameter from its field's default in Python. A resource method passed an optional parameter as it was and dropped the default of the field. A source is a helper value and never an argument, so it no longer fills an optional parameter. * Took the source fallback out of TypeScript resource methods. A resource method hands an optional parameter to its operation, which applies the field's default. A source is a helper value and never an argument, so it no longer fills an optional parameter. * Took the source fallback out of .NET resource methods. The stream sample's resource methods go through the options type, which applies the default. A source is a helper value and never an argument, so it no longer fills an optional parameter. * Gave the stream sample's Producer a default instead of a source. A default is an omittable argument, which the optional `producer` of `append` and `close` needs. The guide and the changelog describe the default rule for resource methods. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Told why the stream sample offset comment matters. --------- Co-authored-by: Tim Conley <timothy.conley@temporal.io>
tconley1428
added a commit
that referenced
this pull request
Nov 13, 2026
#410) * fix(typescript): infer Long from protobuf resource bindings * feat: let a resource declare its type parameters A resource with `@nexus.type-parameter "<alias>"` is generic in Python, TypeScript and .NET. An operation that returns it, or that its method binds, takes a runtime type for each type parameter of its output, so a response decodes its generic values. * test: drop the low-value type parameter rejection fixtures The kept fixtures cover an undeclared type parameter, a generic resource in a record field or in a result with no proto message, and a language that refuses it. The others test directive syntax, rare name clashes or another copy of a kept check. * docs: shorten the generic resources section of the guide * test: assert the full message of each type parameter rejection * docs: say why a generic handle keeps its runtime types Co-authored-by: Tim Conley <timothy.conley@temporal.io> * feat: infer the type parameters of a resource A resource that uses a type parameter, also through its records, is generic in it, as a record is. So a resource needs no annotation, and `@nexus.type-parameter` is valid only on a type alias again. * fix(python): decode generic resources from their type arguments (#367) * fix: infer resource type parameters from results in declaration order Only a returned resource gives its type parameters, as the guide says, so a parameter or field that holds one doesn't. The declaration order doesn't change when a method moves. * fix: refuse a generic resource anywhere in a record field A list, map, tuple, result or variant case builds the resource from the record's message too, so it gets no type argument either. * fix: say that a direct generic resource result isn't supported yet The old reason said a proto message holds the type arguments, which it doesn't. * fix: refuse runtime type names that collide with each other or a method Two type parameters such as `body` and `body-t` give the same runtime type name, and a method can take it too. Either one makes a duplicate parameter or member. * fix(python): keep the runtime output type in the System Nexus dispatch The typed hook of a generic response took only the request, so the terminal decoded `ReadRecordsResponse[Turn]` with the plain class and lost `Turn`. * fix(dotnet): send generic System Nexus operations to the generic hook C# generics are invariant, so the `<object>` checks refused a `Stream<Turn>` call. The dispatch now compares generic type definitions and keeps the call's types. * fix(python): read the type argument of a subclassed generic handle A subclass such as `class TurnStream(Stream[Turn])` has no `__orig_class__`, so the handle reads `__orig_bases__` too. A factory tests `body_type is None`, since a class can be falsy. * test: cover a typed TypeScript read and a bare Python handle class The TypeScript stream test now decodes a body with a runtime type. The Python test decodes to the bare handle class, since a generic alias fails for another reason. * chore: fold the generic resource changelog and describe the fixes No release has the Python constructor change, so it belongs with the added entry. The fixed entry now describes the System Nexus dispatch fixes. * docs: reword the generic resource comments and wrap long lines A few comments used "name" as a verb, ran over two lines or over 100 columns. The guide's first paragraph read as one long sentence. * chore: fold the generic resource changelog into one entry * chore: trim the generic resource tests and docs Drop the tests that repeat another test or restate generated code, and keep the ones that guard behavior. Shorten the guide section and the comments. * feat(python): take a resource's runtime types as a subscript Python can't subscript a function, so each generic call is an object whose subscript binds the types, as in `client.get_stream[Turn](ref=ref)`. A factory's optional field defaults to `None`. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * feat: close a resource at the end of async with `@nexus.close` marks the method that ends a resource. Python calls it at the end of `async with`, and a call that returns the resource returns a `ResourceCall`. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * feat: add @nexus.varargs and flatten a list of one-field records `@nexus.varargs` makes a resource method take a list's items as positional arguments, and a list of a flattened one-field record takes that field's values. The Stream's publish takes values and returns the last offset. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * feat(python): add a Workflow resource factory Workflow code gets a handle on an existing Stream with `get_stream[T]`, from the fields of its ref, with no call. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * feat(python): add RPC options to client calls Each client call and client resource method takes `rpc_metadata` and `rpc_timeout`, and passes them to the `start_operation` of its Nexus client. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * feat: flatten the record of one request field `@nexus.flatten-in-api` on a request field makes that call take the fields of the field's record, and the models keep the record. The Stream create takes the id, the owner and the backend of its ref this way, and the other Stream calls keep `ref`. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * feat: limit `@nexus.api-omit` to some targets A field can leave the API of one target and take a source in another. A call of a target that omits a required list sends it empty. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * chore(samples): sync the stream sample with StreamWriter temporalio/api#895 moves the Producer and the withdrawal fields of a write into one StreamWriter. The sample WIT also takes the final directives of the open nexgen PRs. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * chore(samples): sync the stream sample with the owner and withdrawal removal This syncs the stream sample with the owner and withdrawal removal. The WIT and the stream protos drop the owner, the read-side withdrawal machinery and the `OWNER_*` close statuses, and add the `StreamClose` read item with the kind-oneof `Producer`. The Python, TypeScript and .NET bindings and samples are regenerated, and the hand-written support and tests follow. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * chore(samples): format the stream test with oxfmt Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Omitted an operation's public function with `@nexus.api-omit`. A top-level `func` with `@nexus.api-omit` keeps the operation registered and bound to its resource, but drops the public top-level function and its export. A caller reaches the operation through the resource. On a function the directive omits the workflow top-level function, so its `targets` must include `workflow`. The stream sample omits four operations, so `append-records`, `close-stream`, `read-records` and `describe-stream` have no public function and the `Stream` resource is the way in. `create-stream` stays public, since the Workflow has no other way to create a Stream. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Made a `list<u8>` case of a proto oneof the proto `bytes`. A variant with `@nexus.proto` reads an unnamed `list<u8>` case as the proto `bytes`, the same rule as a field of a proto-backed record. Another width still fails against a `bytes` member. The stream sample's read position is a oneof of a `bytes` offset and a start enum, which showed two more gaps. An enum case kept its proto type, so its WIT enum was pruned. It is now planned as the bound enum, like a record case. A resource method parameter named after the oneof was dropped, since the request plan walked the oneof's members. It now binds to the record's oneof field. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Synced the stream sample with the latest temporalio/api#895. The WIT and protos are those of temporalio/api#895 at b0b2657e. The WIT adds the `@nexus.api-omit` of #405 and the `@nexus.generic` of #399, which the API WIT doesn't have yet. A directive that nexgen doesn't support yet stays a TODO comment, and an offset is an inline `list<u8>`, since nexgen reads only that as `bytes`. * Synced the stream sample with the removal of `create_wait`. temporalio/api#895 at 0ac6b717 drops `create_wait` from a read and renumbers the later fields, so the WIT, the protos and their bindings follow it. * Synced the stream sample with the Stream writes and items reshape. temporalio/api#895 at 23fac086 drops `StreamWriter` for a plain `producer`, moves the offset into the read items, and makes the required fields required. The WIT is the API's as is, and the support helpers are the `producer` sources that it names. * Synced the stream sample with the close offset and namespace comments. * Synced the stream sample with the one fence rule for every Producer. * Synced the stream sample with the unknown writer removal. * Synced the stream sample with the Workflow Producer comments. The API comments now say when a Workflow Producer writes and when it only closes. * Synced the stream sample with the read and Record reshape. The API dropped the read position, the close item and the client epoch. The `read` parameter needs #407 to be `bytes`. * fix: make a `list<u8>` resource method parameter the proto `bytes` of its field (#407) * Typed a `list<u8>` method parameter of a proto `bytes` field as bytes. A proto-backed record reads that field as `bytes`, so a list of integers could not fill it. * Regenerated the stream sample with the bytes parameter. The stream `read` takes its `from_offset` as `bytes` in Python, TypeScript and .NET. * Filled an unset optional parameter from its field's source in Python. The generated code passed an optional parameter as it was and dropped the source of the field. A caller that passed nothing then sent no value for a required field. * Filled an unset optional parameter from its source in TypeScript. A resource method passed an optional parameter as it was and dropped the source of the field. A caller that passed nothing then sent no value for a required field. * Filled an unset optional parameter from its source in .NET. A resource method passed an optional parameter as it was and dropped the source of the field. The complete request also took a flattened list parameter as it was, so it did not compile. * Gave the stream sample's append and close an optional producer. The sample shows the source fallback in its generated Python, TypeScript and .NET. Its client source helper is `default_producer`, since it picks the Producer by where the code runs. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Imported temporalio.client only for type checking in Python clients. The SDK's `Client` inherits the generated extension, so `temporalio.client` imports the generated package. A run-time import back made importing the package first a circular import. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Synced the stream sample with the Store rename and the append result. The API renamed `backend` to `store` in the Stream ref and dropped the `append` result transform. So `append` returns the `AppendRecordsResponse` in every target. The two optional `producer` parameters of the API's WIT are not in this sync, since they need a generator change. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Said in the sample's Producer docs that a call can pass its own. The docs of both `producer` fields match the API's WIT, which has the two optional parameters. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Synced the stream sample with the sequence parameter of append. The API gave `append` an optional `sequence`, so a caller can number a batch and the Store can dedupe a resent one. The optional `producer` parameter still waits for the generator change. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Synced the stream sample with the offset and Workflow Producer comments. The API now says that two offsets of one Stream compare bytewise. It also says that Workflow code on a Store writes through a Local Activity. Only comments changed, so the samples only move in docs. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Left a resource method out of one Python target with `@nexus.api-omit`. A resource method could not be left out of one target, and the stream sample needs a Workflow `Stream` with no `append` and no `close`. Without its close method, a target's resource is not a context manager, and a method that no target keeps is refused. * Left an api-omitted resource method out of the TypeScript Workflow API. A TypeScript resource method calls the Workflow API, so a method omitted for `workflow` goes from the class. Its request helper goes too when no other method used it. * Left an api-omitted resource method out of the .NET Workflow API. A .NET resource method calls the Workflow API, so a method omitted for `workflow` goes from the class. The request overload that only it called becomes private. * Refused a resource method omitted for the Workflow target in Go. Go has no sample that would show its output without the method, so it refuses the directive as it refuses `@nexus.varargs`. A method omitted only for `client` changes nothing in Go. * Left `append` and `close` out of the stream sample's Workflow `Stream`. In this release Workflow code can't append to a Stream or close it, so the Workflow target omits both methods. The client `Stream` keeps them, and the Workflow `create_stream` is a plain call. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Imported temporalio.client only for type checking in Python clients. (#409) The SDK's `Client` inherits the generated extension, so `temporalio.client` imports the generated package. A run-time import back made importing the package first a circular import. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Filled an unset optional parameter from its field's default in Python. A resource method passed an optional parameter as it was and dropped the default of the field. A source is a helper value and never an argument, so it no longer fills an optional parameter. * Took the source fallback out of TypeScript resource methods. A resource method hands an optional parameter to its operation, which applies the field's default. A source is a helper value and never an argument, so it no longer fills an optional parameter. * Took the source fallback out of .NET resource methods. The stream sample's resource methods go through the options type, which applies the default. A source is a helper value and never an argument, so it no longer fills an optional parameter. * Gave the stream sample's Producer a default instead of a source. A default is an omittable argument, which the optional `producer` of `append` and `close` needs. The guide and the changelog describe the default rule for resource methods. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Told why the stream sample offset comment matters. * Rewrote the architecture note on omitted resource methods. The note listed type names and did not say what happens. It says what the directive does and that planning owns the choice. Co-authored-by: Tim Conley <timothy.conley@temporal.io> * Cut the architecture note on omitted resource methods to its gist. --------- Co-authored-by: Tim Conley <timothy.conley@temporal.io>
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed?
This PR makes a
list<u8>resource method parameterbyteswhen it fills a protobytesfield, like that field.It sits on #374, so the diff shows the stream sample's
readtaking itsfrom_offsetasbytes.Why?
Without it,
readtakeslist[int],number[]andIReadOnlyList<uint>for abytesfield, so #374 fails the Python type check and the .NET build.How did you test it?
A Rust unit test and the regenerated stream sample.
cargo validatepasses.Please note that a
list<u16>orlist<u32>parameter of abytesfield also becomesbytes.