Exemple #1
0
        private static IEnumerable <ResultFile> GeneratePackage(string ns,
                                                                IEnumerable <FileDescriptor> packageFileDescriptors, ProtoCatalog catalog, IClock clock,
                                                                ServiceConfig grpcServiceConfig, Service serviceConfig, List <ServiceDetails> allServiceDetails, ApiTransports transports)
        {
            var              clientPathPrefix          = $"{ns}{Path.DirectorySeparatorChar}";
            var              serviceSnippetsPathPrefix = $"{ns}.Snippets{Path.DirectorySeparatorChar}";
            var              snippetsPathPrefix        = $"{ns}.GeneratedSnippets{Path.DirectorySeparatorChar}";
            var              unitTestsPathPrefix       = $"{ns}.Tests{Path.DirectorySeparatorChar}";
            bool             hasLro                       = false;
            HashSet <string> mixins                       = new HashSet <string>();
            bool             hasContent                   = false;
            var              packageServiceDetails        = new List <ServiceDetails>();
            HashSet <string> allResourceNameClasses       = new HashSet <string>();
            HashSet <string> duplicateResourceNameClasses = new HashSet <string>();
            IList <Snippet>  snippets                     = new List <Snippet>();

            IEnumerable <Typ> packageTyps = packageFileDescriptors.SelectMany(
                fileDescriptor => fileDescriptor.Services.Select(serv => Typ.Manual(ns, serv.Name))
                .Union(fileDescriptor.MessageTypes.Select(msg => Typ.Manual(ns, msg.Name)))
                .Union(fileDescriptor.EnumTypes.Select(e => Typ.Manual(ns, e.Name, isEnum: true))));

            var seenPaginatedResponseTyps = new HashSet <Typ>();

            foreach (var fileDesc in packageFileDescriptors)
            {
                foreach (var service in fileDesc.Services)
                {
                    // Generate settings and client code for requested package.
                    var serviceDetails = new ServiceDetails(catalog, ns, service, grpcServiceConfig, serviceConfig, transports);
                    packageServiceDetails.Add(serviceDetails);

                    var ctx      = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases);
                    var code     = ServiceCodeGenerator.Generate(ctx, serviceDetails, seenPaginatedResponseTyps);
                    var filename = $"{clientPathPrefix}{serviceDetails.ClientAbstractTyp.Name}.g.cs";
                    yield return(new ResultFile(filename, code));

                    // Generate service-per-file snippets for the service
                    // TODO: Consider removing this once we have integrated the snippet-per-file snippets
                    // with docs generation.
                    var serviceSnippetsCtx = SourceFileContext.CreateUnaliased(
                        clock, s_wellknownNamespaceAliases, s_avoidAliasingNamespaceRegex, packageTyps, maySkipOwnNamespaceImport: true);
                    var serviceSnippetsCode     = SnippetCodeGenerator.Generate(serviceSnippetsCtx, serviceDetails);
                    var serviceSnippetsFilename = $"{serviceSnippetsPathPrefix}{serviceDetails.ClientAbstractTyp.Name}Snippets.g.cs";
                    yield return(new ResultFile(serviceSnippetsFilename, serviceSnippetsCode));

                    // Generate snippet-per-file snippets for the service
                    // TODO: Integrate snippet-per-file snippets with docs generation.
                    foreach (var snippetGenerator in SnippetCodeGenerator.SnippetsGenerators(serviceDetails))
                    {
                        var snippetCtx = SourceFileContext.CreateUnaliased(
                            clock, s_wellknownNamespaceAliases, s_avoidAliasingNamespaceRegex, packageTyps, maySkipOwnNamespaceImport: false);
                        var(snippetCode, snippetMetadata) = snippetGenerator.Generate(snippetCtx);
                        snippetMetadata.File = $"{serviceDetails.ClientAbstractTyp.Name}.{snippetGenerator.SnippetMethodName}Snippet.g.cs";
                        var snippetFile = $"{snippetsPathPrefix}{snippetMetadata.File}";
                        snippets.Add(snippetMetadata);
                        yield return(new ResultFile(snippetFile, snippetCode));
                    }
                    // Generate unit tests for the the service.
                    var unitTestCtx      = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases);
                    var unitTestCode     = UnitTestCodeGeneration.Generate(unitTestCtx, serviceDetails);
                    var unitTestFilename = $"{unitTestsPathPrefix}{serviceDetails.ClientAbstractTyp.Name}Test.g.cs";
                    yield return(new ResultFile(unitTestFilename, unitTestCode));

                    // Record whether LRO/mixins are used.
                    hasLro |= serviceDetails.Methods.Any(x => x is MethodDetails.Lro);
                    foreach (var mixin in serviceDetails.Mixins)
                    {
                        mixins.Add(mixin.GrpcServiceName);
                    }
                    hasContent = true;
                }
                var resCtx = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases);
                var(resCode, resCodeClassCount) = ResourceNamesGenerator.Generate(catalog, resCtx, fileDesc);
                // Only produce an output file if it contains >0 [partial] classes.
                if (resCodeClassCount > 0)
                {
                    // Keep track of the resource names, to spot duplicates
                    var resourceNameClasses = catalog.GetResourceDefsByFile(fileDesc)
                                              .Where(def => def.HasNotWildcard && !def.IsCommon)
                                              .Select(def => def.ResourceNameTyp.Name);
                    foreach (var resourceNameClass in resourceNameClasses)
                    {
                        if (!allResourceNameClasses.Add(resourceNameClass))
                        {
                            duplicateResourceNameClasses.Add(resourceNameClass);
                        }
                    }

                    // Yield the file to be generated.
                    var filenamePrefix = Path.GetFileNameWithoutExtension(fileDesc.Name).ToUpperCamelCase();
                    var resFilename    = $"{clientPathPrefix}{filenamePrefix}ResourceNames.g.cs";
                    yield return(new ResultFile(resFilename, resCode));

                    hasContent = true;
                }

                var lroAdaptationCtx = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases);
                var(lroCode, lroClassCount) = LroAdaptationGenerator.Generate(catalog, lroAdaptationCtx, fileDesc);
                if (lroClassCount > 0)
                {
                    var filenamePrefix        = Path.GetFileNameWithoutExtension(fileDesc.Name).ToUpperCamelCase();
                    var lroAdaptationFilename = $"{clientPathPrefix}{filenamePrefix}LroAdaptation.g.cs";
                    yield return(new ResultFile(lroAdaptationFilename, lroCode));

                    hasContent = true;
                }
            }
            // Now we've processed all the files, check for duplicate resource names.
            if (duplicateResourceNameClasses.Count > 0)
            {
                throw new InvalidOperationException($"The following resource name classes were created multiple times: {string.Join(", ", duplicateResourceNameClasses)}");
            }
            // Only output csproj's and snippet metadata if there is any other generated content.
            // When processing a (proto) package without any services there will be no generated content.
            if (hasContent)
            {
                // Generate client csproj.
                var csprojContent  = CsProjGenerator.GenerateClient(hasLro, mixins);
                var csprojFilename = $"{clientPathPrefix}{ns}.csproj";
                yield return(new ResultFile(csprojFilename, csprojContent));

                // If we only generated resources, we don't need to generate all of these.
                if (packageServiceDetails.Count > 0)
                {
                    allServiceDetails.AddRange(packageServiceDetails);

                    // Generate the package-wide API metadata
                    var ctx = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases);
                    var packageApiMetadataContent  = PackageApiMetadataGenerator.GeneratePackageApiMetadata(ns, ctx, packageFileDescriptors);
                    var packageApiMetadataFilename = $"{clientPathPrefix}{PackageApiMetadataGenerator.FileName}";
                    yield return(new ResultFile(packageApiMetadataFilename, packageApiMetadataContent));

                    // Generate snippets csproj.
                    var serviceSnippetsCsprojContent  = CsProjGenerator.GenerateSnippets(ns);
                    var serviceSnippetsCsProjFilename = $"{serviceSnippetsPathPrefix}{ns}.Snippets.csproj";
                    yield return(new ResultFile(serviceSnippetsCsProjFilename, serviceSnippetsCsprojContent));

                    // Generate dependency injection extension methods
                    var dependencyInjectionExtensionsContent  = ServiceCollectionExtensionsGenerator.GenerateExtensions(ctx, packageServiceDetails);
                    var dependencyInjectionExtensionsFilename = $"{clientPathPrefix}{ServiceCollectionExtensionsGenerator.FileName}";
                    yield return(new ResultFile(dependencyInjectionExtensionsFilename, dependencyInjectionExtensionsContent));

                    // Generate snippet metadata (only for snippet-per-file snippets).
                    // All services in this package have the same package information, namespace etc. so, we
                    // just pick the first one
                    var serviceDetails          = packageServiceDetails.First();
                    var snippetIndexJsonContent = SnippetCodeGenerator.GenerateSnippetIndexJson(snippets, serviceDetails);
                    yield return(new ResultFile($"{snippetsPathPrefix}snippet_metadata_{serviceDetails.ProtoPackage}.json", snippetIndexJsonContent));

                    // Generate snippet-per-file snippets csproj.
                    var snippetsCsprojContent  = CsProjGenerator.GenerateSnippets(ns);
                    var snippetsCsProjFilename = $"{snippetsPathPrefix}{ns}.GeneratedSnippets.csproj";
                    yield return(new ResultFile(snippetsCsProjFilename, snippetsCsprojContent));

                    // Generate unit-tests csproj.
                    var unitTestsCsprojContent  = CsProjGenerator.GenerateUnitTests(ns);
                    var unitTestsCsprojFilename = $"{unitTestsPathPrefix}{ns}.Tests.csproj";
                    yield return(new ResultFile(unitTestsCsprojFilename, unitTestsCsprojContent));
                }
            }
        }
        private static IEnumerable <ResultFile> GeneratePackage(string ns, IEnumerable <FileDescriptor> packageFileDescriptors, ProtoCatalog catalog, IClock clock,
                                                                ServiceConfig grpcServiceConfig, bool generateMetadata)
        {
            var              clientPathPrefix             = $"{ns}{Path.DirectorySeparatorChar}";
            var              snippetsPathPrefix           = $"{ns}.Snippets{Path.DirectorySeparatorChar}";
            var              standaloneSnippetsPathPrefix = $"{ns}.StandaloneSnippets{Path.DirectorySeparatorChar}";
            var              unitTestsPathPrefix          = $"{ns}.Tests{Path.DirectorySeparatorChar}";
            bool             hasLro                       = false;
            bool             hasContent                   = false;
            HashSet <string> allResourceNameClasses       = new HashSet <string>();
            HashSet <string> duplicateResourceNameClasses = new HashSet <string>();

            var allServiceDetails = new List <ServiceDetails>();

            foreach (var fileDesc in packageFileDescriptors)
            {
                foreach (var service in fileDesc.Services)
                {
                    // Generate settings and client code for requested package.
                    var serviceDetails = new ServiceDetails(catalog, ns, service, grpcServiceConfig);
                    allServiceDetails.Add(serviceDetails);

                    var ctx      = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases);
                    var code     = ServiceCodeGenerator.Generate(ctx, serviceDetails);
                    var filename = $"{clientPathPrefix}{serviceDetails.ClientAbstractTyp.Name}.g.cs";
                    yield return(new ResultFile(filename, code));

                    // Generate snippets for the service
                    // TODO: Consider removing this once we have integrated the standalone snippets
                    // with docs generation.
                    var snippetCtx = SourceFileContext.CreateUnaliased(
                        clock, s_wellknownNamespaceAliases, s_avoidAliasingNamespaceRegex, maySkipOwnNamespaceImport: true);
                    var snippetCode     = SnippetCodeGenerator.Generate(snippetCtx, serviceDetails);
                    var snippetFilename = $"{snippetsPathPrefix}{serviceDetails.ClientAbstractTyp.Name}Snippets.g.cs";
                    yield return(new ResultFile(snippetFilename, snippetCode));

                    // Generate standalone snippets for the service
                    // TODO: Integrate standalone snippets with docs generation.
                    // TODO: Once (and if we) generate just one set of snippets, stop using "standalone" as a differentiatior.
                    foreach (var snippetGenerator in SnippetCodeGenerator.StandaloneGenerators(serviceDetails))
                    {
                        var standaloneSnippetCtx = SourceFileContext.CreateUnaliased(
                            clock, s_wellknownNamespaceAliases, s_avoidAliasingNamespaceRegex, maySkipOwnNamespaceImport: false);
                        var standaloneSnippetCode     = snippetGenerator.Generate(standaloneSnippetCtx);
                        var standaloneSnippetFilename = $"{standaloneSnippetsPathPrefix}{serviceDetails.ClientAbstractTyp.Name}.{snippetGenerator.SnippetMethodName}Snippet.g.cs";
                        yield return(new ResultFile(standaloneSnippetFilename, standaloneSnippetCode));
                    }
                    // Generate unit tests for the the service.
                    var unitTestCtx      = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases);
                    var unitTestCode     = UnitTestCodeGeneration.Generate(unitTestCtx, serviceDetails);
                    var unitTestFilename = $"{unitTestsPathPrefix}{serviceDetails.ClientAbstractTyp.Name}Test.g.cs";
                    yield return(new ResultFile(unitTestFilename, unitTestCode));

                    // Record whether LRO is used.
                    hasLro    |= serviceDetails.Methods.Any(x => x is MethodDetails.Lro);
                    hasContent = true;
                }
                var resCtx = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases);
                var(resCode, resCodeClassCount) = ResourceNamesGenerator.Generate(catalog, resCtx, fileDesc);
                // Only produce an output file if it contains >0 [partial] classes.
                if (resCodeClassCount > 0)
                {
                    // Keep track of the resource names, to spot duplicates
                    var resourceNameClasses = catalog.GetResourceDefsByFile(fileDesc)
                                              .Where(def => def.HasNotWildcard && !def.IsCommon)
                                              .Select(def => def.ResourceNameTyp.Name);
                    foreach (var resourceNameClass in resourceNameClasses)
                    {
                        if (!allResourceNameClasses.Add(resourceNameClass))
                        {
                            duplicateResourceNameClasses.Add(resourceNameClass);
                        }
                    }

                    // Yield the file to be generated.
                    var filenamePrefix = Path.GetFileNameWithoutExtension(fileDesc.Name).ToUpperCamelCase();
                    var resFilename    = $"{clientPathPrefix}{filenamePrefix}ResourceNames.g.cs";
                    yield return(new ResultFile(resFilename, resCode));

                    hasContent = true;
                }
            }
            // Now we've processed all the files, check for duplicate resource names.
            if (duplicateResourceNameClasses.Count > 0)
            {
                throw new InvalidOperationException($"The following resource name classes were created multiple times: {string.Join(", ", duplicateResourceNameClasses)}");
            }
            // Only output csproj's if there is any other generated content.
            // When processing a (proto) package without any services there will be no generated content.
            if (hasContent)
            {
                // Generate client csproj.
                var csprojContent  = CsProjGenerator.GenerateClient(hasLro);
                var csprojFilename = $"{clientPathPrefix}{ns}.csproj";
                yield return(new ResultFile(csprojFilename, csprojContent));

                // Generate snippets csproj.
                var snippetsCsprojContent  = CsProjGenerator.GenerateSnippets(ns);
                var snippetsCsProjFilename = $"{snippetsPathPrefix}{ns}.Snippets.csproj";
                yield return(new ResultFile(snippetsCsProjFilename, snippetsCsprojContent));

                // Generate standalone snippets csproj.
                var standaloneSnippetsCsprojContent  = CsProjGenerator.GenerateSnippets(ns);
                var standaloneSnippetsCsProjFilename = $"{standaloneSnippetsPathPrefix}{ns}.StandaloneSnippets.csproj";
                yield return(new ResultFile(standaloneSnippetsCsProjFilename, standaloneSnippetsCsprojContent));

                // Generate unit-tests csproj.
                var unitTestsCsprojContent  = CsProjGenerator.GenerateUnitTests(ns);
                var unitTestsCsprojFilename = $"{unitTestsPathPrefix}{ns}.Tests.csproj";
                yield return(new ResultFile(unitTestsCsprojFilename, unitTestsCsprojContent));

                if (generateMetadata && allServiceDetails.Any())
                {
                    // Generate gapic_metadata.json, if there are any services.
                    var gapicMetadataJsonContent = MetadataGenerator.GenerateGapicMetadataJson(allServiceDetails);
                    yield return(new ResultFile("gapic_metadata.json", gapicMetadataJsonContent));
                }
            }
        }
Exemple #3
0
        private static IEnumerable <ResultFile> GeneratePackage(string ns, IEnumerable <FileDescriptor> packageFileDescriptors, ProtoCatalog catalog, IClock clock,
                                                                ServiceConfig grpcServiceConfig)
        {
            var  clientPathPrefix    = $"{ns}{Path.DirectorySeparatorChar}";
            var  snippetsPathPrefix  = $"{ns}.Snippets{Path.DirectorySeparatorChar}";
            var  unitTestsPathPrefix = $"{ns}.Tests{Path.DirectorySeparatorChar}";
            bool hasLro     = false;
            bool hasContent = false;

            foreach (var fileDesc in packageFileDescriptors)
            {
                foreach (var service in fileDesc.Services)
                {
                    // Generate settings and client code for requested package.
                    var serviceDetails = new ServiceDetails(catalog, ns, service, grpcServiceConfig);
                    var ctx            = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases);
                    var code           = ServiceCodeGenerator.Generate(ctx, serviceDetails);
                    var filename       = $"{clientPathPrefix}{serviceDetails.ClientAbstractTyp.Name}.g.cs";
                    yield return(new ResultFile(filename, code));

                    // Generate snippets for the service
                    var snippetCtx      = SourceFileContext.CreateUnaliased(clock);
                    var snippetCode     = SnippetCodeGenerator.Generate(snippetCtx, serviceDetails);
                    var snippetFilename = $"{snippetsPathPrefix}{serviceDetails.ClientAbstractTyp.Name}Snippets.g.cs";
                    yield return(new ResultFile(snippetFilename, snippetCode));

                    // Generate unit tests for the the service.
                    var unitTestCtx      = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases);
                    var unitTestCode     = UnitTestCodeGeneration.Generate(unitTestCtx, serviceDetails);
                    var unitTestFilename = $"{unitTestsPathPrefix}{serviceDetails.ClientAbstractTyp.Name}Test.g.cs";
                    yield return(new ResultFile(unitTestFilename, unitTestCode));

                    // Record whether LRO is used.
                    hasLro    |= serviceDetails.Methods.Any(x => x is MethodDetails.Lro);
                    hasContent = true;
                }
                var resCtx = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases);
                var(resCode, resCodeClassCount) = ResourceNamesGenerator.Generate(catalog, resCtx, fileDesc);
                // Only produce an output file if it contains >0 [partial] classes.
                if (resCodeClassCount > 0)
                {
                    var filenamePrefix = Path.GetFileNameWithoutExtension(fileDesc.Name).ToUpperCamelCase();
                    var resFilename    = $"{clientPathPrefix}{filenamePrefix}ResourceNames.g.cs";
                    yield return(new ResultFile(resFilename, resCode));

                    hasContent = true;
                }
            }
            // Only output csproj's if there is any other generated content.
            // When processing a (proto) package without any services there will be no generated content.
            if (hasContent)
            {
                // Generate client csproj.
                var csprojContent  = CsProjGenerator.GenerateClient(hasLro);
                var csprojFilename = $"{clientPathPrefix}{ns}.csproj";
                yield return(new ResultFile(csprojFilename, csprojContent));

                // Generate snippets csproj.
                var snippetsCsprojContent  = CsProjGenerator.GenerateSnippets(ns);
                var snippetsCsProjFilename = $"{snippetsPathPrefix}{ns}.Snippets.csproj";
                yield return(new ResultFile(snippetsCsProjFilename, snippetsCsprojContent));

                // Generate unit-tests csproj.
                var unitTestsCsprojContent  = CsProjGenerator.GenerateUnitTests(ns);
                var unitTestsCsprojFilename = $"{unitTestsPathPrefix}{ns}.Tests.csproj";
                yield return(new ResultFile(unitTestsCsprojFilename, unitTestsCsprojContent));
            }
        }