diff --git a/cli/tests/BasicTests.cs b/cli/tests/BasicTests.cs index 2fdd89f71c..e50299a9fc 100644 --- a/cli/tests/BasicTests.cs +++ b/cli/tests/BasicTests.cs @@ -145,15 +145,14 @@ public void TestUnrealMicroserviceGen() builder.AddZLoggerConsole(); }).CreateLogger(); - var gen = new ServiceDocGenerator(); - var builder = new DependencyBuilder(); builder.AddSingleton(); builder.AddSingleton>(); builder.AddSingleton(new MicroserviceArgs()); + builder.AddSingleton(); var provider = builder.Build(); - var doc = gen.Generate(provider); + var doc = provider.GetService().GetServiceDocument(typeof(TroublesomeService)); UnrealSourceGenerator.exportMacro = "TROUBLESOMEPROJECT_API"; UnrealSourceGenerator.blueprintExportMacro = "TROUBLESOMEPROJECTBLUEPRINTNODES_API"; diff --git a/microservice/beamable.tooling.common/Microservice/AdminRoutes.cs b/microservice/beamable.tooling.common/Microservice/AdminRoutes.cs index d110b983c9..b5b6ea319a 100644 --- a/microservice/beamable.tooling.common/Microservice/AdminRoutes.cs +++ b/microservice/beamable.tooling.common/Microservice/AdminRoutes.cs @@ -5,6 +5,7 @@ using Beamable.Server; using Beamable.Server.Api.Usage; using Beamable.Server.Editor; +using beamable.tooling.common.Microservice; using Beamable.Tooling.Common.OpenAPI; using Microsoft.Extensions.Logging; using Microsoft.OpenApi; @@ -84,19 +85,7 @@ public string HealthCheck() [CustomResponseSerializationAttribute] public string Docs() { - var docs = new ServiceDocGenerator(); - var ctx = GlobalProvider.GetService(); - var doc = docs.Generate(ctx, GlobalProvider); - - if (!string.IsNullOrEmpty(PublicHost)) - { - doc.Servers.Add(new OpenApiServer { Url = PublicHost }); - } - - var outputString = doc.Serialize(OpenApiSpecVersion.OpenApi3_0, OpenApiFormat.Json); - - - return outputString; + return GlobalProvider.GetService().GetCachedDocs(PublicHost); } /// diff --git a/microservice/beamable.tooling.common/Microservice/ServiceOpenApiDocsCache.cs b/microservice/beamable.tooling.common/Microservice/ServiceOpenApiDocsCache.cs new file mode 100644 index 0000000000..9561ccc93d --- /dev/null +++ b/microservice/beamable.tooling.common/Microservice/ServiceOpenApiDocsCache.cs @@ -0,0 +1,61 @@ +using Beamable.Common.Content; +using Beamable.Common.Dependencies; +using Beamable.Server; +using Beamable.Tooling.Common.OpenAPI; +using Microsoft.OpenApi; +using Microsoft.OpenApi.Extensions; +using Microsoft.OpenApi.Models; + +namespace beamable.tooling.common.Microservice; + + +public interface IServiceOpenApiDocsCache +{ + OpenApiDocument GetServiceDocument(Type type); + string GetCachedDocs(string publicHost); +} + +public class ServiceOpenApiDocsCache : IServiceOpenApiDocsCache +{ + public ServiceOpenApiDocsCache(IDependencyProvider dependencyProvider) + { + _dependencyProvider = dependencyProvider; + } + public bool HasCachedDocs => _cachedDocs.HasValue; + private readonly Optional _cachedDocs = new Optional(); + private readonly IDependencyProvider _dependencyProvider; + private Dictionary _serviceDocuments = new Dictionary(); + + public OpenApiDocument GetServiceDocument(Type type) + { + if(_serviceDocuments.TryGetValue(type, out var doc)) + { + return doc; + } + var gen = new ServiceDocGenerator(); + doc = gen.GenerateDocumentByType(_dependencyProvider, type); + _serviceDocuments[type] = doc; + return doc; + } + + public string GetCachedDocs(string publicHost) + { + if (HasCachedDocs) + { + return _cachedDocs.Value; + } + + var docs = new ServiceDocGenerator(); + var ctx = _dependencyProvider.GetService(); + var doc = docs.Generate(ctx, _dependencyProvider); + if (!string.IsNullOrEmpty(publicHost)) + { + doc.Servers.Add(new OpenApiServer { Url = publicHost }); + } + + var outputString = doc.Serialize(OpenApiSpecVersion.OpenApi3_0, OpenApiFormat.Json); + _cachedDocs.Set(outputString); + + return outputString; + } +} diff --git a/microservice/beamable.tooling.common/OpenAPI/ServiceDocGenerator.cs b/microservice/beamable.tooling.common/OpenAPI/ServiceDocGenerator.cs index 3582581389..f48c051ca8 100644 --- a/microservice/beamable.tooling.common/OpenAPI/ServiceDocGenerator.cs +++ b/microservice/beamable.tooling.common/OpenAPI/ServiceDocGenerator.cs @@ -479,19 +479,33 @@ public static class DocGenExtensions /// An OpenApiDocument containing the generated documentation. public static OpenApiDocument Generate(this ServiceDocGenerator generator, IDependencyProvider provider) where TMicroservice : Microservice { - var attr = typeof(TMicroservice).GetCustomAttribute(typeof(MicroserviceAttribute)) as MicroserviceAttribute; - var startupContext = new StartupContext + return generator.GenerateDocumentByType(provider, typeof(TMicroservice)); + } + + /// + /// Generates OpenAPI documentation for a specified microservice type. + /// + /// The type of the microservice to generate documentation for. + /// The administrative routes associated with the microservice. + /// An OpenApiDocument containing the generated documentation. + public static OpenApiDocument GenerateDocumentByType(this ServiceDocGenerator generator, IDependencyProvider provider, Type type, StartupContext context = null) + { + if (context == null) { - routeSources = new BeamRouteSource[] + var attr = type.GetCustomAttribute(typeof(MicroserviceAttribute)) as MicroserviceAttribute; + context = new StartupContext { - new BeamRouteSource + routeSources = new BeamRouteSource[] { - InstanceType = typeof(TMicroservice) - } - }, - attributes = attr, - args = provider.GetService() - }; - return generator.Generate(startupContext, provider); + new BeamRouteSource + { + InstanceType = type + } + }, + attributes = attr, + args = provider.GetService() + }; + } + return generator.Generate(context, provider); } } diff --git a/microservice/microservice/dbmicroservice/MicroserviceStartupUtil.cs b/microservice/microservice/dbmicroservice/MicroserviceStartupUtil.cs index 73a625b145..692a6082e9 100644 --- a/microservice/microservice/dbmicroservice/MicroserviceStartupUtil.cs +++ b/microservice/microservice/dbmicroservice/MicroserviceStartupUtil.cs @@ -722,6 +722,7 @@ public static IDependencyBuilder ConfigureServices(StartupContext startupContext .AddScoped>.FactoryFunction>(provider => StatsCacheFactory) .AddScoped.FactoryFunction>(provider => LeaderboardRankEntryFactory) + .AddSingleton() .AddScoped(ExtractSdks) // allow the DI system to get a list of all telemetry attribute providers... diff --git a/microservice/microserviceTests/OpenAPITests/DocTests.cs b/microservice/microserviceTests/OpenAPITests/DocTests.cs index d20259b309..9c984bc617 100644 --- a/microservice/microserviceTests/OpenAPITests/DocTests.cs +++ b/microservice/microserviceTests/OpenAPITests/DocTests.cs @@ -54,24 +54,23 @@ public void CreateRequestAttributes(IRequestAttributeContext ctx) public void TestMethodScanning() { LoggingUtil.InitTestCorrelator(); - var gen = new ServiceDocGenerator(); - var builder = new DependencyBuilder(); builder.AddSingleton(); builder.AddSingleton>(); builder.AddSingleton(); builder.AddSingleton(); + builder.AddSingleton(); var provider = builder.Build(); - var doc = gen.Generate(provider); - + + var doc = provider.GetService().GetServiceDocument(typeof(DocService)); + string outputString = doc.Serialize(OpenApiSpecVersion.OpenApi3_0, OpenApiFormat.Json); + + // Assert.AreEqual("docs", doc.Info.Title); // Assert.AreEqual(1, doc.Paths.Count); - // var reqSchema = doc.Paths["/Add"].Operations[OperationType.Post].RequestBody.Content["application/json"].Schema; // Assert.AreEqual(1, reqSchema.Required.Count); - - var outputString = doc.Serialize(OpenApiSpecVersion.OpenApi3_0, OpenApiFormat.Json); Console.WriteLine(outputString); }