diff --git a/Google.Api.Generator.Tests/ProtoTest.cs b/Google.Api.Generator.Tests/ProtoTest.cs index 7882c9da..f129472c 100644 --- a/Google.Api.Generator.Tests/ProtoTest.cs +++ b/Google.Api.Generator.Tests/ProtoTest.cs @@ -251,7 +251,7 @@ void CleanUp(string path) public void Paginated0() => ProtoTestSingle("Paginated", ignoreCsProj: true); [Fact] - public void Lro0() => ProtoTestSingle("Lro", ignoreSnippets: true); + public void Lro0() => ProtoTestSingle("Lro", ignoreSnippets: true, serviceConfigPath: "ServiceConfig.yaml"); [Fact] public void ServerStreaming0() => ProtoTestSingle("ServerStreaming", ignoreCsProj: true, ignoreSnippets: true); diff --git a/Google.Api.Generator.Tests/ProtoTests/Lro/Lro.proto b/Google.Api.Generator.Tests/ProtoTests/Lro/Lro.proto index 0cc44ddf..6830bf47 100644 --- a/Google.Api.Generator.Tests/ProtoTests/Lro/Lro.proto +++ b/Google.Api.Generator.Tests/ProtoTests/Lro/Lro.proto @@ -27,6 +27,15 @@ service Lro { }; option (google.api.method_signature) = "name"; } + + // Test an LRO RPC with polling settings specified in the service config. + rpc ServiceConfigPollingMethod(Request) returns(google.longrunning.Operation) { + option (google.longrunning.operation_info) = { + response_type: "LroResponse" + metadata_type: "LroMetadata" + }; + option (google.api.method_signature) = "name"; + } } message Request { diff --git a/Google.Api.Generator.Tests/ProtoTests/Lro/LroFakes.cs b/Google.Api.Generator.Tests/ProtoTests/Lro/LroFakes.cs index d4d2a8db..59653c83 100644 --- a/Google.Api.Generator.Tests/ProtoTests/Lro/LroFakes.cs +++ b/Google.Api.Generator.Tests/ProtoTests/Lro/LroFakes.cs @@ -36,6 +36,8 @@ public LroClient(CallInvoker callInvoker) { } public virtual Operation SignatureMethod(Request request, CallOptions options) => throw new NotImplementedException(); public virtual AsyncUnaryCall ResourcedMethodAsync(ResourceRequest request, CallOptions options) => throw new NotImplementedException(); public virtual Operation ResourcedMethod(ResourceRequest request, CallOptions options) => throw new NotImplementedException(); + public virtual AsyncUnaryCall ServiceConfigPollingMethodAsync(Request request, CallOptions options) => throw new NotImplementedException(); + public virtual Operation ServiceConfigPollingMethod(Request request, CallOptions options) => throw new NotImplementedException(); } } diff --git a/Google.Api.Generator.Tests/ProtoTests/Lro/ServiceConfig.yaml b/Google.Api.Generator.Tests/ProtoTests/Lro/ServiceConfig.yaml new file mode 100644 index 00000000..7179bc5b --- /dev/null +++ b/Google.Api.Generator.Tests/ProtoTests/Lro/ServiceConfig.yaml @@ -0,0 +1,16 @@ +type: google.api.Service +config_version: 3 +name: test.googleapis.com +title: Client Libraries Showcase API + +apis: +- name: testing.lro.Lro + +publishing: + method_settings: + - selector: testing.lro.Lro.ServiceConfigPollingMethod + long_running: + initial_poll_delay: 60s + poll_delay_multiplier: 2 + max_poll_delay: 360s + total_poll_timeout: 54000s diff --git a/Google.Api.Generator.Tests/ProtoTests/Lro/Testing.Lro/LroClient.g.cs b/Google.Api.Generator.Tests/ProtoTests/Lro/Testing.Lro/LroClient.g.cs index 61296e47..c8035f2b 100644 --- a/Google.Api.Generator.Tests/ProtoTests/Lro/Testing.Lro/LroClient.g.cs +++ b/Google.Api.Generator.Tests/ProtoTests/Lro/Testing.Lro/LroClient.g.cs @@ -49,6 +49,8 @@ private LroSettings(LroSettings existing) : base(existing) SignatureMethodOperationsSettings = existing.SignatureMethodOperationsSettings.Clone(); ResourcedMethodSettings = existing.ResourcedMethodSettings; ResourcedMethodOperationsSettings = existing.ResourcedMethodOperationsSettings.Clone(); + ServiceConfigPollingMethodSettings = existing.ServiceConfigPollingMethodSettings; + ServiceConfigPollingMethodOperationsSettings = existing.ServiceConfigPollingMethodOperationsSettings.Clone(); OnCopy(existing); } @@ -114,6 +116,36 @@ private LroSettings(LroSettings existing) : base(existing) DefaultPollSettings = new gax::PollSettings(gax::Expiration.FromTimeout(sys::TimeSpan.FromHours(24)), sys::TimeSpan.FromSeconds(20), 1.5, sys::TimeSpan.FromSeconds(45)), }; + /// + /// for synchronous and asynchronous calls to + /// LroClient.ServiceConfigPollingMethod and LroClient.ServiceConfigPollingMethodAsync. + /// + /// + /// + /// This call will not be retried. + /// No timeout is applied. + /// + /// + public gaxgrpc::CallSettings ServiceConfigPollingMethodSettings { get; set; } = gaxgrpc::CallSettings.FromExpiration(gax::Expiration.None); + + /// + /// Long Running Operation settings for calls to LroClient.ServiceConfigPollingMethod and + /// LroClient.ServiceConfigPollingMethodAsync. + /// + /// + /// Uses default of: + /// + /// Initial delay: 60 seconds. + /// Delay multiplier: 2 + /// Maximum delay: 360 seconds. + /// Total timeout: 15 hours. + /// + /// + public lro::OperationsSettings ServiceConfigPollingMethodOperationsSettings { get; set; } = new lro::OperationsSettings + { + DefaultPollSettings = new gax::PollSettings(gax::Expiration.FromTimeout(sys::TimeSpan.FromHours(15)), sys::TimeSpan.FromSeconds(60), 2, sys::TimeSpan.FromSeconds(360)), + }; + /// Creates a deep clone of this object, with all the same property values. /// A deep clone of this object. public LroSettings Clone() => new LroSettings(this); @@ -440,6 +472,90 @@ internal static LroClient Create(grpccore::CallInvoker callInvoker, LroSettings /// A Task containing the RPC response. public virtual stt::Task> ResourcedMethodAsync(ResourceName name, st::CancellationToken cancellationToken) => ResourcedMethodAsync(name, gaxgrpc::CallSettings.FromCancellationToken(cancellationToken)); + + /// + /// Test an LRO RPC with polling settings specified in the service config. + /// + /// The request object containing all of the parameters for the API call. + /// If not null, applies overrides to this RPC call. + /// The RPC response. + public virtual lro::Operation ServiceConfigPollingMethod(Request request, gaxgrpc::CallSettings callSettings = null) => + throw new sys::NotImplementedException(); + + /// + /// Test an LRO RPC with polling settings specified in the service config. + /// + /// The request object containing all of the parameters for the API call. + /// If not null, applies overrides to this RPC call. + /// A Task containing the RPC response. + public virtual stt::Task> ServiceConfigPollingMethodAsync(Request request, gaxgrpc::CallSettings callSettings = null) => + throw new sys::NotImplementedException(); + + /// + /// Test an LRO RPC with polling settings specified in the service config. + /// + /// The request object containing all of the parameters for the API call. + /// A to use for this RPC. + /// A Task containing the RPC response. + public virtual stt::Task> ServiceConfigPollingMethodAsync(Request request, st::CancellationToken cancellationToken) => + ServiceConfigPollingMethodAsync(request, gaxgrpc::CallSettings.FromCancellationToken(cancellationToken)); + + /// The long-running operations client for ServiceConfigPollingMethod. + public virtual lro::OperationsClient ServiceConfigPollingMethodOperationsClient => throw new sys::NotImplementedException(); + + /// + /// Poll an operation once, using an operationName from a previous invocation of + /// ServiceConfigPollingMethod. + /// + /// + /// The name of a previously invoked operation. Must not be null or empty. + /// + /// If not null, applies overrides to this RPC call. + /// The result of polling the operation. + public virtual lro::Operation PollOnceServiceConfigPollingMethod(string operationName, gaxgrpc::CallSettings callSettings = null) => + lro::Operation.PollOnceFromName(gax::GaxPreconditions.CheckNotNullOrEmpty(operationName, nameof(operationName)), ServiceConfigPollingMethodOperationsClient, callSettings); + + /// + /// Asynchronously poll an operation once, using an operationName from a previous invocation of + /// ServiceConfigPollingMethod. + /// + /// + /// The name of a previously invoked operation. Must not be null or empty. + /// + /// If not null, applies overrides to this RPC call. + /// A task representing the result of polling the operation. + public virtual stt::Task> PollOnceServiceConfigPollingMethodAsync(string operationName, gaxgrpc::CallSettings callSettings = null) => + lro::Operation.PollOnceFromNameAsync(gax::GaxPreconditions.CheckNotNullOrEmpty(operationName, nameof(operationName)), ServiceConfigPollingMethodOperationsClient, callSettings); + + /// + /// Test an LRO RPC with polling settings specified in the service config. + /// + /// + /// + /// If not null, applies overrides to this RPC call. + /// The RPC response. + public virtual lro::Operation ServiceConfigPollingMethod(string name, gaxgrpc::CallSettings callSettings = null) => + ServiceConfigPollingMethod(new Request { Name = name ?? "", }, callSettings); + + /// + /// Test an LRO RPC with polling settings specified in the service config. + /// + /// + /// + /// If not null, applies overrides to this RPC call. + /// A Task containing the RPC response. + public virtual stt::Task> ServiceConfigPollingMethodAsync(string name, gaxgrpc::CallSettings callSettings = null) => + ServiceConfigPollingMethodAsync(new Request { Name = name ?? "", }, callSettings); + + /// + /// Test an LRO RPC with polling settings specified in the service config. + /// + /// + /// + /// A to use for this RPC. + /// A Task containing the RPC response. + public virtual stt::Task> ServiceConfigPollingMethodAsync(string name, st::CancellationToken cancellationToken) => + ServiceConfigPollingMethodAsync(name, gaxgrpc::CallSettings.FromCancellationToken(cancellationToken)); } /// Lro client wrapper implementation, for convenient use. @@ -452,6 +568,8 @@ public sealed partial class LroClientImpl : LroClient private readonly gaxgrpc::ApiCall _callResourcedMethod; + private readonly gaxgrpc::ApiCall _callServiceConfigPollingMethod; + /// /// Constructs a client wrapper for the Lro service, with the specified gRPC client and settings. /// @@ -469,12 +587,16 @@ public LroClientImpl(Lro.LroClient grpcClient, LroSettings settings, mel::ILogge }); SignatureMethodOperationsClient = new lro::OperationsClientImpl(grpcClient.CreateOperationsClient(), effectiveSettings.SignatureMethodOperationsSettings, logger); ResourcedMethodOperationsClient = new lro::OperationsClientImpl(grpcClient.CreateOperationsClient(), effectiveSettings.ResourcedMethodOperationsSettings, logger); + ServiceConfigPollingMethodOperationsClient = new lro::OperationsClientImpl(grpcClient.CreateOperationsClient(), effectiveSettings.ServiceConfigPollingMethodOperationsSettings, logger); _callSignatureMethod = clientHelper.BuildApiCall("SignatureMethod", grpcClient.SignatureMethodAsync, grpcClient.SignatureMethod, effectiveSettings.SignatureMethodSettings); Modify_ApiCall(ref _callSignatureMethod); Modify_SignatureMethodApiCall(ref _callSignatureMethod); _callResourcedMethod = clientHelper.BuildApiCall("ResourcedMethod", grpcClient.ResourcedMethodAsync, grpcClient.ResourcedMethod, effectiveSettings.ResourcedMethodSettings); Modify_ApiCall(ref _callResourcedMethod); Modify_ResourcedMethodApiCall(ref _callResourcedMethod); + _callServiceConfigPollingMethod = clientHelper.BuildApiCall("ServiceConfigPollingMethod", grpcClient.ServiceConfigPollingMethodAsync, grpcClient.ServiceConfigPollingMethod, effectiveSettings.ServiceConfigPollingMethodSettings); + Modify_ApiCall(ref _callServiceConfigPollingMethod); + Modify_ServiceConfigPollingMethodApiCall(ref _callServiceConfigPollingMethod); OnConstruction(grpcClient, effectiveSettings, clientHelper); } @@ -484,6 +606,8 @@ public LroClientImpl(Lro.LroClient grpcClient, LroSettings settings, mel::ILogge partial void Modify_ResourcedMethodApiCall(ref gaxgrpc::ApiCall call); + partial void Modify_ServiceConfigPollingMethodApiCall(ref gaxgrpc::ApiCall call); + partial void OnConstruction(Lro.LroClient grpcClient, LroSettings effectiveSettings, gaxgrpc::ClientHelper clientHelper); /// The underlying gRPC Lro client @@ -546,6 +670,33 @@ public LroClientImpl(Lro.LroClient grpcClient, LroSettings settings, mel::ILogge Modify_ResourceRequest(ref request, ref callSettings); return new lro::Operation(await _callResourcedMethod.Async(request, callSettings).ConfigureAwait(false), ResourcedMethodOperationsClient); } + + /// The long-running operations client for ServiceConfigPollingMethod. + public override lro::OperationsClient ServiceConfigPollingMethodOperationsClient { get; } + + /// + /// Test an LRO RPC with polling settings specified in the service config. + /// + /// The request object containing all of the parameters for the API call. + /// If not null, applies overrides to this RPC call. + /// The RPC response. + public override lro::Operation ServiceConfigPollingMethod(Request request, gaxgrpc::CallSettings callSettings = null) + { + Modify_Request(ref request, ref callSettings); + return new lro::Operation(_callServiceConfigPollingMethod.Sync(request, callSettings), ServiceConfigPollingMethodOperationsClient); + } + + /// + /// Test an LRO RPC with polling settings specified in the service config. + /// + /// The request object containing all of the parameters for the API call. + /// If not null, applies overrides to this RPC call. + /// A Task containing the RPC response. + public override async stt::Task> ServiceConfigPollingMethodAsync(Request request, gaxgrpc::CallSettings callSettings = null) + { + Modify_Request(ref request, ref callSettings); + return new lro::Operation(await _callServiceConfigPollingMethod.Async(request, callSettings).ConfigureAwait(false), ServiceConfigPollingMethodOperationsClient); + } } public static partial class Lro diff --git a/Google.Api.Generator.Tests/ProtoTests/Lro/Testing.Lro/Testing.Lro.csproj b/Google.Api.Generator.Tests/ProtoTests/Lro/Testing.Lro/Testing.Lro.csproj index 1311a146..f241d3f3 100644 --- a/Google.Api.Generator.Tests/ProtoTests/Lro/Testing.Lro/Testing.Lro.csproj +++ b/Google.Api.Generator.Tests/ProtoTests/Lro/Testing.Lro/Testing.Lro.csproj @@ -1,4 +1,4 @@ - + @@ -45,4 +45,4 @@ - + \ No newline at end of file diff --git a/Google.Api.Generator/Generation/MethodDetails.cs b/Google.Api.Generator/Generation/MethodDetails.cs index e6a9fa56..dba53c9e 100644 --- a/Google.Api.Generator/Generation/MethodDetails.cs +++ b/Google.Api.Generator/Generation/MethodDetails.cs @@ -107,6 +107,12 @@ public Paginated(ServiceDetails svc, MethodDescriptor desc, /// public abstract class Lro : MethodDetails { + private static readonly PollSettings s_lroDefaultPollSettings = new PollSettings( + expiration: Expiration.FromTimeout(TimeSpan.FromHours(24)), + delay: TimeSpan.FromSeconds(20), + delayMultiplier: 1.5, + maxDelay: TimeSpan.FromSeconds(45)); + protected Lro(ServiceDetails svc, MethodDescriptor desc) : base(svc, desc) { @@ -114,8 +120,16 @@ protected Lro(ServiceDetails svc, MethodDescriptor desc) LroClientName = $"{desc.Name}OperationsClient"; SyncPollMethodName = $"PollOnce{SyncMethodName}"; AsyncPollMethodName = $"PollOnce{AsyncMethodName}"; + PollSettings = ServiceConfigMethodSettings?.LongRunning is MethodSettings.Types.LongRunning lro + ? new PollSettings( + expiration: Expiration.FromTimeout(lro.TotalPollTimeout.ToTimeSpan()), + delay: lro.InitialPollDelay.ToTimeSpan(), + delayMultiplier: lro.PollDelayMultiplier, + maxDelay: lro.MaxPollDelay.ToTimeSpan()) + : s_lroDefaultPollSettings; } + public PollSettings PollSettings { get; } public abstract override Typ ApiCallTyp { get; } public abstract override Typ SyncReturnTyp { get; } public string LroSettingsName { get; } diff --git a/Google.Api.Generator/Generation/ServiceSettingsCodeGenerator.cs b/Google.Api.Generator/Generation/ServiceSettingsCodeGenerator.cs index a5388016..bd9e5af9 100644 --- a/Google.Api.Generator/Generation/ServiceSettingsCodeGenerator.cs +++ b/Google.Api.Generator/Generation/ServiceSettingsCodeGenerator.cs @@ -34,12 +34,6 @@ namespace Google.Api.Generator.Generation /// internal class ServiceSettingsCodeGenerator { - private static readonly PollSettings s_lroDefaultPollSettings = new PollSettings( - expiration: Expiration.FromTimeout(TimeSpan.FromHours(24)), - delay: TimeSpan.FromSeconds(20), - delayMultiplier: 1.5, - maxDelay: TimeSpan.FromSeconds(45)); - private static readonly SyntaxAnnotation s_cloneSetting = new SyntaxAnnotation("cloneSetting"); public static ClassDeclarationSyntax Generate(SourceFileContext ctx, ServiceDetails svc) => @@ -216,24 +210,27 @@ PropertyDeclarationSyntax PerMixin(ServiceDetails.MixinDetails mixin) } } - private PropertyDeclarationSyntax LroSettingsProperty(MethodDetails.Lro method) => - AutoProperty(Public, _ctx.Type(), method.LroSettingsName, hasSetter: true) + private PropertyDeclarationSyntax LroSettingsProperty(MethodDetails.Lro method) + { + var pollSettings = method.PollSettings; + return AutoProperty(Public, _ctx.Type(), method.LroSettingsName, hasSetter: true) .WithInitializer(New(_ctx.Type())().WithInitializer( new ObjectInitExpr(nameof(OperationsSettings.DefaultPollSettings), New(_ctx.Type())( - _ctx.Type().Call(nameof(Expiration.FromTimeout))(_ctx.Type().Call(nameof(TimeSpan.FromHours))((int) s_lroDefaultPollSettings.Expiration.Timeout.Value.TotalHours)), - _ctx.Type().Call(nameof(TimeSpan.FromSeconds))((int) s_lroDefaultPollSettings.Delay.TotalSeconds), - s_lroDefaultPollSettings.DelayMultiplier, - _ctx.Type().Call(nameof(TimeSpan.FromSeconds))((int) s_lroDefaultPollSettings.MaxDelay.TotalSeconds))))) + _ctx.Type().Call(nameof(Expiration.FromTimeout))(_ctx.Type().Call(nameof(TimeSpan.FromHours))((int) pollSettings.Expiration.Timeout.Value.TotalHours)), + _ctx.Type().Call(nameof(TimeSpan.FromSeconds))((int) pollSettings.Delay.TotalSeconds), + pollSettings.DelayMultiplier, + _ctx.Type().Call(nameof(TimeSpan.FromSeconds))((int) pollSettings.MaxDelay.TotalSeconds))))) .WithXmlDoc( XmlDoc.Summary("Long Running Operation settings for calls to ", XmlDoc.C($"{_svc.ClientAbstractTyp.Name}.{method.SyncMethodName}"), " and ", XmlDoc.C($"{_svc.ClientAbstractTyp.Name}.{method.AsyncMethodName}"), "."), XmlDoc.Remarks("Uses default ", _ctx.Type(), " of:", XmlDoc.UL( - Invariant($"Initial delay: {(int) s_lroDefaultPollSettings.Delay.TotalSeconds} seconds."), - Invariant($"Delay multiplier: {s_lroDefaultPollSettings.DelayMultiplier}"), - Invariant($"Maximum delay: {(int) s_lroDefaultPollSettings.MaxDelay.TotalSeconds} seconds."), - Invariant($"Total timeout: {(int) s_lroDefaultPollSettings.Expiration.Timeout.Value.TotalHours} hours.")))) + Invariant($"Initial delay: {(int) pollSettings.Delay.TotalSeconds} seconds."), + Invariant($"Delay multiplier: {pollSettings.DelayMultiplier}"), + Invariant($"Maximum delay: {(int) pollSettings.MaxDelay.TotalSeconds} seconds."), + Invariant($"Total timeout: {(int) pollSettings.Expiration.Timeout.Value.TotalHours} hours.")))) .WithAdditionalAnnotations(s_cloneSetting); + } private PropertyDeclarationSyntax BidiSettingsProperty(MethodDetails.BidiStreaming method) => AutoProperty(Public, _ctx.Type(), method.StreamingSettingsName, hasSetter: true)