diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/Specs/SchemaCache.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/Specs/SchemaCache.cs index ad59f91f7506..6e1f9a6ae7ad 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/Specs/SchemaCache.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/Specs/SchemaCache.cs @@ -9,12 +9,14 @@ public class SchemaCache public Dictionary> Cache; public Dictionary ResolvedCache; public Dictionary> ParametersCache; + public Dictionary> ResponsesCache; public SchemaCache() { this.Cache = new Dictionary>(); this.ResolvedCache = new Dictionary(); this.ParametersCache = new Dictionary>(); + this.ResponsesCache = new Dictionary>(); } public void AddSchema(string swaggerFilePath, string key, Schema value) @@ -41,6 +43,18 @@ public void AddParameter(string swaggerFilePath, string key, Parameter parameter parameterCache.TryAdd(key, parameter); } + public void AddResponse(string swaggerFilePath, string key, Response response) + { + this.ResponsesCache.TryGetValue(swaggerFilePath, out var responseCache); + if (responseCache == null) + { + responseCache = new Dictionary(); + this.ResponsesCache.TryAdd(swaggerFilePath, responseCache); + } + + responseCache.TryAdd(key, response); + } + public static string GetRefKey(string Ref) { var key = Ref.Split("/").Last(); @@ -66,13 +80,13 @@ private Schema GetSchemaFromResolvedCache(string Ref, string currentSwaggerFileP return resolvedSchema; } - private Schema GetSchemaFromCache(string Ref, string currentSwaggerFilePath) + public Schema GetSchemaFromCache(string Ref, string currentSwaggerFilePath, bool resolveSwaggerPath = true) { - // try get from resolved cache. - - - var referenceSwaggerFilePath = Utils.GetReferencedSwaggerFile(Ref, currentSwaggerFilePath); - + var referenceSwaggerFilePath = currentSwaggerFilePath; + if (resolveSwaggerPath) + { + referenceSwaggerFilePath = Utils.GetReferencedSwaggerFile(Ref, currentSwaggerFilePath); + } this.Cache.TryGetValue(referenceSwaggerFilePath, out var swaggerSchema); if (swaggerSchema == null) @@ -91,11 +105,14 @@ private Schema GetSchemaFromCache(string Ref, string currentSwaggerFilePath) return ret; } - public Parameter GetParameterFromCache(string Ref, string currentSwaggerFilePath, ref string referenceSwaggerFilePath) + public Parameter GetParameterFromCache(string Ref, string currentSwaggerFilePath, bool resolveSwaggerPath = true) { - // try get from resolved cache. - referenceSwaggerFilePath = Utils.GetReferencedSwaggerFile(Ref, currentSwaggerFilePath); - + var referenceSwaggerFilePath = currentSwaggerFilePath; + if (resolveSwaggerPath) + { + referenceSwaggerFilePath = Utils.GetReferencedSwaggerFile(Ref, currentSwaggerFilePath); + } + this.ParametersCache.TryGetValue(referenceSwaggerFilePath, out var parameterCache); if (parameterCache == null) { @@ -113,12 +130,36 @@ public Parameter GetParameterFromCache(string Ref, string currentSwaggerFilePath return ret; } + public Response GetResponseFromCache(string Ref, string currentSwaggerFilePath, bool resolveSwaggerPath = true) + { + var referenceSwaggerFilePath = currentSwaggerFilePath; + if (resolveSwaggerPath) + { + referenceSwaggerFilePath = Utils.GetReferencedSwaggerFile(Ref, currentSwaggerFilePath); + } + + this.ResponsesCache.TryGetValue(referenceSwaggerFilePath, out var responseCache); + if (responseCache == null) + { + return null; + } + + var key = GetRefKey(Ref); + responseCache.TryGetValue(key, out var ret); + + if (ret == null) + { + throw new Exception($"Reference not found. $ref: {Ref}"); + } + + return ret; + } + public Parameter GetResolvedParameter(Parameter parameter, string currentSwaggerFilePath) { if (parameter.IsRefObject()) { - var resolvedFromPath = String.Empty; - return this.GetParameterFromCache(parameter.@ref, currentSwaggerFilePath, ref resolvedFromPath); + return this.GetParameterFromCache(parameter.@ref, currentSwaggerFilePath); } return parameter; diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerAPIViewGenerator.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerAPIViewGenerator.cs index e8b4c0083908..5b36124f4e5d 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerAPIViewGenerator.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerAPIViewGenerator.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; @@ -78,42 +77,41 @@ public static async Task GenerateSwaggerApiView(Swagger swag foreach (var parameter in operation.parameters) { var param = parameter; - var resolvedFromPath = swaggerFilePath; + var referenceSwaggerFilePath = swaggerFilePath; + // Resolve Parameter from multilevel reference if (parameter.IsRefObject()) { param = (Parameter)swaggerSpec.ResolveRefObj(parameter.@ref); if (param == null) { - param = (Parameter)schemaCache.GetParameterFromCache(parameter.@ref, swaggerFilePath, ref resolvedFromPath); + param = (Parameter)schemaCache.GetParameterFromCache(parameter.@ref, swaggerFilePath); } - } - if (param == null) - { - var referencePath = parameter.@ref; - do + if (param == null) { - if (!Path.IsPathFullyQualified(referencePath)) + var referencePath = parameter.@ref; + do { - var referenceSwaggerFilePath = Utils.GetReferencedSwaggerFile(referencePath, swaggerFilePath); - var referenceSwaggerSpec = await SwaggerDeserializer.Deserialize(referenceSwaggerFilePath); - referenceSwaggerSpec.swaggerFilePath = Path.GetFullPath(referenceSwaggerFilePath); - AddDefinitionsToCache(referenceSwaggerSpec, referenceSwaggerFilePath, schemaCache); - param = schemaCache.GetParameterFromCache(referencePath, swaggerFilePath, ref resolvedFromPath); - } - else - { - var referenceSwaggerSpec = await SwaggerDeserializer.Deserialize(referencePath); - referenceSwaggerSpec.swaggerFilePath = Path.GetFullPath(referencePath); - AddDefinitionsToCache(referenceSwaggerSpec, referencePath, schemaCache); - param = schemaCache.GetParameterFromCache(referencePath, swaggerFilePath, ref resolvedFromPath); - } + if (!Path.IsPathFullyQualified(referencePath)) + { + referenceSwaggerFilePath = Utils.GetReferencedSwaggerFile(referencePath, referenceSwaggerFilePath); + var referenceSwaggerSpec = await SwaggerDeserializer.Deserialize(referenceSwaggerFilePath); + AddDefinitionsToCache(referenceSwaggerSpec, referenceSwaggerFilePath, schemaCache); + param = schemaCache.GetParameterFromCache(referencePath, referenceSwaggerFilePath, false); + } + else + { + var referenceSwaggerSpec = await SwaggerDeserializer.Deserialize(referencePath); + AddDefinitionsToCache(referenceSwaggerSpec, referencePath, schemaCache); + param = schemaCache.GetParameterFromCache(referencePath, referencePath, false); + } - if (param != null && param.IsRefObject()) - referencePath = param.@ref; + if (param != null && param.IsRefObject()) + referencePath = param.@ref; + } + while (param != null && param.IsRefObject()); } - while(param != null && param.IsRefObject()); } var swaggerApiViewOperationParameter = new SwaggerApiViewParameter @@ -122,7 +120,7 @@ public static async Task GenerateSwaggerApiView(Swagger swag @in = param.@in, description = param.description, required = param.required, - schema = schemaCache.GetResolvedSchema(param.schema, resolvedFromPath), + schema = schemaCache.GetResolvedSchema(param.schema, referenceSwaggerFilePath), format = param.format, @ref = param.@ref, type = param.type @@ -149,18 +147,69 @@ public static async Task GenerateSwaggerApiView(Swagger swag foreach (var (statusCode, response) in operation.responses) { - var schema = response.schema; - var currentSwaggerFilePath = swaggerFilePath; + var resp = response; + var referenceSwaggerFilePath = swaggerFilePath; + + if (response.IsRefObject()) + { + resp = (Response)swaggerSpec.ResolveRefObj(response.@ref); + if (resp == null) + { + resp = (Response)schemaCache.GetResponseFromCache(response.@ref, swaggerFilePath); + } + + if (resp == null) + { + var referencePath = response.@ref; + do + { + if (!Path.IsPathFullyQualified(referencePath)) + { + referenceSwaggerFilePath = Utils.GetReferencedSwaggerFile(referencePath, swaggerFilePath); + var referenceSwaggerSpec = await SwaggerDeserializer.Deserialize(referenceSwaggerFilePath); + AddDefinitionsToCache(referenceSwaggerSpec, referenceSwaggerFilePath, schemaCache); + resp = schemaCache.GetResponseFromCache(referencePath, referenceSwaggerFilePath, false); + } + else + { + var referenceSwaggerSpec = await SwaggerDeserializer.Deserialize(referencePath); + AddDefinitionsToCache(referenceSwaggerSpec, referencePath, schemaCache); + resp = schemaCache.GetResponseFromCache(referencePath, referencePath, false); + } + + if (resp != null && resp.IsRefObject()) + referencePath = resp.@ref; + } + while (resp != null && resp.IsRefObject()); + } + } + + var schema = resp.schema; //Resolve ref obj for response schema. - if (response.schema != null) + if (schema != null) { - // The initial refChain is the root level schema. - // There are some scenarios that the property of the root level schema is a ref to the root level itself (circular reference). - // Like "errorDetail" schema in common types. - LinkedList refChain = new LinkedList(); - schema = schemaCache.GetResolvedSchema(schema, currentSwaggerFilePath, refChain); + if (schema.IsRefObject()) + { + var referencePath = schema.@ref; + do + { + referenceSwaggerFilePath = Utils.GetReferencedSwaggerFile(referencePath, referenceSwaggerFilePath); + var referenceSwaggerSpec = await SwaggerDeserializer.Deserialize(referenceSwaggerFilePath); + AddDefinitionsToCache(referenceSwaggerSpec, referenceSwaggerFilePath, schemaCache); + schema = schemaCache.GetSchemaFromCache(referencePath, referenceSwaggerFilePath, false); + } + while (schema != null && schema.IsRefObject()); + } + else + { + LinkedList refChain = new LinkedList(); + // The initial refChain is the root level schema. + // There are some scenarios that the property of the root level schema is a ref to the root level itself (circular reference). + // Like "errorDetail" schema in common types. + schema = schemaCache.GetResolvedSchema(schema, referenceSwaggerFilePath, refChain); + } } var headers = response.headers ?? new Dictionary(); @@ -229,6 +278,17 @@ public static void AddDefinitionsToCache(Swagger swaggerSpec, string swaggerFile } } } + + if (swaggerSpec.responses != null) + { + foreach (var response in swaggerSpec.responses) + { + if (!schemaCache.ResponsesCache.ContainsKey(response.Key)) + { + schemaCache.AddResponse(fullPath, response.Key, response.Value); + } + } + } } } } diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewOperation.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewOperation.cs index e8738fc27c78..cc99553e3354 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewOperation.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewOperation.cs @@ -81,22 +81,6 @@ public CodeFileToken[] TokenSerialize(SerializeContext context) ret.Add(TokenSerializer.NewLine()); } - //if (this.xMsLongRunningOperation) - //{ - // ret.Add(TokenSerializer.NavigableToken("x-ms-long-running-operation", CodeFileTokenKind.Keyword, context.IteratorPath.CurrentNextPath("x-ms-long-running-operation"))); - // ret.Add(TokenSerializer.Colon()); - // ret.Add(new CodeFileToken("true", CodeFileTokenKind.Literal)); - // ret.Add(TokenSerializer.NewLine()); - //} - // - //if (this.xMSPageable != null) - //{ - // ret.Add(TokenSerializer.NavigableToken("x-ms-pageable", CodeFileTokenKind.Keyword, context.IteratorPath.CurrentNextPath("x-ms-pageable"))); - // ret.Add(TokenSerializer.Colon()); - // ret.Add(new CodeFileToken(this.xMSPageable.nextLinkName, CodeFileTokenKind.Literal)); - // ret.Add(TokenSerializer.NewLine()); - //} - // new line for `Parameters` section. ret.Add(TokenSerializer.NewLine());