private static MethodDeclarationSyntax GenerateMethod(SourceFileContext ctx, ServiceDetails service)
        {
            var name = $"Add{service.ClientAbstractTyp.Name}";
            var serviceCollection = ctx.Type <IServiceCollection>();
            var services          = Parameter(serviceCollection, "services")
                                    .WithModifiers(SyntaxTokenList.Create(Token(SyntaxKind.ThisKeyword).WithTrailingSpace()));
            var actionType = ctx.Type(Typ.Generic(typeof(Action <>), service.BuilderTyp));
            var action     = Parameter(actionType, "action", @default: Null);

            var builderType = ctx.Type(service.BuilderTyp);
            var builder     = Local(builderType, "builder");

            var provider = Parameter(ctx.Type <IServiceProvider>(), "provider");
            var lambda   = Lambda(provider)(
                builder.WithInitializer(New(builderType)()),
                action.Call("Invoke", true)(builder),
                Return(builder.Call("Build")(provider))
                );

            return(Method(Public | Static, serviceCollection, name)(services, action)
                   .WithBody(services.Call("AddSingleton")(lambda))
                   .WithXmlDoc(
                       XmlDoc.Summary("Adds a singleton ", ctx.Type(service.ClientAbstractTyp), " to ", services, "."),
                       XmlDoc.Param(services, "The service collection to add the client to. The services are used to configure the client when requested."),
                       XmlDoc.Param(action, "An optional action to invoke on the client builder. This is invoked before services from ", services, " are used.")
                       ));
        }
 public ServerStreaming(ServiceDetails svc, MethodDescriptor desc) : base(svc, desc)
 {
     ApiCallTyp         = Typ.Generic(typeof(ApiServerStreamingCall <,>), RequestTyp, ResponseTyp);
     AbstractStreamTyp  = Typ.Nested(svc.ClientAbstractTyp, $"{SyncMethodName}Stream");
     ImplStreamTyp      = Typ.Nested(svc.ClientImplTyp, $"{SyncMethodName}StreamImpl");
     AsyncEnumeratorTyp = Typ.Generic(typeof(AsyncResponseStream <>), ResponseTyp);
 }
            public StandardLro(ServiceDetails svc, MethodDescriptor desc) : base(svc, desc)
            {
                OperationInfo lroData = desc.GetExtension(OperationsExtensions.OperationInfo);

                if (lroData is null)
                {
                    throw new InvalidOperationException("LRO method must contain a `google.api.operation` option.");
                }
                ApiCallTyp = Typ.Generic(typeof(ApiCall <,>), RequestTyp, Typ.Of <Operation>());
                if (string.IsNullOrEmpty(lroData.ResponseType) || string.IsNullOrEmpty(lroData.MetadataType))
                {
                    throw new InvalidOperationException($"Both response-type and metadata-type must be present for method: '{desc.FullName}'.");
                }
                var responseTypeMsg = svc.Catalog.GetMessageByName(lroData.ResponseType);
                var metadataTypeMsg = svc.Catalog.GetMessageByName(lroData.MetadataType);

                if (responseTypeMsg is null || metadataTypeMsg is null)
                {
                    throw new InvalidOperationException(
                              $"Response-type and Metadata-type must both exist in method '{desc.FullName}': '{lroData.ResponseType}', '{lroData.MetadataType}'.");
                }
                OperationResponseTyp = ProtoTyp.Of(responseTypeMsg);
                OperationMetadataTyp = ProtoTyp.Of(metadataTypeMsg);
                SyncReturnTyp        = Typ.Generic(typeof(Operation <,>), OperationResponseTyp, OperationMetadataTyp);
            }
        private MethodDetails(ServiceDetails svc, MethodDescriptor desc)
        {
            Svc                     = svc;
            ProtoRpcName            = desc.Name;
            SyncMethodName          = desc.Name;
            SyncSnippetMethodName   = $"{desc.Name}RequestObject";
            SyncTestMethodName      = $"{desc.Name}RequestObject";
            AsyncMethodName         = $"{desc.Name}Async";
            AsyncSnippetMethodName  = $"{desc.Name}RequestObjectAsync";
            AsyncTestMethodName     = $"{desc.Name}RequestObjectAsync";
            SettingsName            = $"{desc.Name}Settings";
            RequestTyp              = ProtoTyp.Of(desc.InputType);
            ResponseTyp             = ProtoTyp.Of(desc.OutputType);
            ApiCallFieldName        = $"_call{desc.Name}";
            ModifyApiCallMethodName = $"Modify_{desc.Name}ApiCall";
            ModifyRequestMethodName = $"Modify_{RequestTyp.Name}";
            DocLines                = desc.Declaration.DocLines().ToList();
            Signatures              = desc.GetExtension(ClientExtensions.MethodSignature).Select(sig => new Signature(svc, desc.InputType, sig)).ToList();
            RequestMessageDesc      = desc.InputType;
            ResponseMessageDesc     = desc.OutputType;
            var http    = desc.GetExtension(AnnotationsExtensions.Http);
            var routing = desc.GetExtension(RoutingExtensions.Routing);

            RoutingHeaders = ReadRoutingHeaders(routing, http, desc.InputType).ToList();
            (MethodRetry, MethodRetryStatusCodes, Expiration) = LoadTiming(svc, desc);
            // The method is considered deprecated if the RPC, request or response messages are deprecated.
            // In reality, it would be very odd to deprecate the messages without deprecating the RPC, but this makes it consistent.
            IsDeprecated = desc.IsDeprecated() || RequestMessageDesc.IsDeprecated() || ResponseMessageDesc.IsDeprecated();
        }
                public Field(ServiceDetails svc, MessageDescriptor msg, string fieldName)
                {
                    Descs = fieldName.Split('.').Aggregate((msg, result: ImmutableList <FieldDescriptor> .Empty), (acc, part) =>
                    {
                        // TODO: Check nested fields aren't repeated.
                        var fieldDesc = acc.msg.FindFieldByName(part);
                        if (fieldDesc == null)
                        {
                            throw new InvalidOperationException($"Field '{part}' does not exist in message: {acc.msg.FullName}");
                        }
                        return(fieldDesc.FieldType == FieldType.Message ? fieldDesc.MessageType : null, acc.result.Add(fieldDesc));
                    }, acc => acc.result);
                    var lastDesc = Descs.Last();

                    Typ            = ProtoTyp.Of(lastDesc);
                    IsMap          = lastDesc.IsMap;
                    IsRepeated     = lastDesc.IsRepeated;
                    IsWrapperType  = ProtoTyp.IsWrapperType(lastDesc);
                    IsRequired     = lastDesc.GetExtension(FieldBehaviorExtensions.FieldBehavior).Any(x => x == FieldBehavior.Required);
                    ParameterName  = lastDesc.CSharpFieldName();
                    PropertyName   = lastDesc.CSharpPropertyName();
                    IsDeprecated   = Descs.Any(x => x.IsDeprecated());
                    DocLines       = lastDesc.Declaration.DocLines();
                    FieldResources = svc.Catalog.GetResourceDetailsByField(lastDesc);
                }
 protected Lro(ServiceDetails svc, MethodDescriptor desc)
     : base(svc, desc)
 {
     LroSettingsName     = $"{desc.Name}OperationsSettings";
     LroClientName       = $"{desc.Name}OperationsClient";
     SyncPollMethodName  = $"PollOnce{SyncMethodName}";
     AsyncPollMethodName = $"PollOnce{AsyncMethodName}";
 }
        private (RetrySettings, IEnumerable <StatusCode>, Expiration) LoadTiming(ServiceDetails svc, MethodDescriptor desc)
        {
            var config = svc.MethodGrpcConfigsByName.GetValueOrDefault($"{svc.ServiceFullName}/{desc.Name}") ??
                         svc.MethodGrpcConfigsByName.GetValueOrDefault($"{svc.ServiceFullName}/");

            if (config == null)
            {
                return(default);
 public static MethodDetails Create(ServiceDetails svc, MethodDescriptor desc) =>
 DetectPagination(svc, desc) ?? (
     desc.IsClientStreaming && desc.IsServerStreaming ? new BidiStreaming(svc, desc) :
     desc.IsServerStreaming ? new ServerStreaming(svc, desc) :
     desc.IsClientStreaming ? new ClientStreaming(svc, desc) :
     // Any LRO-returning methods within the LRO package itself should be treated normally. Anywhere else, they get special treatment.
     desc.OutputType.FullName == "google.longrunning.Operation" && desc.File.Package != "google.longrunning" ? new StandardLro(svc, desc) :
     !string.IsNullOrEmpty(desc.GetExtension(ExtendedOperationsExtensions.OperationService)) ? new NonStandardLro(svc, desc) :
     (MethodDetails) new Normal(svc, desc));
 public ClientStreaming(ServiceDetails svc, MethodDescriptor desc) : base(svc, desc)
 {
     ApiCallTyp            = Typ.Generic(typeof(ApiClientStreamingCall <,>), RequestTyp, ResponseTyp);
     AbstractStreamTyp     = Typ.Nested(svc.ClientAbstractTyp, $"{SyncMethodName}Stream");
     ImplStreamTyp         = Typ.Nested(svc.ClientImplTyp, $"{SyncMethodName}StreamImpl");
     StreamingSettingsName = $"{desc.Name}StreamingSettings";
     ModifyStreamingCallSettingsMethodName = $"Modify_{RequestTyp.Name}CallSettings";
     ModifyStreamingRequestMethodName      = $"Modify_{RequestTyp.Name}Request";
 }
Example #10
0
 public Paginated(ServiceDetails svc, MethodDescriptor desc,
                  FieldDescriptor responseResourceField, int pageSizeFieldNumber, int pageTokenFieldNumber) : base(svc, desc)
 {
     ResourceTyp          = ProtoTyp.Of(responseResourceField, forceRepeated: false);
     ApiCallTyp           = Typ.Generic(typeof(ApiCall <,>), RequestTyp, ResponseTyp);
     SyncReturnTyp        = Typ.Generic(typeof(PagedEnumerable <,>), ResponseTyp, ResourceTyp);
     AsyncReturnTyp       = Typ.Generic(typeof(PagedAsyncEnumerable <,>), ResponseTyp, ResourceTyp);
     SyncGrpcType         = Typ.Generic(typeof(GrpcPagedEnumerable <, ,>), RequestTyp, ResponseTyp, ResourceTyp);
     AsyncGrpcType        = Typ.Generic(typeof(GrpcPagedAsyncEnumerable <, ,>), RequestTyp, ResponseTyp, ResourceTyp);
     ResourcesFieldName   = responseResourceField.CSharpPropertyName();
     PageSizeFieldNumber  = pageSizeFieldNumber;
     PageTokenFieldNumber = pageTokenFieldNumber;
 }
            public NonStandardLro(ServiceDetails svc, MethodDescriptor desc) : base(svc, desc)
            {
                OperationService = desc.GetExtension(ExtendedOperationsExtensions.OperationService);
                if (string.IsNullOrEmpty(OperationService))
                {
                    throw new InvalidOperationException($"{desc.FullName} is not a non-standard LRO; it does not have an operation_service option.");
                }
                var service = svc.Catalog.GetServiceByName(OperationService);

                OperationServiceDetails = ServiceDetails.NonStandardLroDetails.ForService(service);
                SyncReturnTyp           = Typ.Generic(typeof(Operation <,>), OperationResponseTyp, OperationMetadataTyp);
                ApiCallTyp = Typ.Generic(typeof(ApiCall <,>), RequestTyp, ResponseTyp);
            }
 public Paginated(ServiceDetails svc, MethodDescriptor desc,
                  FieldDescriptor responseResourceField, int pageSizeFieldNumber, int pageTokenFieldNumber) : base(svc, desc)
 {
     ResourceTyp = ProtoTyp.Of(responseResourceField, forceRepeated: false);
     // For map fields, ResourceTyp is a constructed KeyValuePair<,> type: we need the open type in a cref.
     ResourceTypForCref = responseResourceField.IsMap
         ? Typ.Generic(typeof(KeyValuePair <,>), Typ.GenericParam("TKey"), Typ.GenericParam("TValue"))
         : ResourceTyp;
     ApiCallTyp           = Typ.Generic(typeof(ApiCall <,>), RequestTyp, ResponseTyp);
     SyncReturnTyp        = Typ.Generic(typeof(PagedEnumerable <,>), ResponseTyp, ResourceTyp);
     AsyncReturnTyp       = Typ.Generic(typeof(PagedAsyncEnumerable <,>), ResponseTyp, ResourceTyp);
     SyncGrpcType         = Typ.Generic(typeof(GrpcPagedEnumerable <, ,>), RequestTyp, ResponseTyp, ResourceTyp);
     AsyncGrpcType        = Typ.Generic(typeof(GrpcPagedAsyncEnumerable <, ,>), RequestTyp, ResponseTyp, ResourceTyp);
     ResourcesFieldName   = responseResourceField.CSharpPropertyName();
     PageSizeFieldNumber  = pageSizeFieldNumber;
     PageTokenFieldNumber = pageTokenFieldNumber;
 }
Example #13
0
        public static CompilationUnitSyntax Generate(SourceFileContext ctx, ServiceDetails svc, HashSet <Typ> seenPaginatedResponseTyps)
        {
            var ns = Namespace(svc.Namespace);

            using (ctx.InNamespace(ns))
            {
                var settingsClass       = ServiceSettingsCodeGenerator.Generate(ctx, svc);
                var builderClass        = ServiceBuilderCodeGenerator.Generate(ctx, svc);
                var abstractClientClass = ServiceAbstractClientClassCodeGenerator.Generate(ctx, svc);
                var implClientClass     = ServiceImplClientClassGenerator.Generate(ctx, svc);
                ns = ns.AddMembers(settingsClass, builderClass, abstractClientClass, implClientClass);

                ns = ns.AddMembers(PaginatedPartialClasses(ctx, svc, seenPaginatedResponseTyps).ToArray());
                ns = ns.AddMembers(LroPartialClasses(ctx, svc).ToArray());
            }
            return(ctx.CreateCompilationUnit(ns));
        }
 private static ServiceForTransport ServiceForTransportMetadata(ServiceDetails serviceDetails) =>
 new ServiceForTransport
 {
     Clients =
     {
         [TransportKeyGrpc] = new ServiceAsClient
                 {
                 LibraryClient = serviceDetails.ClientAbstractTyp.Name,
                 Rpcs          =
                 {
                 new SortedDictionary <string,                        MethodList>(
                     serviceDetails.Methods.ToDictionary(
                         methodDetails => methodDetails.ProtoRpcName,
                         methodDetails => new MethodList
                 {
                 Methods = { methodDetails.SyncMethodName,        methodDetails.AsyncMethodName}
                 }),                                                  StringComparer.Ordinal)
                 }
                 }
     }
 };
 public Normal(ServiceDetails svc, MethodDescriptor desc) : base(svc, desc)
 {
     ApiCallTyp    = Typ.Generic(typeof(ApiCall <,>), RequestTyp, ResponseTyp);
     SyncReturnTyp = ResponseTyp.FullName == typeof(Empty).FullName ? Typ.Void : ResponseTyp;
 }
 public Signature(ServiceDetails svc, MessageDescriptor msg, string sig)
 {
     Fields = sig.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(fieldName => new Field(svc, msg, fieldName.Trim())).ToList();
 }
        private static MethodDetails DetectPagination(ServiceDetails svc, MethodDescriptor desc)
        {
            if (desc.File.Package == "google.cloud.talent.v4beta1" && (desc.Name == "SearchProfiles" || desc.Name == "SearchJobs"))
            {
                // TODO: remove this once the next version of the Talent API is published.
                // This is a workaround to disable auto-pagination for specifc RPCs in
                // Talent v4beta1. The API team will make their API non-conforming in the
                // next version.
                // This should not be done for any other API.
                return(null);
            }

            var input  = desc.InputType;
            var output = desc.OutputType;

            var pageSizeCandidate      = FindPageSizeCandidate(input);
            var pageTokenCandidate     = input.FindFieldByName("page_token");
            var nextPageTokenCandidate = output.FindFieldByName("next_page_token");

            var mapCandidates = output.Fields.InDeclarationOrder().Where(f => f.IsMap).ToList();
            var repeatedCandidatesByDeclOrder = output.Fields.InDeclarationOrder().Where(IsRepeatedCandidate).ToList();
            var repeatedCandidatesByNumOrder  = output.Fields.InFieldNumberOrder().Where(IsRepeatedCandidate).ToList();

            var hasMapOrRepeatedCandidates = mapCandidates.Count == 1 || repeatedCandidatesByNumOrder.Count >= 1;

            if (pageSizeCandidate is null || pageTokenCandidate is null || nextPageTokenCandidate is null || !hasMapOrRepeatedCandidates)
            {
                return(null);
            }

            if (pageSizeCandidate.Name == "max_results" &&
                pageSizeCandidate.FieldType == FieldType.Message &&
                pageSizeCandidate.MessageType?.FullName == "google.protobuf.UInt32Value")
            {
                // This happens in BigQuery APIs that should still be generated, without pagination
                return(null);
            }

            // At this point the method is a candidate for pagination since its input has page_size/max_results + page_token
            // and its output has next_page_token and at least one map or repeated field

            if (!IsValidPageSizeCandidate(pageSizeCandidate))
            {
                throw new InvalidOperationException($"{pageSizeCandidate.Name} must be of type int32/uint32");
            }

            if (pageTokenCandidate.FieldType != FieldType.String || pageTokenCandidate.IsRepeated)
            {
                throw new InvalidOperationException("page_token must be of type string");
            }

            if (nextPageTokenCandidate.FieldType != FieldType.String || nextPageTokenCandidate.IsRepeated)
            {
                throw new InvalidOperationException("next_page_token must be of type string");
            }

            // GRPC case first.
            // - PageSizeCandidate should be named page_size
            // - there should be non-map candidates
            // - The repeated candidate should be the first in both orders
            // - There should be 0 or more than 1 map candidates, to disambiguate with DiREGapic single-map case
            //   OR The repeated candidate should have a field number of 1
            if (pageSizeCandidate.Name == "page_size" && repeatedCandidatesByNumOrder.Any() && repeatedCandidatesByDeclOrder[0] == repeatedCandidatesByNumOrder[0] &&
                (repeatedCandidatesByNumOrder[0].FieldNumber == 1 || mapCandidates.Count != 1))
            {
                return(new Paginated(svc, desc, repeatedCandidatesByDeclOrder[0], pageSizeCandidate.FieldNumber, pageTokenCandidate.FieldNumber));
            }

            // DiREGapic case where a return message has exactly one map
            if (mapCandidates.Count == 1)
            {
                return(new Paginated(svc, desc, mapCandidates.Single(), pageSizeCandidate.FieldNumber,
                                     pageTokenCandidate.FieldNumber));
            }

            // DiREGapic case where a return message has exactly one repeated field
            // (pageSizeCandidate's name can be either "page_size" or "max_results")
            if (repeatedCandidatesByDeclOrder.Count == 1 && !mapCandidates.Any())
            {
                return(new Paginated(svc, desc, repeatedCandidatesByDeclOrder.Single(), pageSizeCandidate.FieldNumber, pageTokenCandidate.FieldNumber));
            }

            var errMsg = $"The method {desc.FullName} is selected as a pagination candidate " +
                         $"but the configuration of the item response field candidates " +
                         $"does not match any of the configurations we can generate.";

            throw new InvalidOperationException(errMsg);

            bool IsRepeatedCandidate(FieldDescriptor field) =>
            field.IsRepeated && !field.IsMap && (field.FieldType == FieldType.Message || field.FieldType == FieldType.String);
        }
Example #18
0
 private ServiceAbstractClientClassCodeGenerator(SourceFileContext ctx, ServiceDetails svc) =>
 (_ctx, _svc) = (ctx, svc);
Example #19
0
 public static ClassDeclarationSyntax Generate(SourceFileContext ctx, ServiceDetails svc) =>
 new ServiceImplClientClassGenerator(ctx, svc).Generate();
Example #20
0
        private static IEnumerable <MemberDeclarationSyntax> PaginatedPartialClasses(SourceFileContext ctx, ServiceDetails svc, HashSet <Typ> seenPaginatedResponseTyps)
        {
            var paginatedMethods = svc.Methods.OfType <MethodDetails.Paginated>();

            foreach (var representingMethod in paginatedMethods
                     .GroupBy(m => m.RequestTyp)
                     .Select(typMethodGrp => typMethodGrp.First()))
            {
                yield return(PaginatedPartialInterfaceClass(ctx, representingMethod.RequestTyp, representingMethod.RequestMessageDesc));
            }

            foreach (var method in paginatedMethods)
            {
                if (seenPaginatedResponseTyps.Add(method.ResponseTyp))
                {
                    var cls = Class(Public | Partial, method.ResponseTyp, baseTypes: ctx.Type(Typ.Generic(typeof(IPageResponse <>), method.ResourceTyp)));
                    using (ctx.InClass(cls))
                    {
                        var propertyName         = method.ResourcesFieldName;
                        var genericGetEnumerator = Method(Public, ctx.Type(Typ.Generic(typeof(IEnumerator <>), method.ResourceTyp)), "GetEnumerator")()
                                                   .WithBody(Property(Public, ctx.TypeDontCare, propertyName).Call(nameof(IEnumerable <int> .GetEnumerator))())
                                                   .WithXmlDoc(XmlDoc.Summary("Returns an enumerator that iterates through the resources in this response."));
                        var getEnumerator = Method(None, ctx.Type <IEnumerator>(), "GetEnumerator")()
                                            .WithExplicitInterfaceSpecifier(ctx.Type <IEnumerable>())
                                            .WithBody(This.Call(genericGetEnumerator)());
                        cls = cls.AddMembers(genericGetEnumerator, getEnumerator);
                    }
                    yield return(cls);
                }
            }
        }
Example #21
0
 private ServiceImplClientClassGenerator(SourceFileContext ctx, ServiceDetails svc) =>
 (_ctx, _svc) = (ctx, svc);
Example #22
0
 public static IEnumerable <MemberDeclarationSyntax> Generate(SourceFileContext ctx, ServiceDetails svc, bool inAbstract) =>
 new ServiceMethodGenerator(ctx, svc, inAbstract).Generate();
Example #23
0
 public static ClassDeclarationSyntax Generate(SourceFileContext ctx, ServiceDetails svc) =>
 new ServiceSettingsCodeGenerator(ctx, svc).Generate();
Example #24
0
 private static IEnumerable <MemberDeclarationSyntax> LroPartialClasses(SourceFileContext ctx, ServiceDetails svc)
 {
     if (svc.Methods.Any(m => m is MethodDetails.Lro))
     {
         // Emit partial class to give access to an LRO operations client.
         var grpcOuterCls = Class(Public | Static | Partial, svc.GrpcClientTyp.DeclaringTyp);
         using (ctx.InClass(grpcOuterCls))
         {
             var grpcInnerClass = Class(Public | Partial, svc.GrpcClientTyp);
             using (ctx.InClass(grpcInnerClass))
             {
                 var callInvoker = Property(Private, ctx.TypeDontCare, "CallInvoker");
                 var opTyp       = ctx.Type <Operations.OperationsClient>();
                 var createOperationsClientMethod = Method(Public | Virtual, opTyp, "CreateOperationsClient")()
                                                    .WithBody(New(opTyp)(callInvoker))
                                                    .WithXmlDoc(
                     XmlDoc.Summary("Creates a new instance of ", opTyp, " using the same call invoker as this client."),
                     XmlDoc.Returns("A new Operations client for the same target as this client.")
                     );
                 grpcInnerClass = grpcInnerClass.AddMembers(createOperationsClientMethod);
             }
             grpcOuterCls = grpcOuterCls.AddMembers(grpcInnerClass);
         }
         yield return(grpcOuterCls);
     }
 }
Example #25
0
 private UnitTestCodeGeneration(SourceFileContext ctx, ServiceDetails svc) => (_ctx, _svc) = (ctx, svc);
Example #26
0
 public static CompilationUnitSyntax Generate(SourceFileContext ctx, ServiceDetails svc) =>
 new UnitTestCodeGeneration(ctx, svc).Generate();
Example #27
0
 private ServiceSettingsCodeGenerator(SourceFileContext ctx, ServiceDetails svc) =>
 (_ctx, _svc) = (ctx, svc);
Example #28
0
        private static IEnumerable <MemberDeclarationSyntax> MixinPartialClasses(SourceFileContext ctx, ServiceDetails svc)
        {
            if (svc.Mixins.Any())
            {
                // Emit partial class to give access to clients for mixin APIs.
                // (We may well have one of these *and* an LRO partial class, but that's okay.)
                var grpcOuterCls = Class(Public | Static | Partial, svc.GrpcClientTyp.DeclaringTyp);
                using (ctx.InClass(grpcOuterCls))
                {
                    var grpcInnerClass = Class(Public | Partial, svc.GrpcClientTyp);
                    using (ctx.InClass(grpcInnerClass))
                    {
                        var callInvoker = Property(Private, ctx.TypeDontCare, "CallInvoker");

                        foreach (var mixin in svc.Mixins)
                        {
                            var grpcClientType     = ctx.Type(mixin.GrpcClientType);
                            var createClientMethod = Method(Public | Virtual, grpcClientType, "Create" + mixin.GrpcClientType.Name)()
                                                     .WithBody(New(grpcClientType)(callInvoker))
                                                     .WithXmlDoc(
                                XmlDoc.Summary("Creates a new instance of ", grpcClientType, " using the same call invoker as this client."),
                                XmlDoc.Returns("A new ", grpcClientType, " for the same target as this client.")
                                );
                            grpcInnerClass = grpcInnerClass.AddMembers(createClientMethod);
                        }
                    }
                    grpcOuterCls = grpcOuterCls.AddMembers(grpcInnerClass);
                }
                yield return(grpcOuterCls);
            }
        }
Example #29
0
 private ServiceMethodGenerator(SourceFileContext ctx, ServiceDetails svc, bool inAbstract) =>
 (_ctx, _svc, _inAbstract) = (ctx, svc, inAbstract);
Example #30
0
        private static IEnumerable <MemberDeclarationSyntax> LroPartialClasses(SourceFileContext ctx, ServiceDetails svc)
        {
            if (svc.Methods.Any(m => m is MethodDetails.StandardLro))
            {
                // Emit partial class to give access to an LRO operations client.
                var grpcOuterCls = Class(Public | Static | Partial, svc.GrpcClientTyp.DeclaringTyp);
                using (ctx.InClass(grpcOuterCls))
                {
                    var grpcInnerClass = Class(Public | Partial, svc.GrpcClientTyp);
                    using (ctx.InClass(grpcInnerClass))
                    {
                        var callInvoker = Property(Private, ctx.TypeDontCare, "CallInvoker");
                        var opTyp       = ctx.Type <Operations.OperationsClient>();
                        var createOperationsClientMethod = Method(Public | Virtual, opTyp, "CreateOperationsClient")()
                                                           .WithBody(New(opTyp)(callInvoker))
                                                           .WithXmlDoc(
                            XmlDoc.Summary("Creates a new instance of ", opTyp, " using the same call invoker as this client."),
                            XmlDoc.Returns("A new Operations client for the same target as this client.")
                            );
                        grpcInnerClass = grpcInnerClass.AddMembers(createOperationsClientMethod);
                    }
                    grpcOuterCls = grpcOuterCls.AddMembers(grpcInnerClass);
                }
                yield return(grpcOuterCls);
            }

            // Generate partial classes to delegate to other services handling operations
            if (svc.Methods.Any(m => m is MethodDetails.NonStandardLro))
            {
                var operationServices = svc.Methods.OfType <MethodDetails.NonStandardLro>().Select(lro => lro.OperationService).Distinct().ToList();

                // Emit partial class to give access to an LRO operations client.
                var grpcOuterCls = Class(Public | Static | Partial, svc.GrpcClientTyp.DeclaringTyp);
                using (ctx.InClass(grpcOuterCls))
                {
                    var grpcInnerClass = Class(Public | Partial, svc.GrpcClientTyp);
                    using (ctx.InClass(grpcInnerClass))
                    {
                        var callInvoker = Property(Private, ctx.TypeDontCare, "CallInvoker");
                        var opTyp       = ctx.Type <Operations.OperationsClient>();

                        foreach (var operationService in operationServices)
                        {
                            var grpcClient = ctx.Type(Typ.Nested(Typ.Manual(ctx.Namespace, operationService), $"{operationService}Client"));
                            var createOperationsClientMethod = Method(Public | Virtual, opTyp, $"CreateOperationsClientFor{operationService}")()
                                                               .WithBody(grpcClient.Call("CreateOperationsClient")(callInvoker))
                                                               .WithXmlDoc(
                                XmlDoc.Summary("Creates a new instance of ", opTyp, $" using the same call invoker as this client, delegating to {operationService}."),
                                XmlDoc.Returns("A new Operations client for the same target as this client.")
                                );
                            grpcInnerClass = grpcInnerClass.AddMembers(createOperationsClientMethod);
                        }
                    }
                    grpcOuterCls = grpcOuterCls.AddMembers(grpcInnerClass);
                }
                yield return(grpcOuterCls);
            }

            // Generate partial classes for the operation-handling services
            if (svc.NonStandardLro is ServiceDetails.NonStandardLroDetails lroDetails)
            {
                // Emit partial class to give access to an LRO operations client.
                var grpcOuterCls = Class(Public | Static | Partial, svc.GrpcClientTyp.DeclaringTyp);
                using (ctx.InClass(grpcOuterCls))
                {
                    var grpcInnerClass = Class(Public | Partial, svc.GrpcClientTyp);
                    using (ctx.InClass(grpcInnerClass))
                    {
                        var callInvoker                  = Parameter(ctx.Type <CallInvoker>(), "callInvoker");
                        var request                      = Parameter(ctx.TypeDontCare, "request");
                        var response                     = Parameter(ctx.TypeDontCare, "response");
                        var opTyp                        = ctx.Type <Operations.OperationsClient>();
                        var forwardingCallInvoker        = Local(ctx.Type <CallInvoker>(), "forwardingCallInvoker");
                        var createOperationsClientMethod = Method(Internal | Static, opTyp, "CreateOperationsClient")(callInvoker)
                                                           .WithBody(
                            forwardingCallInvoker.WithInitializer(
                                // Note: can't use Typ.Of<ForwardingCallInvoker<GetOperationRequest>> as it's a static class.
                                ctx.Type(Typ.Generic(Typ.Of(typeof(ForwardingCallInvoker <>)), Typ.Of <GetOperationRequest>())).Call("Create")(
                                    callInvoker,
                                    "/google.longrunning.Operations/GetOperation",
                                    Property(Private, ctx.TypeDontCare, $"__Method_{lroDetails.PollingMethod.Name}"),
                                    ctx.Type(lroDetails.PollingRequestTyp).Access("ParseLroRequest"),     // Method group conversion
                                    Lambda(request, response)(response.Call("ToLroResponse")(request.Access("Name")))
                                    )),
                            Return(New(opTyp)(forwardingCallInvoker)))
                                                           .WithXmlDoc(
                            XmlDoc.Summary(
                                "Creates a new instance of ", opTyp, "using the specified call invoker, but ",
                                $"redirecting Google.LongRunning RPCs to {lroDetails.Service.Name} RPCs."),
                            XmlDoc.Returns("A new Operations client for the same target as this client.")
                            );
                        grpcInnerClass = grpcInnerClass.AddMembers(createOperationsClientMethod);
                    }
                    grpcOuterCls = grpcOuterCls.AddMembers(grpcInnerClass);
                }
                yield return(grpcOuterCls);
            }
        }