Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,27 @@ return StartedWorkflow(
If a resource constructor field name cannot be found in either, the generator
raises `InvalidResource`.

**A result record that holds the resource.** An operation can also return a
proto-backed record with one `own<T>` field and other fields. The resource binds
from the record's message as above, and the other fields come from the fields of
the same name. The record is the API model only, and it declares only the fields
that its caller sees. Its message travels as a wire record that takes the proto
message's name. Python, TypeScript, .NET and Go generate it. In Python, the
record lives in its resource's module.

```wit
/// @nexus.proto "acme.jobs.v1.CreateJobResponse"
record create-job-result {
job: own<job>,
created: option<bool>,
}
```

```python
result = await handle # CreateJobResponse
return CreateJobResult(job=Job(job_id=result.job_id), created=result.created)
```

### Edge Cases and Error Conditions

#### Same field name in constructor and method parameter
Expand Down
3 changes: 2 additions & 1 deletion advanced/samples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,4 +206,5 @@ protoc -I descriptors -I "$API" --csharp_out=dotnet/proto streamsample/v1/stream
```

The TypeScript `typescript/proto/streamsample.d.ts` mirrors the proto by hand,
with only the `I<Message>` interfaces that the generated code imports.
with only the `I<Message>` interfaces and the enums that the generated code
imports.
Binary file modified advanced/samples/descriptors/stream_service.bin
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,17 @@ message CreateStreamRequest {
google.protobuf.Duration retention = 3;
}

enum StreamStatus {
STREAM_STATUS_UNSPECIFIED = 0;
STREAM_STATUS_OPEN = 1;
STREAM_STATUS_CLOSED = 2;
STREAM_STATUS_EXPIRED = 3;
}

message CreateStreamResponse {
StreamRef stream = 1;
bool created = 2;
StreamStatus status = 3;
}

message AppendRecordsRequest {
Expand Down
99 changes: 75 additions & 24 deletions advanced/samples/dotnet/proto/StreamService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,36 +34,40 @@ static StreamServiceReflection() {
"bXBvcmFsLmFwaS5jb21tb24udjEuUGF5bG9hZCJ/ChNDcmVhdGVTdHJlYW1S",
"ZXF1ZXN0EhEKCW5hbWVzcGFjZRgBIAEoCRInCgNyZWYYAiABKAsyGi5zdHJl",
"YW1zYW1wbGUudjEuU3RyZWFtUmVmEiwKCXJldGVudGlvbhgDIAEoCzIZLmdv",
"b2dsZS5wcm90b2J1Zi5EdXJhdGlvbiJTChRDcmVhdGVTdHJlYW1SZXNwb25z",
"ZRIqCgZzdHJlYW0YASABKAsyGi5zdHJlYW1zYW1wbGUudjEuU3RyZWFtUmVm",
"Eg8KB2NyZWF0ZWQYAiABKAgilwEKFEFwcGVuZFJlY29yZHNSZXF1ZXN0EhEK",
"CW5hbWVzcGFjZRgBIAEoCRIqCgZzdHJlYW0YAiABKAsyGi5zdHJlYW1zYW1w",
"bGUudjEuU3RyZWFtUmVmEhAKCHNlcXVlbmNlGAMgASgDEi4KB3JlY29yZHMY",
"BCADKAsyHS5zdHJlYW1zYW1wbGUudjEuQXBwZW5kUmVjb3JkIkIKFUFwcGVu",
"ZFJlY29yZHNSZXNwb25zZRIUCgxmaXJzdF9vZmZzZXQYASABKAMSEwoLbGFz",
"dF9vZmZzZXQYAiABKAMihAEKEkNsb3NlU3RyZWFtUmVxdWVzdBIRCgluYW1l",
"c3BhY2UYASABKAkSKgoGc3RyZWFtGAIgASgLMhouc3RyZWFtc2FtcGxlLnYx",
"LlN0cmVhbVJlZhIvCgZyZXN1bHQYAyABKAsyHy50ZW1wb3JhbC5hcGkuY29t",
"bW9uLnYxLlBheWxvYWQiFQoTQ2xvc2VTdHJlYW1SZXNwb25zZSK2AQoSUmVh",
"ZFJlY29yZHNSZXF1ZXN0EhEKCW5hbWVzcGFjZRgBIAEoCRIqCgZzdHJlYW0Y",
"AiABKAsyGi5zdHJlYW1zYW1wbGUudjEuU3RyZWFtUmVmEhMKC2Zyb21fb2Zm",
"c2V0GAMgASgDEg4KBnRvcGljcxgEIAMoCRITCgttYXhfcmVjb3JkcxgFIAEo",
"BRInCgR3YWl0GAYgASgLMhkuZ29vZ2xlLnByb3RvYnVmLkR1cmF0aW9uImgK",
"E1JlYWRSZWNvcmRzUmVzcG9uc2USLgoHcmVjb3JkcxgBIAMoCzIdLnN0cmVh",
"bXNhbXBsZS52MS5TdHJlYW1SZWNvcmQSEwoLbmV4dF9vZmZzZXQYAiABKAMS",
"DAoEZG9uZRgDIAEoCCJWChVEZXNjcmliZVN0cmVhbVJlcXVlc3QSEQoJbmFt",
"ZXNwYWNlGAEgASgJEioKBnN0cmVhbRgCIAEoCzIaLnN0cmVhbXNhbXBsZS52",
"MS5TdHJlYW1SZWYiWwoWRGVzY3JpYmVTdHJlYW1SZXNwb25zZRITCgtsYXN0",
"X29mZnNldBgBIAEoAxIsCglyZXRlbnRpb24YAiABKAsyGS5nb29nbGUucHJv",
"dG9idWYuRHVyYXRpb25CEqoCD1N0cmVhbVNhbXBsZS5WMWIGcHJvdG8z"));
"b2dsZS5wcm90b2J1Zi5EdXJhdGlvbiKCAQoUQ3JlYXRlU3RyZWFtUmVzcG9u",
"c2USKgoGc3RyZWFtGAEgASgLMhouc3RyZWFtc2FtcGxlLnYxLlN0cmVhbVJl",
"ZhIPCgdjcmVhdGVkGAIgASgIEi0KBnN0YXR1cxgDIAEoDjIdLnN0cmVhbXNh",
"bXBsZS52MS5TdHJlYW1TdGF0dXMilwEKFEFwcGVuZFJlY29yZHNSZXF1ZXN0",
"EhEKCW5hbWVzcGFjZRgBIAEoCRIqCgZzdHJlYW0YAiABKAsyGi5zdHJlYW1z",
"YW1wbGUudjEuU3RyZWFtUmVmEhAKCHNlcXVlbmNlGAMgASgDEi4KB3JlY29y",
"ZHMYBCADKAsyHS5zdHJlYW1zYW1wbGUudjEuQXBwZW5kUmVjb3JkIkIKFUFw",
"cGVuZFJlY29yZHNSZXNwb25zZRIUCgxmaXJzdF9vZmZzZXQYASABKAMSEwoL",
"bGFzdF9vZmZzZXQYAiABKAMihAEKEkNsb3NlU3RyZWFtUmVxdWVzdBIRCglu",
"YW1lc3BhY2UYASABKAkSKgoGc3RyZWFtGAIgASgLMhouc3RyZWFtc2FtcGxl",
"LnYxLlN0cmVhbVJlZhIvCgZyZXN1bHQYAyABKAsyHy50ZW1wb3JhbC5hcGku",
"Y29tbW9uLnYxLlBheWxvYWQiFQoTQ2xvc2VTdHJlYW1SZXNwb25zZSK2AQoS",
"UmVhZFJlY29yZHNSZXF1ZXN0EhEKCW5hbWVzcGFjZRgBIAEoCRIqCgZzdHJl",
"YW0YAiABKAsyGi5zdHJlYW1zYW1wbGUudjEuU3RyZWFtUmVmEhMKC2Zyb21f",
"b2Zmc2V0GAMgASgDEg4KBnRvcGljcxgEIAMoCRITCgttYXhfcmVjb3JkcxgF",
"IAEoBRInCgR3YWl0GAYgASgLMhkuZ29vZ2xlLnByb3RvYnVmLkR1cmF0aW9u",
"ImgKE1JlYWRSZWNvcmRzUmVzcG9uc2USLgoHcmVjb3JkcxgBIAMoCzIdLnN0",
"cmVhbXNhbXBsZS52MS5TdHJlYW1SZWNvcmQSEwoLbmV4dF9vZmZzZXQYAiAB",
"KAMSDAoEZG9uZRgDIAEoCCJWChVEZXNjcmliZVN0cmVhbVJlcXVlc3QSEQoJ",
"bmFtZXNwYWNlGAEgASgJEioKBnN0cmVhbRgCIAEoCzIaLnN0cmVhbXNhbXBs",
"ZS52MS5TdHJlYW1SZWYiWwoWRGVzY3JpYmVTdHJlYW1SZXNwb25zZRITCgts",
"YXN0X29mZnNldBgBIAEoAxIsCglyZXRlbnRpb24YAiABKAsyGS5nb29nbGUu",
"cHJvdG9idWYuRHVyYXRpb24qegoMU3RyZWFtU3RhdHVzEh0KGVNUUkVBTV9T",
"VEFUVVNfVU5TUEVDSUZJRUQQABIWChJTVFJFQU1fU1RBVFVTX09QRU4QARIY",
"ChRTVFJFQU1fU1RBVFVTX0NMT1NFRBACEhkKFVNUUkVBTV9TVEFUVVNfRVhQ",
"SVJFRBADQhKqAg9TdHJlYW1TYW1wbGUuVjFiBnByb3RvMw=="));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.DurationReflection.Descriptor, global::Temporalio.Api.Common.V1.MessageReflection.Descriptor, },
new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
new pbr::GeneratedClrTypeInfo(new[] {typeof(global::StreamSample.V1.StreamStatus), }, null, new pbr::GeneratedClrTypeInfo[] {
new pbr::GeneratedClrTypeInfo(typeof(global::StreamSample.V1.StreamRef), global::StreamSample.V1.StreamRef.Parser, new[]{ "Id", "WorkflowId" }, null, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::StreamSample.V1.AppendRecord), global::StreamSample.V1.AppendRecord.Parser, new[]{ "Topic", "Body" }, null, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::StreamSample.V1.StreamRecord), global::StreamSample.V1.StreamRecord.Parser, new[]{ "Offset", "Topic", "Body" }, null, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::StreamSample.V1.CreateStreamRequest), global::StreamSample.V1.CreateStreamRequest.Parser, new[]{ "Namespace", "Ref", "Retention" }, null, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::StreamSample.V1.CreateStreamResponse), global::StreamSample.V1.CreateStreamResponse.Parser, new[]{ "Stream", "Created" }, null, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::StreamSample.V1.CreateStreamResponse), global::StreamSample.V1.CreateStreamResponse.Parser, new[]{ "Stream", "Created", "Status" }, null, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::StreamSample.V1.AppendRecordsRequest), global::StreamSample.V1.AppendRecordsRequest.Parser, new[]{ "Namespace", "Stream", "Sequence", "Records" }, null, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::StreamSample.V1.AppendRecordsResponse), global::StreamSample.V1.AppendRecordsResponse.Parser, new[]{ "FirstOffset", "LastOffset" }, null, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::StreamSample.V1.CloseStreamRequest), global::StreamSample.V1.CloseStreamRequest.Parser, new[]{ "Namespace", "Stream", "Result" }, null, null, null, null),
Expand All @@ -77,6 +81,16 @@ static StreamServiceReflection() {
#endregion

}
#region Enums
public enum StreamStatus {
[pbr::OriginalName("STREAM_STATUS_UNSPECIFIED")] Unspecified = 0,
[pbr::OriginalName("STREAM_STATUS_OPEN")] Open = 1,
[pbr::OriginalName("STREAM_STATUS_CLOSED")] Closed = 2,
[pbr::OriginalName("STREAM_STATUS_EXPIRED")] Expired = 3,
}

#endregion

#region Messages
[global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")]
public sealed partial class StreamRef : pb::IMessage<StreamRef>
Expand Down Expand Up @@ -1165,6 +1179,7 @@ public CreateStreamResponse() {
public CreateStreamResponse(CreateStreamResponse other) : this() {
stream_ = other.stream_ != null ? other.stream_.Clone() : null;
created_ = other.created_;
status_ = other.status_;
_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
}

Expand Down Expand Up @@ -1198,6 +1213,18 @@ public bool Created {
}
}

/// <summary>Field number for the "status" field.</summary>
public const int StatusFieldNumber = 3;
private global::StreamSample.V1.StreamStatus status_ = global::StreamSample.V1.StreamStatus.Unspecified;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public global::StreamSample.V1.StreamStatus Status {
get { return status_; }
set {
status_ = value;
}
}

[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public override bool Equals(object other) {
Expand All @@ -1215,6 +1242,7 @@ public bool Equals(CreateStreamResponse other) {
}
if (!object.Equals(Stream, other.Stream)) return false;
if (Created != other.Created) return false;
if (Status != other.Status) return false;
return Equals(_unknownFields, other._unknownFields);
}

Expand All @@ -1224,6 +1252,7 @@ public override int GetHashCode() {
int hash = 1;
if (stream_ != null) hash ^= Stream.GetHashCode();
if (Created != false) hash ^= Created.GetHashCode();
if (Status != global::StreamSample.V1.StreamStatus.Unspecified) hash ^= Status.GetHashCode();
if (_unknownFields != null) {
hash ^= _unknownFields.GetHashCode();
}
Expand All @@ -1250,6 +1279,10 @@ public void WriteTo(pb::CodedOutputStream output) {
output.WriteRawTag(16);
output.WriteBool(Created);
}
if (Status != global::StreamSample.V1.StreamStatus.Unspecified) {
output.WriteRawTag(24);
output.WriteEnum((int) Status);
}
if (_unknownFields != null) {
_unknownFields.WriteTo(output);
}
Expand All @@ -1268,6 +1301,10 @@ public void WriteTo(pb::CodedOutputStream output) {
output.WriteRawTag(16);
output.WriteBool(Created);
}
if (Status != global::StreamSample.V1.StreamStatus.Unspecified) {
output.WriteRawTag(24);
output.WriteEnum((int) Status);
}
if (_unknownFields != null) {
_unknownFields.WriteTo(ref output);
}
Expand All @@ -1284,6 +1321,9 @@ public int CalculateSize() {
if (Created != false) {
size += 1 + 1;
}
if (Status != global::StreamSample.V1.StreamStatus.Unspecified) {
size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Status);
}
if (_unknownFields != null) {
size += _unknownFields.CalculateSize();
}
Expand All @@ -1305,6 +1345,9 @@ public void MergeFrom(CreateStreamResponse other) {
if (other.Created != false) {
Created = other.Created;
}
if (other.Status != global::StreamSample.V1.StreamStatus.Unspecified) {
Status = other.Status;
}
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
}

Expand Down Expand Up @@ -1335,6 +1378,10 @@ public void MergeFrom(pb::CodedInputStream input) {
Created = input.ReadBool();
break;
}
case 24: {
Status = (global::StreamSample.V1.StreamStatus) input.ReadEnum();
break;
}
}
}
#endif
Expand Down Expand Up @@ -1365,6 +1412,10 @@ public void MergeFrom(pb::CodedInputStream input) {
Created = input.ReadBool();
break;
}
case 24: {
Status = (global::StreamSample.V1.StreamStatus) input.ReadEnum();
break;
}
}
}
}
Expand Down
99 changes: 68 additions & 31 deletions advanced/samples/dotnet/wit/stream-service/Models.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,68 @@
namespace Nexgen.StreamService
{

[GeneratedCode("nexgen", null)]
public enum StreamStatus
{
StreamStatusUnspecified = 0,
StreamStatusOpen = 1,
StreamStatusClosed = 2,
StreamStatusExpired = 3,
}

[Temporalio.Converters.TemporalTransferTypeConverter(typeof(CreateStreamResponse.TransferTypeConverter))]
[GeneratedCode("nexgen", null)]
public record CreateStreamResponse
{
public StreamRef? Stream { get; init; }
public bool? Created { get; init; }
public StreamStatus? Status { get; init; }

internal static CreateStreamResponse FromTransferType(StreamSample.V1.CreateStreamResponse wire)
{
return new CreateStreamResponse()
{
Stream = wire.Stream == null ? null : StreamRef.FromTransferType(wire.Stream),
Created = wire.Created == false ? null : wire.Created,
Status = (int)wire.Status == 0 ? null : (StreamStatus)wire.Status,
};
}

internal StreamSample.V1.CreateStreamResponse ToTransferType()
{
var proto = new StreamSample.V1.CreateStreamResponse();
if (Stream is { } stream)
{
proto.Stream = (StreamSample.V1.StreamRef)stream.ToTransferType();
}
if (Created is { } created)
{
proto.Created = created;
}
if (Status is { } status)
{
proto.Status = (StreamSample.V1.StreamStatus)status;
}
return proto;
}

/// <summary>
/// Converts this model to and from its generated transfer type.
/// </summary>
public sealed class TransferTypeConverter : Temporalio.Converters.ITemporalTransferTypeConverter
{
/// <summary>Gets the generated transfer type.</summary>
public System.Type TransferType => typeof(StreamSample.V1.CreateStreamResponse);

/// <summary>Converts a model value to its transfer type.</summary>
public object? ToTransferType(object? value) => value is null ? null : ((CreateStreamResponse)value).ToTransferType();

/// <summary>Converts a transfer-type value to this model.</summary>
public object? FromTransferType(object? transferType) => transferType is null ? null : CreateStreamResponse.FromTransferType((StreamSample.V1.CreateStreamResponse)transferType);
}

}

[Temporalio.Converters.TemporalTransferTypeConverter(typeof(AppendRecord.TransferTypeConverter))]
[GeneratedCode("nexgen", null)]
public record AppendRecord
Expand Down Expand Up @@ -286,45 +348,20 @@ public sealed class TransferTypeConverter : Temporalio.Converters.ITemporalTrans

}

[Temporalio.Converters.TemporalTransferTypeConverter(typeof(CreateStreamResult.TransferTypeConverter))]
[GeneratedCode("nexgen", null)]
public record CreateStreamResult
{
public StreamRef? Stream { get; init; }

internal static CreateStreamResult FromTransferType(StreamSample.V1.CreateStreamResponse wire)
{
return new CreateStreamResult()
{
Stream = wire.Stream == null ? null : StreamRef.FromTransferType(wire.Stream),
};
}

internal StreamSample.V1.CreateStreamResponse ToTransferType()
{
var proto = new StreamSample.V1.CreateStreamResponse();
if (Stream is { } stream)
{
proto.Stream = (StreamSample.V1.StreamRef)stream.ToTransferType();
}
return proto;
}

/// <summary>
/// Converts this model to and from its generated transfer type.
/// Initializes a new instance of the <see cref="CreateStreamResult"/> class.
/// </summary>
public sealed class TransferTypeConverter : Temporalio.Converters.ITemporalTransferTypeConverter
public CreateStreamResult(Stream stream)
{
/// <summary>Gets the generated transfer type.</summary>
public System.Type TransferType => typeof(StreamSample.V1.CreateStreamResponse);

/// <summary>Converts a model value to its transfer type.</summary>
public object? ToTransferType(object? value) => value is null ? null : ((CreateStreamResult)value).ToTransferType();

/// <summary>Converts a transfer-type value to this model.</summary>
public object? FromTransferType(object? transferType) => transferType is null ? null : CreateStreamResult.FromTransferType((StreamSample.V1.CreateStreamResponse)transferType);
Stream = stream;
}

public Stream Stream { get; init; }
public bool? Created { get; init; }
public StreamStatus? Status { get; init; }
}

[Temporalio.Converters.TemporalTransferTypeConverter(typeof(DescribeStreamRequest.TransferTypeConverter))]
Expand Down
8 changes: 4 additions & 4 deletions advanced/samples/dotnet/wit/stream-service/Operations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,16 @@ public static class Operations
private const string StreamServiceEndpoint = "streams";

[GeneratedCode("nexgen", null)]
private static async Task<Stream> CreateStreamAsync(CreateStreamRequest request)
private static async Task<CreateStreamResult> CreateStreamAsync(CreateStreamRequest request)
{
var client = Workflow.CreateNexusWorkflowClient<IStreamService>(StreamServiceEndpoint);
var result = await client.ExecuteNexusOperationAsync<CreateStreamResult>(svc => svc.CreateStream(request)).ConfigureAwait(true);
return new Stream((result.Stream ?? throw new System.InvalidOperationException("CreateStream answered without `stream`")));
var result = await client.ExecuteNexusOperationAsync<CreateStreamResponse>(svc => svc.CreateStream(request)).ConfigureAwait(true);
return new CreateStreamResult(new Stream((result.Stream ?? throw new System.InvalidOperationException("CreateStream answered without `stream`")))) { Created = result.Created, Status = result.Status };
}

/// <param name="options">Options for the operation.</param>
[GeneratedCode("nexgen", null)]
public static Task<Stream> CreateStreamAsync(CreateStreamOptions? options = null)
public static Task<CreateStreamResult> CreateStreamAsync(CreateStreamOptions? options = null)
{
var request = new CreateStreamRequest()
{
Expand Down
Loading
Loading