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); }
// Ctor for wildcard only. private Definition() { Patterns = new[] { new Pattern("*") }; ResourceNameTyp = Typ.Of <IResourceName>(); ResourceParserTyp = Typ.Of <UnparsedResourceName>(); IsUnparsed = true; }
public MixinDetails(string grpcServiceName, System.Type gapicClientType, System.Type gapicClientImplType, System.Type grpcClientType, System.Type gapicSettingsType) { GrpcServiceName = grpcServiceName; GapicClientType = Typ.Of(gapicClientType); GapicClientImplType = Typ.Of(gapicClientImplType); GrpcClientType = Typ.Of(grpcClientType); GapicSettingsType = Typ.Of(gapicSettingsType); }
/// <summary> /// Determines how code referring to this data model should be refer to it; this takes /// into account whether it's a placeholder, an array etc. /// </summary> internal Typ GetTypForReference() { var ret = IsPlaceholder ? Typ.Of <object>() : Typ; if (IsArray) { ret = Typ.Generic(typeof(IList <>), ret); } if (_schema.AdditionalProperties is object) { ret = SchemaTypes.GetTypFromAdditionalProperties(Package, _schema.AdditionalProperties, Name, ret, inParameter: false); } return(ret); }
private static MemberDeclarationSyntax PaginatedPartialInterfaceClass(SourceFileContext ctx, Typ typ, MessageDescriptor messageDesc) { var partialInterfaceCls = Class(Public | Partial, typ, baseTypes: ctx.Type <IPageRequest>()); if (messageDesc.FindFieldByName("page_size") is null) { //DiREGapic scenario where `max_results` is an option for a page size-semantic field. var maxResMessage = messageDesc.FindFieldByName("max_results"); if (maxResMessage is null) { throw new InvalidOperationException("Paginated request should have either page_size or max_results field."); } using (ctx.InClass(partialInterfaceCls)) { var underlyingProperty = Property(DontCare, ctx.TypeDontCare, "MaxResults"); var getBody = ProtoTyp.Of(maxResMessage) == Typ.Of <int>() ? Return(underlyingProperty) : Return(CheckedCast(ctx.Type <int>(), underlyingProperty)); var assignFrom = ProtoTyp.Of(maxResMessage) == Typ.Of <int>() ? Value : CheckedCast(ctx.Type(ProtoTyp.Of(maxResMessage)), Value); var setBody = underlyingProperty.Assign(assignFrom); var property = Property(Public, ctx.Type <int>(), "PageSize") .WithGetBody(getBody) .WithSetBody(setBody) .WithXmlDoc(XmlDoc.InheritDoc); partialInterfaceCls = partialInterfaceCls.AddMembers(property); } } return(partialInterfaceCls); }
private static ClassDeclarationSyntax GenerateClass(string ns, SourceFileContext ctx, IEnumerable <FileDescriptor> packageFileDescriptors) { var typ = Typ.Manual(ns, ClassName); var cls = Class(Internal | Static, typ) .WithXmlDoc(XmlDoc.Summary("Static class to provide common access to package-wide API metadata.")); var yieldStatements = packageFileDescriptors.Select(GenerateYieldStatement).ToArray(); var fileDescriptorMethod = Method(Private | Static, ctx.Type(Typ.Of <IEnumerable <FileDescriptor> >()), "GetFileDescriptors")() .WithBlockBody(yieldStatements); var apiMetadataType = ctx.Type <ApiMetadata>(); var property = AutoProperty(Internal | Static, apiMetadataType, PropertyName) .WithInitializer(New(apiMetadataType)(ns, IdentifierName(fileDescriptorMethod.Identifier))) .WithXmlDoc(XmlDoc.Summary("The ", apiMetadataType, " for services in this package.")); return(cls.AddMembers(property, fileDescriptorMethod)); YieldStatementSyntax GenerateYieldStatement(FileDescriptor descriptor) { var type = ctx.Type(ProtoTyp.OfReflectionClass(descriptor)); return(YieldStatement(SyntaxKind.YieldReturnStatement, type.Access("Descriptor"))); } }
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); } }
public ClassDeclarationSyntax GenerateServiceClass(SourceFileContext ctx) { var cls = Class(Modifier.Public, ServiceTyp, ctx.Type <BaseClientService>()).WithXmlDoc(XmlDoc.Summary($"The {ClassName} Service.")); using (ctx.InClass(cls)) { var discoveryVersionTyp = Typ.Manual("Google.Apis.Discovery", "DiscoveryVersion"); var version = Field(Modifier.Public | Modifier.Const, ctx.Type <string>(), "Version") .WithInitializer(ApiVersion) .WithXmlDoc(XmlDoc.Summary("The API version.")); var discoveryVersion = Field(Modifier.Public | Modifier.Static, ctx.Type(discoveryVersionTyp), "DiscoveryVersionUsed") .WithInitializer(ctx.Type(discoveryVersionTyp).Access(nameof(DiscoveryVersion.Version_1_0))) .WithXmlDoc(XmlDoc.Summary("The discovery version used to generate this service.")); var parameterlessCtor = Ctor(Modifier.Public, cls, ThisInitializer(New(ctx.Type <BaseClientService.Initializer>())()))() .WithBody() .WithXmlDoc(XmlDoc.Summary("Constructs a new service.")); var initializerParam = Parameter(ctx.Type <BaseClientService.Initializer>(), "initializer"); var featuresArrayInitializer = ApiFeatures.Any() ? NewArray(ctx.ArrayType(Typ.Of <string[]>()))(ApiFeatures.ToArray()) : NewArray(ctx.ArrayType(Typ.Of <string[]>()), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0))); var features = Property(Modifier.Public | Modifier.Override, ctx.Type <IList <string> >(), "Features") .WithGetBody(featuresArrayInitializer) .WithXmlDoc(XmlDoc.Summary("Gets the service supported features.")); var nameProperty = Property(Modifier.Public | Modifier.Override, ctx.Type <string>(), "Name") .WithGetBody(ApiName) .WithXmlDoc(XmlDoc.Summary("Gets the service name.")); // Note: the following 4 properties have special handling post-generation, in terms // of adding the #if directives in. var baseUri = Property(Modifier.Public | Modifier.Override, ctx.Type <string>(), "BaseUri") .WithGetBody(IdentifierName("BaseUriOverride").NullCoalesce(BaseUri)) .WithXmlDoc(XmlDoc.Summary("Gets the service base URI.")); var basePath = Property(Modifier.Public | Modifier.Override, ctx.Type <string>(), "BasePath") .WithGetBody(BasePath) .WithXmlDoc(XmlDoc.Summary("Gets the service base path.")); var batchUri = Property(Modifier.Public | Modifier.Override, ctx.Type <string>(), "BatchUri") .WithGetBody(BatchUri) .WithXmlDoc(XmlDoc.Summary("Gets the batch base URI; ", XmlDoc.C("null"), " if unspecified.")); var batchPath = Property(Modifier.Public | Modifier.Override, ctx.Type <string>(), "BatchPath") .WithGetBody(BatchPath) .WithXmlDoc(XmlDoc.Summary("Gets the batch base path; ", XmlDoc.C("null"), " if unspecified.")); var resourceProperties = Resources.Select(resource => resource.GenerateProperty(ctx)).ToArray(); var parameterizedCtor = Ctor(Modifier.Public, cls, BaseInitializer(initializerParam))(initializerParam) .WithBlockBody(resourceProperties.Zip(Resources).Select(pair => pair.First.Assign(New(ctx.Type(pair.Second.Typ))(This))).ToArray()) .WithXmlDoc( XmlDoc.Summary("Constructs a new service."), XmlDoc.Param(initializerParam, "The service initializer.")); cls = cls.AddMembers(version, discoveryVersion, parameterlessCtor, parameterizedCtor, features, nameProperty, baseUri, basePath, batchUri, batchPath); if (AuthScopes.Any()) { var scopeClass = Class(Modifier.Public, Typ.Manual(PackageName, "Scope")) .WithXmlDoc(XmlDoc.Summary($"Available OAuth 2.0 scopes for use with the {Title}.")); using (ctx.InClass(scopeClass)) { foreach (var scope in AuthScopes) { var field = Field(Modifier.Public | Modifier.Static, ctx.Type <string>(), scope.Name) .WithInitializer(scope.Value) .WithXmlDoc(XmlDoc.Summary(scope.Description)); scopeClass = scopeClass.AddMembers(field); } } var scopeConstantsClass = Class(Modifier.Public | Modifier.Static, Typ.Manual(PackageName, "ScopeConstants")) .WithXmlDoc(XmlDoc.Summary($"Available OAuth 2.0 scope constants for use with the {Title}.")); using (ctx.InClass(scopeConstantsClass)) { foreach (var scope in AuthScopes) { var field = Field(Modifier.Public | Modifier.Const, ctx.Type <string>(), scope.Name) .WithInitializer(scope.Value) .WithXmlDoc(XmlDoc.Summary(scope.Description)); scopeConstantsClass = scopeConstantsClass.AddMembers(field); } } cls = cls.AddMembers(scopeClass, scopeConstantsClass); } // TODO: Find an example of this... foreach (var method in Methods) { cls = cls.AddMembers(method.GenerateDeclarations(ctx).ToArray()); } cls = cls.AddMembers(resourceProperties); } return(cls); }
public MethodModel(PackageModel package, ResourceModel resource, string name, RestMethod restMethod) { Package = package; Resource = resource; Name = name; PascalCasedName = name.ToClassName(package, resource.ClassName); ParentTyp = resource?.Typ ?? package.ServiceTyp; RequestTyp = Typ.Nested(ParentTyp, $"{PascalCasedName}Request"); BodyTyp = restMethod.Request is object?package.GetDataModelByReference(restMethod.Request.Ref__).GetTypForReference() : null; ResponseTyp = restMethod.Response is object?package.GetDataModelByReference(restMethod.Response.Ref__).GetTypForReference() : Typ.Of <string>(); _restMethod = restMethod; }
private IEnumerable <MemberDeclarationSyntax> GenerateUploadMembers(SourceFileContext ctx) { var uploadTyp = Typ.Nested(ctx.CurrentTyp, PascalCasedName + "MediaUpload"); var baseTypArgument1 = BodyTyp ?? Typ.Of <string>(); var baseTyp = _restMethod.Response is object ?Typ.Generic(Typ.Of(typeof(ResumableUpload <,>)), baseTypArgument1, ResponseTyp) : Typ.Generic(Typ.Of(typeof(ResumableUpload <>)), baseTypArgument1); var uploadCls = Class(Modifier.Public, uploadTyp, ctx.Type(baseTyp)) .WithXmlDoc(XmlDoc.Summary($"{PascalCasedName} media upload which supports resumable upload.")); var parameters = CreateParameterList(uploadTyp); var requiredParameters = parameters .TakeWhile(p => p.IsRequired) .Select(p => (param: p, decl: Parameter(ctx.Type(p.Typ), p.CodeParameterName))) .ToList(); var insertMethodParameters = new List <ParameterSyntax>(); if (BodyTyp is object) { insertMethodParameters.Add(Parameter(ctx.Type(BodyTyp), "body")); } insertMethodParameters.AddRange(requiredParameters.Select(pair => pair.decl)); var streamParam = Parameter(ctx.Type <Stream>(), "stream"); var contentTypeParam = Parameter(ctx.Type <string>(), "contentType"); insertMethodParameters.Add(streamParam); insertMethodParameters.Add(contentTypeParam); // This doc comment is common between the method and the constructor. var remarks = XmlDoc.Remarks("Considerations regarding ", streamParam, ":", XmlDoc.BulletListOfItemNodes( XmlDoc.Item("If ", streamParam, " is seekable, then the stream position will be reset to ", 0, " before reading commences. If ", streamParam, " is not seekable, then it will be read from its current position"), XmlDoc.Item("Caller is responsible for maintaining the ", streamParam, " open until the upload is completed"), XmlDoc.Item("Caller is responsible for closing the ", streamParam))); var methodDocComments = new List <DocumentationCommentTriviaSyntax> { XmlDoc.Summary(_restMethod.Description), remarks }; if (BodyTyp is object) { methodDocComments.Add(XmlDoc.Param(Parameter(ctx.Type(BodyTyp), "body"), "The body of the request.")); } methodDocComments.AddRange(requiredParameters.Select(pair => XmlDoc.Param(pair.decl, pair.param.Description))); methodDocComments.Add(XmlDoc.Param(streamParam, "The stream to upload. See remarks for further information.")); methodDocComments.Add(XmlDoc.Param(contentTypeParam, "The content type of the stream to upload.")); // We're taking it from the field in the parent type, but it becomes a parameter for the constructor... // So long as it appears as "service" we don't really mind. var serviceParam = Parameter(ctx.Type <IClientService>(), "service"); var ctorParameters = new List <ParameterSyntax> { serviceParam }; ctorParameters.AddRange(insertMethodParameters); var insertMethod = Method(Modifier.Public | Modifier.Virtual, ctx.Type(uploadTyp), PascalCasedName)(insertMethodParameters.ToArray()) .WithBlockBody(Return(New(ctx.Type(uploadTyp))(ctorParameters))) .WithXmlDoc(methodDocComments.ToArray()); using (ctx.InClass(uploadTyp)) { var baseInitializerArgs = new List <object> { serviceParam, ctx.Type <string>().Call(nameof(string.Format))("/{0}/{1}{2}", "upload", serviceParam.Access("BasePath"), _restMethod.Path), _restMethod.HttpMethod, streamParam, contentTypeParam }; var baseInitializer = BaseInitializer(baseInitializerArgs).WithAdditionalAnnotations(Annotations.LineBreakAnnotation); var assignments = requiredParameters.Select(p => Field(0, ctx.Type(p.param.Typ), p.param.PropertyName).Assign(p.decl)).ToList(); if (BodyTyp is object) { // The Body property is in the base class. var bodyProperty = Property(Modifier.Public, ctx.Type(BodyTyp), "Body"); assignments.Add(bodyProperty.Assign(Parameter(ctx.Type(BodyTyp), "body"))); } // The parameters to the constructor are the same as for the method. var uploadCtor = Ctor(Modifier.Public, uploadCls, baseInitializer)(ctorParameters.ToArray()) .WithBlockBody(assignments) .WithXmlDoc(XmlDoc.Summary($"Constructs a new {PascalCasedName} media upload instance."), remarks); uploadCls = uploadCls.AddMembers(Package.CreateParameterList(uploadTyp).SelectMany(p => p.GenerateDeclarations(ctx)).ToArray()); uploadCls = uploadCls.AddMembers(parameters.SelectMany(p => p.GenerateDeclarations(ctx)).ToArray()); uploadCls = uploadCls.AddMembers(uploadCtor); } yield return(insertMethod); yield return(uploadCls); }