diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/CHANGELOG b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/CHANGELOG index 58da3f423e3..593880783dc 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/CHANGELOG +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/CHANGELOG @@ -1,5 +1,11 @@ # Release History +# 1.0.6 (2-2-2023) + ++ Remove APIView and APIViewWeb dependency ++ Fix don't resolve response issue ++ Add null check for array item + # 1.0.5 (1-18-2023) + Fix root schema circular reference issue by adding root schema ref in refChain. @@ -10,6 +16,7 @@ + To keep APIView revision consistent. Json serialization use alwaysMultiline option. + Remove unused code and comments. + ## 1.0.3 (11-17-2022) + Remove trailing colon from API path diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/Program.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/Program.cs index c78919ae200..3a038add1e3 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/Program.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/Program.cs @@ -5,115 +5,114 @@ using System.CommandLine; using System.Linq; -namespace SwaggerApiParser +namespace SwaggerApiParser; + +internal class Program { - internal class Program + static Task Main(string[] args) { - static Task Main(string[] args) - { - var swaggers = new Option>("--swaggers", - "The input swagger file. Can be a single file or multiple files separated by space."); + var swaggers = new Option>("--swaggers", + "The input swagger file. Can be a single file or multiple files separated by space."); - var output = new Option(name: "--output", description: "The output file path.", - getDefaultValue: () => "swagger.json"); + var output = new Option(name: "--output", description: "The output file path.", + getDefaultValue: () => "swagger.json"); - var readmeFile = new Option(name: "--readme", "The input readme file."); + var readmeFile = new Option(name: "--readme", "The input readme file."); - var readmeTag = new Option(name: "--tag", description: "Readme tag used to generate swagger apiView", - getDefaultValue: () => "default"); + var readmeTag = new Option(name: "--tag", description: "Readme tag used to generate swagger apiView", + getDefaultValue: () => "default"); - var packageName = new Option(name: "--package-name", - description: "The package name for the generated code file.", - getDefaultValue: () => "swagger"); + var packageName = new Option(name: "--package-name", + description: "The package name for the generated code file.", + getDefaultValue: () => "swagger"); - var swaggerLinks = new Option>(name: "--swaggerLinks", - description: "The input swagger links. Can be a single URL or multiple URLs separated by space.", getDefaultValue: - () => new List()) {AllowMultipleArgumentsPerToken = true}; + var swaggerLinks = new Option>(name: "--swaggerLinks", + description: "The input swagger links. Can be a single URL or multiple URLs separated by space.", getDefaultValue: + () => new List()) {AllowMultipleArgumentsPerToken = true}; - var cmd = new RootCommand - { - swaggers, - output, - packageName, - swaggerLinks, - readmeFile, - readmeTag - }; + var cmd = new RootCommand + { + swaggers, + output, + packageName, + swaggerLinks, + readmeFile, + readmeTag + }; - cmd.Description = "Parse swagger file into codefile."; + cmd.Description = "Parse swagger file into codefile."; - cmd.SetHandler(async (IEnumerable swaggerFiles, string outputFile, string package, IEnumerable links, string readme, string tag) => - { - var swaggerLinksArray = links.ToList(); + cmd.SetHandler(async (IEnumerable swaggerFiles, string outputFile, string package, IEnumerable links, string readme, string tag) => + { + var swaggerLinksArray = links.ToList(); - var enumerable = swaggerFiles as string[] ?? swaggerFiles.ToArray(); + var enumerable = swaggerFiles as string[] ?? swaggerFiles.ToArray(); - if (readme != null) - { - readme = Path.GetFullPath(readme); - } - await HandleGenerateCodeFile(enumerable, outputFile, package, swaggerLinksArray, readme, tag); - }, swaggers, output, packageName, swaggerLinks, readmeFile, readmeTag); + if (readme != null) + { + readme = Path.GetFullPath(readme); + } + await HandleGenerateCodeFile(enumerable, outputFile, package, swaggerLinksArray, readme, tag); + }, swaggers, output, packageName, swaggerLinks, readmeFile, readmeTag); - return Task.FromResult(cmd.Invoke(args)); - } + return Task.FromResult(cmd.Invoke(args)); + } - static async Task HandleGenerateCodeFile(IEnumerable swaggers, string output, string packageName, List swaggerLinks, string readmeFile, string readmeTag) - { + static async Task HandleGenerateCodeFile(IEnumerable swaggers, string output, string packageName, List swaggerLinks, string readmeFile, string readmeTag) + { - var swaggerFilePaths = swaggers.ToList(); - if (readmeFile != null) - { - var readmeFileDir = Path.GetDirectoryName(readmeFile); - var swaggerFiles = ReadmeParser.GetSwaggerFilesFromReadme(readmeFile, readmeTag); - swaggerFilePaths = swaggerFilePaths.Concat(swaggerFiles.Select(it => Path.Join(readmeFileDir, it))).ToList(); - } + var swaggerFilePaths = swaggers.ToList(); + if (readmeFile != null) + { + var readmeFileDir = Path.GetDirectoryName(readmeFile); + var swaggerFiles = ReadmeParser.GetSwaggerFilesFromReadme(readmeFile, readmeTag); + swaggerFilePaths = swaggerFilePaths.Concat(swaggerFiles.Select(it => Path.Join(readmeFileDir, it))).ToList(); + } - if (!swaggerFilePaths.Any()) - { - Console.WriteLine("No swagger files specified. For usage, run with --help"); - return; - } + if (!swaggerFilePaths.Any()) + { + Console.WriteLine("No swagger files specified. For usage, run with --help"); + return; + } - SwaggerApiViewRoot root = new SwaggerApiViewRoot(packageName, packageName); + SwaggerApiViewRoot root = new SwaggerApiViewRoot(packageName, packageName); - List swaggerSpecs = new List(); + List swaggerSpecs = new List(); - var idx = 0; - foreach (var swaggerFilePath in swaggerFilePaths) + var idx = 0; + foreach (var swaggerFilePath in swaggerFilePaths) + { + if (!File.Exists(swaggerFilePath)) { - if (!File.Exists(swaggerFilePath)) - { - throw new FileNotFoundException(swaggerFilePath); - } - - var swaggerLink = idx < swaggerLinks.Count ? swaggerLinks[idx] : ""; - idx++; - - var input = Path.GetFullPath(swaggerFilePath); - Console.WriteLine("Generating codefile for swagger file: {0}", Path.GetFileName(input)); - var swaggerSpec = await SwaggerDeserializer.Deserialize(input); - swaggerSpec.swaggerFilePath = Path.GetFullPath(swaggerFilePath); - swaggerSpec.swaggerLink = swaggerLink; - swaggerSpecs.Add(swaggerSpec); - root.AddDefinitionToCache(swaggerSpec, swaggerSpec.swaggerFilePath); - + throw new FileNotFoundException(swaggerFilePath); } - foreach (var swaggerSpec in swaggerSpecs) - { - root.AddSwaggerSpec(swaggerSpec, swaggerSpec.swaggerFilePath, packageName, swaggerSpec.swaggerLink); - } + var swaggerLink = idx < swaggerLinks.Count ? swaggerLinks[idx] : ""; + idx++; - var codeFile = root.GenerateCodeFile(); - var outputFilePath = Path.GetFullPath(output); - await using FileStream writer = File.Open(outputFilePath, FileMode.Create); - Console.WriteLine($"Generate codefile {output} successfully."); - await codeFile.SerializeAsync(writer); + var input = Path.GetFullPath(swaggerFilePath); + Console.WriteLine("Generating codefile for swagger file: {0}", Path.GetFileName(input)); + var swaggerSpec = await SwaggerDeserializer.Deserialize(input); + swaggerSpec.swaggerFilePath = Path.GetFullPath(swaggerFilePath); + swaggerSpec.swaggerLink = swaggerLink; + swaggerSpecs.Add(swaggerSpec); + root.AddDefinitionToCache(swaggerSpec, swaggerSpec.swaggerFilePath); + } + + foreach (var swaggerSpec in swaggerSpecs) + { + root.AddSwaggerSpec(swaggerSpec, swaggerSpec.swaggerFilePath, packageName, swaggerSpec.swaggerLink); + } + + var codeFile = root.GenerateCodeFile(); + var outputFilePath = Path.GetFullPath(output); + await using FileStream writer = File.Open(outputFilePath, FileMode.Create); + Console.WriteLine($"Generate codefile {output} successfully."); + await codeFile.SerializeAsync(writer); } -} +} \ No newline at end of file diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerAPIViewGenerator.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerAPIViewGenerator.cs index 53d6f8341ee..e8c9392a6f8 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerAPIViewGenerator.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerAPIViewGenerator.cs @@ -2,201 +2,195 @@ using System.Collections.Generic; using System.IO; -namespace SwaggerApiParser +namespace SwaggerApiParser; + +public class SwaggerApiViewGenerator { - public class SwaggerApiViewGenerator + public static SwaggerApiViewSpec GenerateSwaggerApiView(SwaggerSpec swaggerSpec, string swaggerFilePath, SchemaCache schemaCache, string packageName = "", string swaggerLink = "") { - public static SwaggerApiViewSpec GenerateSwaggerApiView(SwaggerSpec swaggerSpec, string swaggerFilePath, SchemaCache schemaCache, string packageName = "", string swaggerLink = "") + SwaggerApiViewSpec ret = new SwaggerApiViewSpec { - SwaggerApiViewSpec ret = new SwaggerApiViewSpec + SwaggerApiViewGeneral = { - SwaggerApiViewGeneral = - { - info = swaggerSpec.info, - swagger = swaggerSpec.swagger, - host = swaggerSpec.host, - schemes = swaggerSpec.schemes, - consumes = swaggerSpec.consumes, - produces = swaggerSpec.produces, - security = swaggerSpec.security, - securityDefinitions = swaggerSpec.securityDefinitions, - xMsParameterizedHost = swaggerSpec.xMsParameterizedHost, - swaggerLink = swaggerLink - }, - fileName = Path.GetFileName(swaggerFilePath), - packageName = packageName - }; - - AddDefinitionsToCache(swaggerSpec, swaggerFilePath, schemaCache); - ret.SwaggerApiViewGeneral.xMsParameterizedHost?.ResolveParameters(schemaCache, swaggerFilePath); + info = swaggerSpec.info, + swagger = swaggerSpec.swagger, + host = swaggerSpec.host, + schemes = swaggerSpec.schemes, + consumes = swaggerSpec.consumes, + produces = swaggerSpec.produces, + security = swaggerSpec.security, + securityDefinitions = swaggerSpec.securityDefinitions, + xMsParameterizedHost = swaggerSpec.xMsParameterizedHost, + swaggerLink = swaggerLink + }, + fileName = Path.GetFileName(swaggerFilePath), + packageName = packageName + }; + + AddDefinitionsToCache(swaggerSpec, swaggerFilePath, schemaCache); + ret.SwaggerApiViewGeneral.xMsParameterizedHost?.ResolveParameters(schemaCache, swaggerFilePath); - // If swagger doesn't have any path, it's common definition swagger. - if (swaggerSpec.paths.Count == 0) - { - return null; - } + // If swagger doesn't have any path, it's common definition swagger. + if (swaggerSpec.paths.Count == 0) + { + return null; + } - foreach (var (currentPath, operations) in swaggerSpec.paths) + foreach (var (currentPath, operations) in swaggerSpec.paths) + { + if (operations == null) { - if (operations == null) - { - continue; - } + continue; + } - foreach (var (key, value) in operations.operations) + foreach (var (key, value) in operations.operations) + { + SwaggerApiViewOperation op = new SwaggerApiViewOperation { - SwaggerApiViewOperation op = new SwaggerApiViewOperation - { - operation = value, - method = key, - path = currentPath, - operationId = value.operationId, - description = value.description, - summary = value.summary, - tags = value.tags, - procudes = value.produces, - consumes = value.consumes, - xMSPageable = value.xMsPageable, - operationIdPrefix = Utils.GetOperationIdPrefix(value.operationId), - operationIdAction = Utils.GetOperationIdAction(value.operationId), - PathParameters = new SwaggerApiViewOperationParameters("PathParameters"), - QueryParameters = new SwaggerApiViewOperationParameters("QueryParameters"), - BodyParameters = new SwaggerApiViewOperationParameters("BodyParameters"), - Responses = new List(), - xMsLongRunningOperation = value.xMsLongRunningOperaion - }; + operation = value, + method = key, + path = currentPath, + operationId = value.operationId, + description = value.description, + summary = value.summary, + tags = value.tags, + procudes = value.produces, + consumes = value.consumes, + xMSPageable = value.xMsPageable, + operationIdPrefix = Utils.GetOperationIdPrefix(value.operationId), + operationIdAction = Utils.GetOperationIdAction(value.operationId), + PathParameters = new SwaggerApiViewOperationParameters("PathParameters"), + QueryParameters = new SwaggerApiViewOperationParameters("QueryParameters"), + BodyParameters = new SwaggerApiViewOperationParameters("BodyParameters"), + Responses = new List(), + xMsLongRunningOperation = value.xMsLongRunningOperaion + }; - if (value.parameters != null) - foreach (var parameter in value.parameters) + if (value.parameters != null) + foreach (var parameter in value.parameters) + { + var param = parameter; + if (parameter.IsRefObject()) { - var param = parameter; - if (parameter.IsRefObject()) - { - param = (Parameter)swaggerSpec.ResolveRefObj(parameter.Ref) ?? schemaCache.GetParameterFromCache(parameter.Ref, swaggerFilePath); - } - - var currentSwaggerFilePath = swaggerFilePath; - - if (param == null) - { - continue; - } - - var swaggerApiViewOperationParameter = new SwaggerApiViewParameter - { - description = param.description, - name = param.name, - required = param.required, - format = param.format, - In = param.In, - schema = schemaCache.GetResolvedSchema(param.schema, currentSwaggerFilePath), - Ref = param.Ref, - type = param.type - }; - - - switch (param.In) - { - case "path": - op.PathParameters.Add(swaggerApiViewOperationParameter); - break; - case "query": - op.QueryParameters.Add(swaggerApiViewOperationParameter); - break; - case "body": - op.BodyParameters.Add(swaggerApiViewOperationParameter); - break; - } + param = (Parameter)swaggerSpec.ResolveRefObj(parameter.Ref) ?? schemaCache.GetParameterFromCache(parameter.Ref, swaggerFilePath); } - { - } - - - foreach (var (statusCode, response) in value.responses) - { - var schema = response.schema; var currentSwaggerFilePath = swaggerFilePath; - //Resolve ref obj for response schema. - - if (response.schema != null) + if (param == 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(); - if (schema.Ref != null) - { - refChain.AddFirst(schema.Ref); - } + continue; + } - schema = schemaCache.GetResolvedSchema(schema, currentSwaggerFilePath, refChain); + var swaggerApiViewOperationParameter = new SwaggerApiViewParameter + { + description = param.description, + name = param.name, + required = param.required, + format = param.format, + In = param.In, + schema = schemaCache.GetResolvedSchema(param.schema, currentSwaggerFilePath), + Ref = param.Ref, + type = param.type + }; + + + switch (param.In) + { + case "path": + op.PathParameters.Add(swaggerApiViewOperationParameter); + break; + case "query": + op.QueryParameters.Add(swaggerApiViewOperationParameter); + break; + case "body": + op.BodyParameters.Add(swaggerApiViewOperationParameter); + break; } + } - op.Responses.Add(new SwaggerApiViewResponse() {description = response.description, statusCode = statusCode, schema = schema}); + { + } + + + foreach (var (statusCode, response) in value.responses) + { + var schema = response.schema; + var currentSwaggerFilePath = swaggerFilePath; + + //Resolve ref obj for response schema. + + if (response.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); } - ret.Paths.AddSwaggerApiViewOperation(op); + op.Responses.Add(new SwaggerApiViewResponse() {description = response.description, statusCode = statusCode, schema = schema}); } + + ret.Paths.AddSwaggerApiViewOperation(op); } + } - if (swaggerSpec.definitions != null) + if (swaggerSpec.definitions != null) + { + foreach (var definition in swaggerSpec.definitions) { - foreach (var definition in swaggerSpec.definitions) - { - ret.SwaggerApiViewDefinitions.Add(definition.Key, definition.Value); - } + ret.SwaggerApiViewDefinitions.Add(definition.Key, definition.Value); } + } - if (swaggerSpec.parameters != null) + if (swaggerSpec.parameters != null) + { + foreach (var kv in swaggerSpec.parameters) { - foreach (var kv in swaggerSpec.parameters) + var param = kv.Value; + var swaggerApiViewParameter = new SwaggerApiViewParameter { - var param = kv.Value; - var swaggerApiViewParameter = new SwaggerApiViewParameter - { - description = param.description, - name = param.name, - required = param.required, - format = param.format, - In = param.In, - schema = schemaCache.GetResolvedSchema(param.schema, swaggerFilePath), - Ref = param.Ref, - type = param.type - }; - ret.SwaggerApiViewGlobalParameters.Add(kv.Key, swaggerApiViewParameter); - } + description = param.description, + name = param.name, + required = param.required, + format = param.format, + In = param.In, + schema = schemaCache.GetResolvedSchema(param.schema, swaggerFilePath), + Ref = param.Ref, + type = param.type + }; + ret.SwaggerApiViewGlobalParameters.Add(kv.Key, swaggerApiViewParameter); } - - ret.Paths.SortByMethod(); - return ret; } - public static void AddDefinitionsToCache(SwaggerSpec swaggerSpec, string swaggerFilePath, SchemaCache schemaCache) + ret.Paths.SortByMethod(); + return ret; + } + + public static void AddDefinitionsToCache(SwaggerSpec swaggerSpec, string swaggerFilePath, SchemaCache schemaCache) + { + if (swaggerSpec.definitions != null) { - if (swaggerSpec.definitions != null) + foreach (var definition in swaggerSpec.definitions) { - foreach (var definition in swaggerSpec.definitions) + if (!schemaCache.Cache.ContainsKey(definition.Key)) { - if (!schemaCache.Cache.ContainsKey(definition.Key)) - { - schemaCache.AddSchema(swaggerFilePath, definition.Key, definition.Value); - } + schemaCache.AddSchema(swaggerFilePath, definition.Key, definition.Value); } } + } - if (swaggerSpec.parameters != null) + if (swaggerSpec.parameters != null) + { + foreach (var parameter in swaggerSpec.parameters) { - foreach (var parameter in swaggerSpec.parameters) + if (!schemaCache.ParametersCache.ContainsKey(parameter.Key)) { - if (!schemaCache.ParametersCache.ContainsKey(parameter.Key)) - { - schemaCache.AddParameter(swaggerFilePath, parameter.Key, parameter.Value); - } + schemaCache.AddParameter(swaggerFilePath, parameter.Key, parameter.Value); } } } diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiParser.csproj b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiParser.csproj index 3e8b9c05c4f..9781b8b27f9 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiParser.csproj +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiParser.csproj @@ -6,7 +6,7 @@ swaggerAPIParser net6.0 swagger_api_parser - 1.0.5 + 1.0.6 Azure.Sdk.Tools.SwaggerApiParser True $(OfficialBuildId) @@ -19,10 +19,6 @@ $(NoWarn);NU5105;NU5119 - - - - diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/CodeFile.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/CodeFile.cs new file mode 100644 index 00000000000..32467f28985 --- /dev/null +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/CodeFile.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Threading.Tasks; + +namespace SwaggerApiParser; + +public class CodeFile +{ + private static readonly JsonSerializerOptions JsonSerializerOptions = new JsonSerializerOptions() {AllowTrailingCommas = true, ReadCommentHandling = JsonCommentHandling.Skip}; + + private static HashSet _collapsibleLanguages = new HashSet(new string[] {"Swagger"}); + + + public string VersionString { get; set; } + + public string Name { get; set; } + + public string Language { get; set; } + + public string LanguageVariant { get; set; } + + public string PackageName { get; set; } + + public string ServiceName { get; set; } + + public string PackageDisplayName { get; set; } + + public CodeFileToken[] Tokens { get; set; } = Array.Empty(); + + public List LeafSections { get; set; } + + public NavigationItem[] Navigation { get; set; } + + public static bool IsCollapsibleSectionSSupported(string language) => _collapsibleLanguages.Contains(language); + + public static async Task DeserializeAsync(Stream stream, bool hasSections = false) + { + var codeFile = await JsonSerializer.DeserializeAsync( + stream, + JsonSerializerOptions); + + if (hasSections == false && codeFile.LeafSections == null && IsCollapsibleSectionSSupported(codeFile.Language)) + hasSections = true; + + // Spliting out the 'leafSections' of the codeFile is done so as not to have to render large codeFiles at once + // Rendering sections in part helps to improve page load time + if (hasSections) + { + var index = 0; + var tokens = codeFile.Tokens; + var newTokens = new List(); + var leafSections = new List(); + var section = new List(); + var isLeaf = false; + var numberOfLinesinLeafSection = 0; + + while (index < tokens.Length) + { + var token = tokens[index]; + if (token.Kind == CodeFileTokenKind.FoldableSectionHeading) + { + section.Add(token); + isLeaf = false; + } + else if (token.Kind == CodeFileTokenKind.FoldableSectionContentStart) + { + section.Add(token); + newTokens.AddRange(section); + section.Clear(); + isLeaf = true; + numberOfLinesinLeafSection = 0; + } + else if (token.Kind == CodeFileTokenKind.FoldableSectionContentEnd) + { + if (isLeaf) + { + leafSections.Add(section.ToArray()); + section.Clear(); + isLeaf = false; + + // leafSectionPlaceholder will be used to identify the appriopriate index and number of lines in the leafSections + // numberOfLinesinLeafSection help keep line numbering consistent with the main 'non-leaf' sections + var leafSectionPlaceholder = new CodeFileToken( + $"{(leafSections.Count() - 1)}", CodeFileTokenKind.LeafSectionPlaceholder, numberOfLinesinLeafSection); + var newLineToken = new CodeFileToken("", CodeFileTokenKind.Newline); + section.Add(leafSectionPlaceholder); + section.Add(newLineToken); + } + + section.Add(token); + } + else + { + if (isLeaf && token.Kind == CodeFileTokenKind.Newline) + { + numberOfLinesinLeafSection++; + } + + section.Add(token); + } + + index++; + } + + newTokens.AddRange(section); + codeFile.Tokens = newTokens.ToArray(); + codeFile.LeafSections = leafSections; + } + + return codeFile; + } + + public async Task SerializeAsync(Stream stream) + { + await JsonSerializer.SerializeAsync( + stream, + this, + JsonSerializerOptions); + } +} diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/CodeFileToken.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/CodeFileToken.cs new file mode 100644 index 00000000000..8fcc1fea982 --- /dev/null +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/CodeFileToken.cs @@ -0,0 +1,66 @@ +namespace SwaggerApiParser; + +public enum CodeFileTokenKind +{ + Text = 0, + Newline = 1, + Whitespace = 2, + Punctuation = 3, + Keyword = 4, + LineIdMarker = 5, + TypeName = 6, + MemberName = 7, + StringLiteral = 8, + Literal = 9, + Comment = 10, + DocumentRangeStart = 11, + DocumentRangeEnd = 12, + DeprecatedRangeStart = 13, + DeprecatedRangeEnd = 14, + SkipDiffRangeStart = 15, + SkipDiffRangeEnd = 16, + FoldableSectionHeading = 17, + FoldableSectionContentStart = 18, + FoldableSectionContentEnd = 19, + TableBegin = 20, + TableEnd = 21, + TableRowCount = 22, + TableColumnCount = 23, + TableColumnName = 24, + TableCellBegin = 25, + TableCellEnd = 26, + LeafSectionPlaceholder = 27, + ExternalLinkStart = 28, + ExternalLinkEnd = 29, + HiddenApiRangeStart = 30, + HiddenApiRangeEnd = 31 +} +public struct CodeFileToken +{ + public CodeFileToken(string value, CodeFileTokenKind kind, int? numberOfLinesinLeafSection = null) + { + Value = value; + NavigateToId = null; + Kind = kind; + DefinitionId = null; + CrossLanguageDefId = null; + NumberOfLinesinLeafSection = numberOfLinesinLeafSection; + } + + public string DefinitionId { get; set; } + + public string NavigateToId { get; set; } + + public string Value { get; set; } + + public CodeFileTokenKind Kind { get; set; } + + public string CrossLanguageDefId { get; set; } + + public int? NumberOfLinesinLeafSection { get; set; } + + public override string ToString() + { + return $"{Value} ({Kind})"; + } +} \ No newline at end of file diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/INavigable.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/INavigable.cs index f38cf2ecb0c..a8c9c889e10 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/INavigable.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/INavigable.cs @@ -1,12 +1,24 @@ using System; using System.Collections.Generic; -using APIView; -namespace SwaggerApiParser +namespace SwaggerApiParser; + +public class NavigationItem { - public interface INavigable - { - public NavigationItem BuildNavigationItem(IteratorPath iteratorPath = null); - } + public string Text { get; set; } + + public string NavigationId { get; set; } + + public NavigationItem[] ChildItems { get; set; } = Array.Empty(); + + public Dictionary Tags { get; set; } = new Dictionary(0); + + public bool IsHiddenApi { get; set; } + + public override string ToString() => Text; } +public interface INavigable +{ + public NavigationItem BuildNavigationItem(IteratorPath iteratorPath = null); +} \ No newline at end of file diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/ITokenSerializable.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/ITokenSerializable.cs index 299f409b341..a1e71aaa9c7 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/ITokenSerializable.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/ITokenSerializable.cs @@ -1,8 +1,6 @@ -using APIView; - namespace SwaggerApiParser; public interface ITokenSerializable { public CodeFileToken[] TokenSerialize(SerializeContext context); -} +} \ No newline at end of file diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewDefinitions.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewDefinitions.cs index cf1608e62ff..0d738dd959c 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewDefinitions.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewDefinitions.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using APIView; namespace SwaggerApiParser; diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewGeneral.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewGeneral.cs index a6d9507f57a..2b066a3cdd1 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewGeneral.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewGeneral.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using APIView; namespace SwaggerApiParser; diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewGlobalParamerters.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewGlobalParamerters.cs index f0e5d713ad3..f0387b3ecf6 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewGlobalParamerters.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewGlobalParamerters.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using APIView; namespace SwaggerApiParser; 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 f5cd570b443..eff3fecf567 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewOperation.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewOperation.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using APIView; namespace SwaggerApiParser; diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewParameter.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewParameter.cs index 6e35ff4c8f0..6584cce1f4f 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewParameter.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewParameter.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using APIView; -using Microsoft.VisualBasic; namespace SwaggerApiParser; diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewPaths.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewPaths.cs index 660bbe9842a..1cb4b738c85 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewPaths.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewPaths.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using APIView; namespace SwaggerApiParser; diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewResponse.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewResponse.cs index 19f5b0dc9c1..66e6d6438f8 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewResponse.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewResponse.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Generic; -using APIView; namespace SwaggerApiParser; diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewRoot.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewRoot.cs index 53cc403abc0..ecbe1be1517 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewRoot.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewRoot.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using ApiView; -using APIView; namespace SwaggerApiParser; diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewSpec.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewSpec.cs index 3bfe20d81c0..fb07032c028 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewSpec.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerApiView/SwaggerApiViewSpec.cs @@ -1,6 +1,4 @@ using System.Collections.Generic; -using ApiView; -using APIView; namespace SwaggerApiParser; diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerCodeFileMerger.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerCodeFileMerger.cs index f862e7140ff..a9ebc89dbc9 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerCodeFileMerger.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerCodeFileMerger.cs @@ -3,8 +3,6 @@ using System.IO; using System.Linq; using System.Threading.Tasks; -using ApiView; -using APIView; namespace SwaggerApiParser; diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerDeserializer.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerDeserializer.cs index 702cb59f11c..e3fde39170c 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerDeserializer.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerDeserializer.cs @@ -2,16 +2,15 @@ using System.Text.Json; using System.Threading.Tasks; -namespace SwaggerApiParser +namespace SwaggerApiParser; + +public static class SwaggerDeserializer { - public static class SwaggerDeserializer + public static async Task Deserialize(string swaggerFilePath) { - public static async Task Deserialize(string swaggerFilePath) - { - var fullPath = Path.GetFullPath(swaggerFilePath); - await using FileStream openStream = File.OpenRead(swaggerFilePath); - SwaggerSpec swaggerSpec = await JsonSerializer.DeserializeAsync(openStream); - return swaggerSpec; - } + var fullPath = Path.GetFullPath(swaggerFilePath); + await using FileStream openStream = File.OpenRead(swaggerFilePath); + SwaggerSpec swaggerSpec = await JsonSerializer.DeserializeAsync(openStream); + return swaggerSpec; } -} +} \ No newline at end of file diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/ApiPath.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/ApiPath.cs index 0a0d93b7671..9221de5672a 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/ApiPath.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/ApiPath.cs @@ -44,4 +44,4 @@ public Dictionary operations return ret; } } -} +} \ No newline at end of file diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Definition.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Definition.cs index fef5a49a38f..6d1d92d82e0 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Definition.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Definition.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using APIView; namespace SwaggerApiParser; @@ -14,4 +13,4 @@ public CodeFileToken[] TokenSerializeDefinition(SerializeContext context) } -} +} \ No newline at end of file diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Extension.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Extension.cs index 05e7fb26b77..8007c63f662 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Extension.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Extension.cs @@ -1,6 +1,5 @@ namespace SwaggerApiParser; - public class XMSEnum{ public string name { get; set; } public bool modelAsString { get; set; } @@ -9,4 +8,4 @@ public string ToKeywords() { return $"x-ms-enum: name: {name}, modelAsString: {modelAsString}"; } -} +} \ No newline at end of file diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/ExternalDocs.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/ExternalDocs.cs index 85d0e7ca0cf..c2727d090d9 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/ExternalDocs.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/ExternalDocs.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using APIView; namespace SwaggerApiParser; @@ -23,4 +22,4 @@ public CodeFileToken[] TokenSerialize(SerializeContext context) return ret.ToArray(); } -} +} \ No newline at end of file diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Info.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Info.cs index a0d75c88910..6daa1b6b89c 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Info.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Info.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using APIView; namespace SwaggerApiParser; @@ -38,4 +37,4 @@ public CodeFileToken[] TokenSerialize(SerializeContext context) ret.Add(TokenSerializer.FoldableContentEnd()); return ret.ToArray(); } -} +} \ No newline at end of file diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Operation.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Operation.cs index cb48727cc9a..3ffe8c43503 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Operation.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Operation.cs @@ -34,5 +34,4 @@ public class XMsPageable public string itemName { get; set; } public string nextLinkName { get; set; } public string operationName { get; set; } -} - +} \ No newline at end of file diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Parameter.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Parameter.cs index 6f568c594d7..3e97133fc60 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Parameter.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Parameter.cs @@ -1,5 +1,4 @@ using System.Text.Json.Serialization; -using APIView; namespace SwaggerApiParser; diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Response.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Response.cs index 5fbf20bab97..79721172bf7 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Response.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Response.cs @@ -10,4 +10,4 @@ public class Response public BaseSchema schema { get; set; } [JsonExtensionData] public IDictionary examples { get; set; } -} +} \ No newline at end of file diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/SchemaCache.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/SchemaCache.cs index 3ceec89241d..c8cac72cf87 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/SchemaCache.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/SchemaCache.cs @@ -69,10 +69,10 @@ public static string GetRefKey(string Ref) var key = Ref.Split("/").Last(); return key; } - + public static string RemoveCrossFileReferenceFromRef(string Ref) { - var idx = Ref.IndexOf("#", StringComparison.Ordinal); + var idx = Ref.IndexOf("#", StringComparison.Ordinal); var key = Ref[idx..]; return key; } @@ -172,9 +172,17 @@ public BaseSchema GetResolvedSchema(BaseSchema root, string currentSwaggerFilePa { return resolvedSchema; } - + + // If refChain already has resolve refKey. Circular reference. return root. + if (refChain.Contains(GetResolvedCacheRefKey(root.Ref, currentSwaggerFilePath))) + { + root.originalRef = root.Ref; + root.Ref = null; + return root; + } + // get from original schema cache. - refChain.AddLast(root.Ref); + refChain.AddLast(GetResolvedCacheRefKey(root.Ref, currentSwaggerFilePath)); var schema = this.GetSchemaFromCache(root.Ref, currentSwaggerFilePath); var ret = this.GetResolvedSchema(schema, GetReferencedSwaggerFile(root.Ref, currentSwaggerFilePath), refChain); // write back resolved cache @@ -185,6 +193,7 @@ public BaseSchema GetResolvedSchema(BaseSchema root, string currentSwaggerFilePa { return null; } + ret.originalRef = root.Ref; return ret; } @@ -220,6 +229,7 @@ public BaseSchema GetResolvedSchema(BaseSchema root, string currentSwaggerFilePa { continue; } + if (!refChain.Contains(rootProperty.Value.Ref) && !refChain.Contains(rootProperty.Value.Ref) && !refChain.Contains(rootProperty.Value.items?.Ref)) { root.properties[rootProperty.Key] = this.GetResolvedSchema(rootProperty.Value, currentSwaggerFilePath, refChain); diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Security.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Security.cs index 39c718e6fd0..f7645ec20a6 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Security.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/Security.cs @@ -1,10 +1,8 @@ -using System; using System.Collections.Generic; using System.Text.Json; namespace SwaggerApiParser; - public class SecurityDefinitions : Dictionary { } @@ -12,4 +10,4 @@ public class SecurityDefinitions : Dictionary public class Security : List>> { -} +} \ No newline at end of file diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/SwaggerSpec.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/SwaggerSpec.cs index e4eb19c16a4..55ca7ca43ee 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/SwaggerSpec.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/SwaggerSpec.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Text.Json.Serialization; diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/SwaggerTypes.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/SwaggerTypes.cs index 5ac3ab29565..9b56420241e 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/SwaggerTypes.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/SwaggerTypes.cs @@ -3,346 +3,353 @@ using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; -using APIView; -namespace SwaggerApiParser +namespace SwaggerApiParser; + +public class Header : BaseSchema { - public class Header : BaseSchema - { - } +} - public class SchemaTableItem - { - public String Model { get; set; } - public String Field { get; set; } - public String TypeFormat { get; set; } +public class SchemaTableItem +{ + public String Model { get; set; } + public String Field { get; set; } + public String TypeFormat { get; set; } - public String Keywords { get; set; } - public String Description { get; set; } + public String Keywords { get; set; } + public String Description { get; set; } - public CodeFileToken[] TokenSerialize() - { - List ret = new List(); - string[] serializedFields = new[] {"Model", "Field", "TypeFormat", "Keywords", "Description"}; - ret.AddRange(this.TokenSerializeWithOptions(serializedFields)); - return ret.ToArray(); - } + public CodeFileToken[] TokenSerialize() + { + List ret = new List(); + string[] serializedFields = new[] {"Model", "Field", "TypeFormat", "Keywords", "Description"}; + ret.AddRange(this.TokenSerializeWithOptions(serializedFields)); + return ret.ToArray(); + } - public CodeFileToken[] TokenSerializeWithOptions(string[] serializedFields) + public CodeFileToken[] TokenSerializeWithOptions(string[] serializedFields) + { + List ret = new List(); + foreach (var property in this.GetType().GetProperties()) { - List ret = new List(); - foreach (var property in this.GetType().GetProperties()) + if (serializedFields.Contains(property.Name)) { - if (serializedFields.Contains(property.Name)) - { - ret.AddRange(TokenSerializer.TableCell(new[] {new CodeFileToken(property.GetValue(this, null)?.ToString(), CodeFileTokenKind.Literal)})); - } + ret.AddRange(TokenSerializer.TableCell(new[] {new CodeFileToken(property.GetValue(this, null)?.ToString(), CodeFileTokenKind.Literal)})); } - - return ret.ToArray(); } + + return ret.ToArray(); } +} - public class BaseSchema : ITokenSerializable - { - public string type { get; set; } - public string description { get; set; } - public string format { get; set; } - public string originalRef { get; set; } +public class BaseSchema : ITokenSerializable +{ + public string type { get; set; } + public string description { get; set; } + public string format { get; set; } + public string originalRef { get; set; } - public List allOf { get; set; } - public List anyOf { get; set; } + public List allOf { get; set; } + public List anyOf { get; set; } - public List oneOf { get; set; } - public BaseSchema items { get; set; } + public List oneOf { get; set; } + public BaseSchema items { get; set; } - // public Boolean additionalProperties { get; set; } - public bool readOnly { get; set; } + // public Boolean additionalProperties { get; set; } + public bool readOnly { get; set; } - public bool writeOnly { get; set; } + public bool writeOnly { get; set; } - public string discriminator { get; set; } + public string discriminator { get; set; } - [JsonPropertyName("x-ms-nullable")] public bool xMsNullable { get; set; } + [JsonPropertyName("x-ms-nullable")] public bool xMsNullable { get; set; } - [JsonPropertyName("x-ms-enum")] public XMSEnum xmsEnum { get; set; } + [JsonPropertyName("x-ms-enum")] public XMSEnum xmsEnum { get; set; } - public Dictionary properties { get; set; } - public Dictionary allOfProperities { get; set; } + public Dictionary properties { get; set; } + public Dictionary allOfProperities { get; set; } - [JsonPropertyName("x-ms-discriminator-value")] - public string xMsDiscriminatorValue { get; set; } + [JsonPropertyName("x-ms-discriminator-value")] + public string xMsDiscriminatorValue { get; set; } - public List required { get; set; } + public List required { get; set; } - [JsonPropertyName("enum")] public List Enum { get; set; } + [JsonPropertyName("enum")] public List Enum { get; set; } - public bool IsPropertyRequired(string propertyName) - { - return this.required != null && this.required.Contains(propertyName); - } + public bool IsPropertyRequired(string propertyName) + { + return this.required != null && this.required.Contains(propertyName); + } - [JsonPropertyName("$ref")] public string Ref { get; set; } + [JsonPropertyName("$ref")] public string Ref { get; set; } - private List tableItems; + private List tableItems; - public bool IsRefObj() + public bool IsRefObj() + { + return this.Ref != null; + } + + public List GetKeywords() + { + List keywords = new List(); + if (this.readOnly) { - return this.Ref != null; + keywords.Add("readOnly"); } - public List GetKeywords() + if (this.writeOnly) { - List keywords = new List(); - if (this.readOnly) - { - keywords.Add("readOnly"); - } + keywords.Add("writeOnly"); + } - if (this.writeOnly) - { - keywords.Add("writeOnly"); - } + if (this.xMsNullable) + { + keywords.Add("x-ms-nullable"); + } - if (this.xMsNullable) - { - keywords.Add("x-ms-nullable"); - } + if (this.Enum != null && this.Enum.Count > 0) + { + keywords.Add($"enum: [{string.Join(",", this.Enum)}]"); + } - if (this.Enum != null && this.Enum.Count > 0) - { - keywords.Add($"enum: [{string.Join(",", this.Enum)}]"); - } + if (this.xmsEnum != null) + { + keywords.Add(this.xmsEnum.ToKeywords()); + } - if (this.xmsEnum != null) - { - keywords.Add(this.xmsEnum.ToKeywords()); - } + return keywords; + } + + public string GetTypeFormat() + { + var typeFormat = this.format != null ? $"/{this.format}" : ""; - return keywords; + + if (this.type is "array") + { + var reference = this.items.originalRef ?? this.items.Ref; + var arrayType = Utils.GetDefinitionType(reference) ?? this.items.type; + return this.type + $"<{arrayType}>"; } - public string GetTypeFormat() + if (this.originalRef != null) { - var typeFormat = this.format != null ? $"/{this.format}" : ""; + return Utils.GetDefinitionType(this.originalRef) + typeFormat; + } + return this.type + typeFormat; + } - if (this.type is "array") - { - var reference = this.items.originalRef ?? this.items.Ref; - var arrayType = Utils.GetDefinitionType(reference) ?? this.items.type; - return this.type + $"<{arrayType}>"; - } + private CodeFileToken[] TokenSerializeInternal(SerializeContext context, BaseSchema schema, ref List flattenedTableItems, Boolean serializeRef = true) + { + List ret = new List(); + if (serializeRef) + { + ret.Add(new CodeFileToken(Utils.GetDefinitionType(schema.originalRef), CodeFileTokenKind.TypeName)); + flattenedTableItems.Add(new SchemaTableItem() {Model = Utils.GetDefinitionType(schema.originalRef), TypeFormat = schema.type, Description = schema.description}); + ret.Add(TokenSerializer.NewLine()); + context.intent++; + } - if (this.originalRef != null) - { - return Utils.GetDefinitionType(this.originalRef) + typeFormat; - } - return this.type + typeFormat; + if (schema.properties?.Count != 0) + { + TokenSerializeProperties(context, schema, schema.properties, ret, ref flattenedTableItems, serializeRef); } - private CodeFileToken[] TokenSerializeInternal(SerializeContext context, BaseSchema schema, ref List flattenedTableItems, Boolean serializeRef = true) + if (schema.allOfProperities?.Count != 0 && schema.allOf != null) { - List ret = new List(); - if (serializeRef) + ret.Add(new CodeFileToken("allOf", CodeFileTokenKind.Keyword)); + ret.Add(TokenSerializer.Colon()); + ret.Add(TokenSerializer.NewLine()); + foreach (var allOfSchema in schema.allOf) { - ret.Add(new CodeFileToken(Utils.GetDefinitionType(schema.originalRef), CodeFileTokenKind.TypeName)); - flattenedTableItems.Add(new SchemaTableItem() {Model = Utils.GetDefinitionType(schema.originalRef), TypeFormat = schema.type, Description = schema.description}); - ret.Add(TokenSerializer.NewLine()); - context.intent++; + if (allOfSchema != null) + { + ret.Add(new CodeFileToken(Utils.GetDefinitionType(allOfSchema.Ref), CodeFileTokenKind.TypeName)); + ret.Add(TokenSerializer.NewLine()); + } } + TokenSerializeProperties(new SerializeContext(context.intent + 2, context.IteratorPath), schema, schema.allOfProperities, ret, ref flattenedTableItems, serializeRef); + } - if (schema.properties?.Count != 0) - { - TokenSerializeProperties(context, schema, schema.properties, ret, ref flattenedTableItems, serializeRef); - } + if (schema.type == "array") + { + SchemaTableItem arrayItem = new SchemaTableItem {Description = schema.description}; + var arrayType = schema.items.type != null ? $"array<{schema.items.type}>" : $"array<{Utils.GetDefinitionType(schema.items.originalRef)}>"; + arrayItem.TypeFormat = arrayType; + flattenedTableItems.Add(arrayItem); + TokenSerializeArray(context, ret, schema, ref flattenedTableItems, serializeRef); + } - if (schema.allOfProperities?.Count != 0 && schema.allOf != null) + if (schema.type == "string") + { + if (schema.Enum != null) { - ret.Add(new CodeFileToken("allOf", CodeFileTokenKind.Keyword)); - ret.Add(TokenSerializer.Colon()); - ret.Add(TokenSerializer.NewLine()); - foreach (var allOfSchema in schema.allOf) + SchemaTableItem enumItem = new SchemaTableItem {Description = schema.description}; + const string enumType = "enum"; + enumItem.TypeFormat = enumType; + if (schema.xmsEnum != null) { - if (allOfSchema != null) - { - ret.Add(new CodeFileToken(Utils.GetDefinitionType(allOfSchema.Ref), CodeFileTokenKind.TypeName)); - ret.Add(TokenSerializer.NewLine()); - } + enumItem.Keywords = string.Join(",", schema.GetKeywords()); } - TokenSerializeProperties(new SerializeContext(context.intent + 2, context.IteratorPath), schema, schema.allOfProperities, ret, ref flattenedTableItems, serializeRef); - } - - if (schema.type == "array") - { - SchemaTableItem arrayItem = new SchemaTableItem {Description = schema.description}; - var arrayType = schema.items.type != null ? $"array<{schema.items.type}>" : $"array<{Utils.GetDefinitionType(schema.items.originalRef)}>"; - arrayItem.TypeFormat = arrayType; - flattenedTableItems.Add(arrayItem); - TokenSerializeArray(context, ret, schema, ref flattenedTableItems, serializeRef); + flattenedTableItems.Add(enumItem); } + } - if (schema.type == "string") - { - if (schema.Enum != null) - { - SchemaTableItem enumItem = new SchemaTableItem {Description = schema.description}; - const string enumType = "enum"; - enumItem.TypeFormat = enumType; - if (schema.xmsEnum != null) - { - enumItem.Keywords = string.Join(",", schema.GetKeywords()); - } - - flattenedTableItems.Add(enumItem); - } - } + return ret.ToArray(); + } - return ret.ToArray(); + private static List GetPropertyKeywordsFromBaseSchema(BaseSchema baseSchema, string propertyName, BaseSchema schema) + { + var keywords = new HashSet(); + if (baseSchema.IsPropertyRequired(propertyName)) + { + keywords.Add("required"); } - private static List GetPropertyKeywordsFromBaseSchema(BaseSchema baseSchema, string propertyName, BaseSchema schema) + foreach (var it in schema.GetKeywords()) { - var keywords = new HashSet(); - if (baseSchema.IsPropertyRequired(propertyName)) - { - keywords.Add("required"); - } + keywords.Add(it); + } - foreach (var it in schema.GetKeywords()) - { - keywords.Add(it); - } + return keywords.ToList(); + } - return keywords.ToList(); + private static void TokenSerializeProperties(SerializeContext context, BaseSchema schema, Dictionary properties, List ret, ref List flattenedTableItems, + Boolean serializeRef = true) + { + if (properties == null) + { + return; } - private static void TokenSerializeProperties(SerializeContext context, BaseSchema schema, Dictionary properties, List ret, ref List flattenedTableItems, - Boolean serializeRef = true) + foreach (var kv in properties) { - if (properties == null) + ret.Add(new CodeFileToken(kv.Key, CodeFileTokenKind.Literal)); + ret.Add(TokenSerializer.Colon()); + if (kv.Value == null) { - return; + continue; } - foreach (var kv in properties) + // Normal case: If properties is has values. Serialize each key value pair in properties. + if ((kv.Value.properties != null && kv.Value.properties?.Count != 0)) { - ret.Add(new CodeFileToken(kv.Key, CodeFileTokenKind.Literal)); - ret.Add(TokenSerializer.Colon()); - if (kv.Value == null) - { - continue; - } - - // Normal case: If properties is has values. Serialize each key value pair in properties. - if ((kv.Value.properties != null && kv.Value.properties?.Count != 0)) - { - var keywords = GetPropertyKeywordsFromBaseSchema(schema, kv.Key, kv.Value); - SchemaTableItem item = new SchemaTableItem {Field = kv.Key, Description = kv.Value.description, Keywords = String.Join(",", keywords), TypeFormat = kv.Value.GetTypeFormat()}; - flattenedTableItems.Add(item); - ret.Add(TokenSerializer.NewLine()); - if (serializeRef) - { - ret.AddRange(schema.TokenSerializeInternal(new SerializeContext(context.intent + 1, context.IteratorPath), kv.Value, ref flattenedTableItems, serializeRef)); - } - } - // Circular reference case: the ref won't be expanded. - else if (kv.Value.Ref != null) - { - ret.Add(TokenSerializer.NewLine()); - ret.Add(new CodeFileToken("<", CodeFileTokenKind.Punctuation)); - var refName = kv.Value.Ref; - ret.Add(new CodeFileToken(refName.Split("/").Last(), CodeFileTokenKind.TypeName)); - ret.Add(new CodeFileToken(">", CodeFileTokenKind.Punctuation)); - } - // Array case: Serialize array. - else if (kv.Value.type == "array") - { - SchemaTableItem arrayItem = new SchemaTableItem(); - arrayItem.Field = kv.Key; - arrayItem.Description = kv.Value.description; - var arrayType = (kv.Value.items.originalRef == null && kv.Value.items.Ref == null) - ? $"array<{kv.Value.items.type}>" - : $"array<{Utils.GetDefinitionType(kv.Value.items.originalRef ?? Utils.GetDefinitionType(kv.Value.items.Ref))}>"; - arrayItem.TypeFormat = arrayType; - var keywords = GetPropertyKeywordsFromBaseSchema(schema, kv.Key, kv.Value); - arrayItem.Keywords = string.Join(",", keywords); - flattenedTableItems.Add(arrayItem); - TokenSerializeArray(context, ret, kv.Value, ref flattenedTableItems, serializeRef); - } - else + var keywords = GetPropertyKeywordsFromBaseSchema(schema, kv.Key, kv.Value); + SchemaTableItem item = new SchemaTableItem {Field = kv.Key, Description = kv.Value.description, Keywords = String.Join(",", keywords), TypeFormat = kv.Value.GetTypeFormat()}; + flattenedTableItems.Add(item); + ret.Add(TokenSerializer.NewLine()); + if (serializeRef) { - var keywords = GetPropertyKeywordsFromBaseSchema(schema, kv.Key, kv.Value); - SchemaTableItem item = new SchemaTableItem {Field = kv.Key, Description = kv.Value.description, TypeFormat = kv.Value.GetTypeFormat(), Keywords = string.Join(",", keywords)}; - flattenedTableItems.Add(item); - ret.Add(new CodeFileToken(kv.Value.type, CodeFileTokenKind.Keyword)); - ret.Add(TokenSerializer.NewLine()); + ret.AddRange(schema.TokenSerializeInternal(new SerializeContext(context.intent + 1, context.IteratorPath), kv.Value, ref flattenedTableItems, serializeRef)); } } - } - - private static void TokenSerializeArray(SerializeContext context, List ret, BaseSchema arraySchema, ref List flattenedTableItems, Boolean serializeRef) - { - ret.Add(new CodeFileToken("array", CodeFileTokenKind.Keyword)); - - if (arraySchema.items.type != null && arraySchema.items.type != "object") + // Circular reference case: the ref won't be expanded. + else if (kv.Value.Ref != null) { - ret.Add(new CodeFileToken("<", CodeFileTokenKind.Punctuation)); - ret.Add(new CodeFileToken(arraySchema.items.type, CodeFileTokenKind.TypeName)); - ret.Add(new CodeFileToken(">", CodeFileTokenKind.Punctuation)); ret.Add(TokenSerializer.NewLine()); - } - else - { ret.Add(new CodeFileToken("<", CodeFileTokenKind.Punctuation)); - var refName = arraySchema.items.originalRef ?? arraySchema.items.Ref ?? ""; + var refName = kv.Value.Ref; ret.Add(new CodeFileToken(refName.Split("/").Last(), CodeFileTokenKind.TypeName)); ret.Add(new CodeFileToken(">", CodeFileTokenKind.Punctuation)); - ret.Add(TokenSerializer.NewLine()); - - // circular reference - if (arraySchema.items.Ref != null) + } + // Array case: Serialize array. + else if (kv.Value.type == "array") + { + SchemaTableItem arrayItem = new SchemaTableItem(); + arrayItem.Field = kv.Key; + arrayItem.Description = kv.Value.description; + var arrayType = "array"; + if (kv.Value.items != null) { - return; + arrayType = (kv.Value.items.originalRef == null && kv.Value.items.Ref == null) + ? $"array<{kv.Value.items.type}>" + : $"array<{Utils.GetDefinitionType(kv.Value.items.originalRef ?? Utils.GetDefinitionType(kv.Value.items.Ref))}>"; } - if (serializeRef) - { - ret.AddRange(arraySchema.items.TokenSerializeInternal(new SerializeContext(context.intent + 1, context.IteratorPath), arraySchema.items, ref flattenedTableItems, serializeRef)); - } + arrayItem.TypeFormat = arrayType; + var keywords = GetPropertyKeywordsFromBaseSchema(schema, kv.Key, kv.Value); + arrayItem.Keywords = string.Join(",", keywords); + flattenedTableItems.Add(arrayItem); + TokenSerializeArray(context, ret, kv.Value, ref flattenedTableItems, serializeRef); } + else + { + var keywords = GetPropertyKeywordsFromBaseSchema(schema, kv.Key, kv.Value); + SchemaTableItem item = new SchemaTableItem {Field = kv.Key, Description = kv.Value.description, TypeFormat = kv.Value.GetTypeFormat(), Keywords = string.Join(",", keywords)}; + flattenedTableItems.Add(item); + ret.Add(new CodeFileToken(kv.Value.type, CodeFileTokenKind.Keyword)); + ret.Add(TokenSerializer.NewLine()); + } + } + } + + private static void TokenSerializeArray(SerializeContext context, List ret, BaseSchema arraySchema, ref List flattenedTableItems, Boolean serializeRef) + { + ret.Add(new CodeFileToken("array", CodeFileTokenKind.Keyword)); + if (arraySchema.items == null) + { + return; } - public void TokenSerializePropertyIntoTableItems(SerializeContext context, ref List retTableItems, Boolean serializeRef = true, string[] columns = null) + if (arraySchema.items.type != null && arraySchema.items.type != "object") + { + ret.Add(new CodeFileToken("<", CodeFileTokenKind.Punctuation)); + ret.Add(new CodeFileToken(arraySchema.items.type, CodeFileTokenKind.TypeName)); + ret.Add(new CodeFileToken(">", CodeFileTokenKind.Punctuation)); + ret.Add(TokenSerializer.NewLine()); + } + else { - if (retTableItems == null) + ret.Add(new CodeFileToken("<", CodeFileTokenKind.Punctuation)); + var refName = arraySchema.items.originalRef ?? arraySchema.items.Ref ?? ""; + ret.Add(new CodeFileToken(refName.Split("/").Last(), CodeFileTokenKind.TypeName)); + ret.Add(new CodeFileToken(">", CodeFileTokenKind.Punctuation)); + ret.Add(TokenSerializer.NewLine()); + + // circular reference + if (arraySchema.items.Ref != null) + { + return; + } + + if (serializeRef) { - retTableItems = new List(); - this.TokenSerializeInternal(context, this, ref retTableItems, serializeRef); + ret.AddRange(arraySchema.items.TokenSerializeInternal(new SerializeContext(context.intent + 1, context.IteratorPath), arraySchema.items, ref flattenedTableItems, serializeRef)); } } + } - public CodeFileToken[] TokenSerialize(SerializeContext context) + public void TokenSerializePropertyIntoTableItems(SerializeContext context, ref List retTableItems, Boolean serializeRef = true, string[] columns = null) + { + if (retTableItems == null) { - string[] columns = new[] {"Model", "Field", "Type/Format", "Keywords", "Description"}; - this.TokenSerializePropertyIntoTableItems(context, ref this.tableItems); - var tableRet = new List(); + retTableItems = new List(); + this.TokenSerializeInternal(context, this, ref retTableItems, serializeRef); + } + } - var tableRows = new List(); - foreach (var tableItem in this.tableItems) - { - tableRows.AddRange(tableItem.TokenSerialize()); - } + public CodeFileToken[] TokenSerialize(SerializeContext context) + { + string[] columns = new[] {"Model", "Field", "Type/Format", "Keywords", "Description"}; + this.TokenSerializePropertyIntoTableItems(context, ref this.tableItems); + var tableRet = new List(); - tableRet.AddRange(TokenSerializer.TokenSerializeAsTableFormat(this.tableItems.Count, columns.Length, columns, tableRows.ToArray(), context.IteratorPath.CurrentNextPath("table"))); - tableRet.Add(TokenSerializer.NewLine()); - return tableRet.ToArray(); + var tableRows = new List(); + foreach (var tableItem in this.tableItems) + { + tableRows.AddRange(tableItem.TokenSerialize()); } + + tableRet.AddRange(TokenSerializer.TokenSerializeAsTableFormat(this.tableItems.Count, columns.Length, columns, tableRows.ToArray(), context.IteratorPath.CurrentNextPath("table"))); + tableRet.Add(TokenSerializer.NewLine()); + return tableRet.ToArray(); } } diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/XMsParameterizedHost.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/XMsParameterizedHost.cs index b89be36561d..7b37c40fb9a 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/XMsParameterizedHost.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerSpec/XMsParameterizedHost.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using APIView; namespace SwaggerApiParser; diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerTokenSerializer.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerTokenSerializer.cs index 574b1fb5d70..a048befe0a5 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerTokenSerializer.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/SwaggerTokenSerializer.cs @@ -6,884 +6,881 @@ using System.Linq; using System.Text.Json; using System.Threading.Tasks; -using ApiView; -using APIView; -namespace SwaggerApiParser +namespace SwaggerApiParser; + +public class SwaggerTokenSerializer { - public class SwaggerTokenSerializer - { - internal const string LanguageServiceName = "Swagger"; - public string Name => LanguageServiceName; + internal const string LanguageServiceName = "Swagger"; + public string Name => LanguageServiceName; - // This is an unfortunate hack because JsonLanguageService is already - // squatting on `.json`. We'll have to fix this before we ask anyone - // to use ApiView for swagger files. - public string Extension => ".swagger"; + // This is an unfortunate hack because JsonLanguageService is already + // squatting on `.json`. We'll have to fix this before we ask anyone + // to use ApiView for swagger files. + public string Extension => ".swagger"; - // I don't really know what this is doing, but the other language - // services do the same. It'd probably be worth making this the default - // implementation if everyone needs to copy it as-is. - public bool CanUpdate(string versionString) => false; + // I don't really know what this is doing, but the other language + // services do the same. It'd probably be worth making this the default + // implementation if everyone needs to copy it as-is. + public bool CanUpdate(string versionString) => false; - public async Task GetCodeFileInternalAsync(string originalName, Stream stream, bool runAnalysis) => - SwaggerVisitor.GenerateCodeListing(originalName, await JsonDocument.ParseAsync(stream)); + public async Task GetCodeFileInternalAsync(string originalName, Stream stream, bool runAnalysis) => + SwaggerVisitor.GenerateCodeListing(originalName, await JsonDocument.ParseAsync(stream)); #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously - public async Task GetCodeFileFromJsonDocumentAsync(string originalName, JsonDocument jsonDoc, bool runAnalysis) => - SwaggerVisitor.GenerateCodeListing(originalName, jsonDoc); + public async Task GetCodeFileFromJsonDocumentAsync(string originalName, JsonDocument jsonDoc, bool runAnalysis) => + SwaggerVisitor.GenerateCodeListing(originalName, jsonDoc); #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously + /// + /// Generate an ApiView listing for an OpenAPI 2.0 specification in + /// JSON. + /// + /// The name of the swagger file. + /// Stream full of JSON. + /// This is unused. + /// An ApiView listing. + public async Task GetCodeFileAsync(string originalName, Stream stream, bool runAnalysis) => + await CodeFile.DeserializeAsync(stream); + + /// + /// Incredibly simple class to make fenceposting (i.e., performing an + /// operation between every element in a sequence but not before or + /// after) easy and consistent. + /// + internal class Fenceposter + { /// - /// Generate an ApiView listing for an OpenAPI 2.0 specification in - /// JSON. + /// Flag indicating whether a separator is required. It starts + /// false, but will be permanently flipped to true after the first + /// time the RequiresSeperator property is accessed. /// - /// The name of the swagger file. - /// Stream full of JSON. - /// This is unused. - /// An ApiView listing. - public async Task GetCodeFileAsync(string originalName, Stream stream, bool runAnalysis) => - await CodeFile.DeserializeAsync(stream); + private bool _requiresSeparator = false; /// - /// Incredibly simple class to make fenceposting (i.e., performing an - /// operation between every element in a sequence but not before or - /// after) easy and consistent. + /// Gets a value indicating whether a separator is required. This + /// will always be false the first time it is called after a new + /// Fenceposter is constructed and true every time after. /// - internal class Fenceposter + public bool RequiresSeparator { - /// - /// Flag indicating whether a separator is required. It starts - /// false, but will be permanently flipped to true after the first - /// time the RequiresSeperator property is accessed. - /// - private bool _requiresSeparator = false; - - /// - /// Gets a value indicating whether a separator is required. This - /// will always be false the first time it is called after a new - /// Fenceposter is constructed and true every time after. - /// - public bool RequiresSeparator + get { - get + if (_requiresSeparator) { - if (_requiresSeparator) - { - return true; - } - else - { - _requiresSeparator = true; - return false; - } + return true; + } + else + { + _requiresSeparator = true; + return false; } } } + } + /// + /// IndentWriter provides helpful features for writing blocks of indented + /// text (like source code, JSON, etc.). + /// + public partial class IndentWriter + { /// - /// IndentWriter provides helpful features for writing blocks of indented - /// text (like source code, JSON, etc.). + /// The buffer where tokens are written. It is obtained by the + /// user via IndentWriter.ToTokens(). /// - public partial class IndentWriter - { - /// - /// The buffer where tokens are written. It is obtained by the - /// user via IndentWriter.ToTokens(). - /// - private IList _tokens = new List(); + private IList _tokens = new List(); - /// - /// Whether or not the last character written was a newline - /// character (which means the next line written should - /// automatically add the current indent depth). - /// - private bool _isNewline = true; + /// + /// Whether or not the last character written was a newline + /// character (which means the next line written should + /// automatically add the current indent depth). + /// + private bool _isNewline = true; - /// - /// Gets or sets the text used as each indent character (i.e., - /// could be a single tab character or four space characters). The - /// default value is four space characters. - /// - public string IndentText { get; set; } = " "; + /// + /// Gets or sets the text used as each indent character (i.e., + /// could be a single tab character or four space characters). The + /// default value is four space characters. + /// + public string IndentText { get; set; } = " "; - /// - /// Gets the depth of the current indent level. - /// - public uint Indent { get; private set; } + /// + /// Gets the depth of the current indent level. + /// + public uint Indent { get; private set; } - /// - /// Gets the text that has been written thus far. - /// - /// The text written thus far. - public CodeFileToken[] ToTokens() => _tokens.ToArray(); + /// + /// Gets the text that has been written thus far. + /// + /// The text written thus far. + public CodeFileToken[] ToTokens() => _tokens.ToArray(); - private int GetLastTokenIndex(CodeFileTokenKind? kind = null) + private int GetLastTokenIndex(CodeFileTokenKind? kind = null) + { + CodeFileToken last; + for (int i = _tokens.Count - 1; i >= 0; i--) { - CodeFileToken last; - for (int i = _tokens.Count - 1; i >= 0; i--) - { - last = _tokens[i]; - if (kind == null || last.Kind == kind) { return i; } - } - - return -1; + last = _tokens[i]; + if (kind == null || last.Kind == kind) { return i; } } - public void AnnotateDefinition(string definitionId) - { - int index = GetLastTokenIndex(); - var last = _tokens[index]; - last.DefinitionId = definitionId; - _tokens[index] = last; - } + return -1; + } - public void AnnotateLink(string navigationId, CodeFileTokenKind kind) - { - int index = GetLastTokenIndex(kind); - var last = _tokens[index]; - last.NavigateToId = navigationId; - _tokens[index] = last; - } + public void AnnotateDefinition(string definitionId) + { + int index = GetLastTokenIndex(); + var last = _tokens[index]; + last.DefinitionId = definitionId; + _tokens[index] = last; + } - /// - /// Pushes the scope a level deeper. - /// - public void PushScope() => Indent++; + public void AnnotateLink(string navigationId, CodeFileTokenKind kind) + { + int index = GetLastTokenIndex(kind); + var last = _tokens[index]; + last.NavigateToId = navigationId; + _tokens[index] = last; + } - /// - /// Pops the scope a level. - /// - public void PopScope() - { - if (Indent == 0) - { - throw new InvalidOperationException("Cannot pop scope any further!"); - } + /// + /// Pushes the scope a level deeper. + /// + public void PushScope() => Indent++; - Indent--; + /// + /// Pops the scope a level. + /// + public void PopScope() + { + if (Indent == 0) + { + throw new InvalidOperationException("Cannot pop scope any further!"); } - private void Append(CodeFileTokenKind kind, string text) => - _tokens.Add(new CodeFileToken(text, kind)); - - /// - /// Writes an indent if needed. This is used before each write - /// operation to ensure we're always indenting. We don't need to - /// indent for a series of calls like Write("Foo"); Write("Bar"); - /// but would indent between a series like WriteLine("Foo"); - /// Write("Bar"); - /// - private void WriteIndentIfNeeded() - { - // If we had just written a full line - if (_isNewline) - { - _isNewline = false; + Indent--; + } - // Then we'll write out the current indent depth before anything - // else is written - Append( - CodeFileTokenKind.Whitespace, - string.Concat(Enumerable.Repeat(IndentText, (int)Indent))); - } - } + private void Append(CodeFileTokenKind kind, string text) => + _tokens.Add(new CodeFileToken(text, kind)); - /// - /// Write the text representation of the given values with - /// indentation as appropriate. - /// - /// Format string. - /// Optional arguments to the format string. - public void Write(CodeFileTokenKind kind, string format, params object[] args) + /// + /// Writes an indent if needed. This is used before each write + /// operation to ensure we're always indenting. We don't need to + /// indent for a series of calls like Write("Foo"); Write("Bar"); + /// but would indent between a series like WriteLine("Foo"); + /// Write("Bar"); + /// + private void WriteIndentIfNeeded() + { + // If we had just written a full line + if (_isNewline) { - WriteIndentIfNeeded(); - - // Only use AppendFormat if we have args so that we don't have - // to escape curly brace literals used on their own. - if (args?.Length > 0) - { - format = string.Format(format, args); - } + _isNewline = false; - Append(kind, format); + // Then we'll write out the current indent depth before anything + // else is written + Append( + CodeFileTokenKind.Whitespace, + string.Concat(Enumerable.Repeat(IndentText, (int)Indent))); } + } - /// - /// Write the text representation of the given values followed by a - /// newline, with indentation as appropriate. This will force the - /// next Write call to indent before anything else is written. - /// - /// Format string. - /// Optional arguments to the format string. - public void WriteLine(CodeFileTokenKind kind, string format, params object[] args) - { - Write(kind, format, args); - Write(CodeFileTokenKind.Newline, Environment.NewLine); + /// + /// Write the text representation of the given values with + /// indentation as appropriate. + /// + /// Format string. + /// Optional arguments to the format string. + public void Write(CodeFileTokenKind kind, string format, params object[] args) + { + WriteIndentIfNeeded(); - // Track that we just wrote a line so the next write operation - // will indent first - _isNewline = true; + // Only use AppendFormat if we have args so that we don't have + // to escape curly brace literals used on their own. + if (args?.Length > 0) + { + format = string.Format(format, args); } - /// - /// Write a newline (which will force the next write operation to - /// indent before anything else is written). - /// - public void WriteLine() => WriteLine(CodeFileTokenKind.Text, null); + Append(kind, format); + } - /// - /// Increase the indent level after writing the text representation - /// of the given values to the current line. This would be used - /// like: - /// myIndentWriter.PushScope("{"); - /// /* Write indented lines here */ - /// myIndentWriter.PopScope("}"); - /// - /// Format string. - /// Optional arguments to the format string. - public void PushScope(CodeFileTokenKind kind, string format, params object[] args) => - PushScope(newline: true, kind, format, args); + /// + /// Write the text representation of the given values followed by a + /// newline, with indentation as appropriate. This will force the + /// next Write call to indent before anything else is written. + /// + /// Format string. + /// Optional arguments to the format string. + public void WriteLine(CodeFileTokenKind kind, string format, params object[] args) + { + Write(kind, format, args); + Write(CodeFileTokenKind.Newline, Environment.NewLine); - public void PushScope(bool newline, CodeFileTokenKind kind, string format, params object[] args) - { - if (newline) - { - WriteLine(kind, format, args); - } - else - { - Write(kind, format, args); - } + // Track that we just wrote a line so the next write operation + // will indent first + _isNewline = true; + } - PushScope(); - } + /// + /// Write a newline (which will force the next write operation to + /// indent before anything else is written). + /// + public void WriteLine() => WriteLine(CodeFileTokenKind.Text, null); - /// - /// Decrease the indent level after writing the text representation - /// of the given values to the current line. This would be used - /// like: - /// myIndentWriter.PushScope("{"); - /// /* Write indented lines here */ - /// myIndentWriter.PopScope("}"); - /// - /// Format string. - /// Optional arguments to the format string. - public void PopScope(CodeFileTokenKind kind, string format, params object[] args) => - PopScope(newline: true, kind, format, args); + /// + /// Increase the indent level after writing the text representation + /// of the given values to the current line. This would be used + /// like: + /// myIndentWriter.PushScope("{"); + /// /* Write indented lines here */ + /// myIndentWriter.PopScope("}"); + /// + /// Format string. + /// Optional arguments to the format string. + public void PushScope(CodeFileTokenKind kind, string format, params object[] args) => + PushScope(newline: true, kind, format, args); - public void PopScope(bool newline, CodeFileTokenKind kind, string format, params object[] args) + public void PushScope(bool newline, CodeFileTokenKind kind, string format, params object[] args) + { + if (newline) + { + WriteLine(kind, format, args); + } + else { - PopScope(); - - // Force the format string to be written on a new line, but - // don't add an extra one if we just wrote a newline. - if (!_isNewline && newline) - { - WriteLine(); - } - Write(kind, format, args); } - /// - /// Create a writer scope that will indent until the scope is - /// disposed. This is used like: - /// using (writer.Scope()) - /// { - /// /* Write indented lines here */ - /// } - /// /* Back to normal here */ - /// - public IDisposable Scope() => new WriterScope(this); + PushScope(); + } + /// + /// Decrease the indent level after writing the text representation + /// of the given values to the current line. This would be used + /// like: + /// myIndentWriter.PushScope("{"); + /// /* Write indented lines here */ + /// myIndentWriter.PopScope("}"); + /// + /// Format string. + /// Optional arguments to the format string. + public void PopScope(CodeFileTokenKind kind, string format, params object[] args) => + PopScope(newline: true, kind, format, args); - /// - /// Create a writer scope that will indent until the scope is - /// disposed and starts/ends the scope with the given text. This - /// is used like: - /// using (writer.Scope("{", "}")) - /// { - /// /* Write indented lines here */ - /// } - /// /* Back to normal here */ - /// - /// Text starting the scope. - /// Text ending the scope. - /// The kind of token to use. - public IDisposable Scope(string start, string end, bool newline = true, CodeFileTokenKind kind = CodeFileTokenKind.Punctuation) => - new WriterScope( - this, - start ?? throw new ArgumentNullException("start"), - end ?? throw new ArgumentNullException("end"), - newline, - kind); + public void PopScope(bool newline, CodeFileTokenKind kind, string format, params object[] args) + { + PopScope(); - /// - /// The WriterScope class allows us to create an indentation block - /// via a C# using statement. It will typically be used via - /// something like: - /// using (writer.Scope("{", "}")) - /// { - /// /* Indented writing here */ - /// } - /// /* No longer indented from here on... */ - /// - private class WriterScope : IDisposable + // Force the format string to be written on a new line, but + // don't add an extra one if we just wrote a newline. + if (!_isNewline && newline) { - /// - /// The IndentWriter that contains this scope. - /// - private IndentWriter _writer; - - /// - /// An optional string to write upon closing the scope. - /// - private string _scopeEnd; - - /// - /// An optional value indicating whether to add newlines. - /// - private bool _newline; - - /// - /// Optional kind of token to write. - /// - private CodeFileTokenKind _kind; - - /// - /// Initializes a new instance of the WriterScope class. - /// - /// - /// The IndentWriter containing the scope. - /// - /// The kind of token to write. - public WriterScope(IndentWriter writer) - { - Debug.Assert(writer != null, "writer cannot be null!"); - _writer = writer; - _writer.PushScope(); - } + WriteLine(); + } - /// - /// Initializes a new instance of the WriterScope class. - /// - /// - /// The IndentWriter containing the scope. - /// - /// Text starting the scope. - /// Text ending the scope. - /// Whether to inject a newline. - /// The kind of token to write. - public WriterScope(IndentWriter writer, string scopeStart, string scopeEnd, bool newline, CodeFileTokenKind kind) - { - Debug.Assert(writer != null, "writer cannot be null!"); - Debug.Assert(scopeStart != null, "scopeStart cannot be null!"); - Debug.Assert(scopeEnd != null, "scopeEnd cannot be null!"); - - _writer = writer; - _writer.PushScope(newline, kind, scopeStart); - _scopeEnd = scopeEnd; - _kind = kind; - _newline = newline; - } + Write(kind, format, args); + } - /// - /// Close the scope. - /// - public void Dispose() - { - if (_writer != null) - { - // Close the scope with the desired text if given - if (_scopeEnd != null) - { - _writer.PopScope(_newline, _kind, _scopeEnd); - } - else - { - _writer.PopScope(); - } + /// + /// Create a writer scope that will indent until the scope is + /// disposed. This is used like: + /// using (writer.Scope()) + /// { + /// /* Write indented lines here */ + /// } + /// /* Back to normal here */ + /// + public IDisposable Scope() => new WriterScope(this); - // Prevent multiple disposals - _writer = null; - } - } - } - } /// - /// Represents the navigation tree for a swagger document. + /// Create a writer scope that will indent until the scope is + /// disposed and starts/ends the scope with the given text. This + /// is used like: + /// using (writer.Scope("{", "}")) + /// { + /// /* Write indented lines here */ + /// } + /// /* Back to normal here */ /// - internal class SwaggerTree + /// Text starting the scope. + /// Text ending the scope. + /// The kind of token to use. + public IDisposable Scope(string start, string end, bool newline = true, CodeFileTokenKind kind = CodeFileTokenKind.Punctuation) => + new WriterScope( + this, + start ?? throw new ArgumentNullException("start"), + end ?? throw new ArgumentNullException("end"), + newline, + kind); + + /// + /// The WriterScope class allows us to create an indentation block + /// via a C# using statement. It will typically be used via + /// something like: + /// using (writer.Scope("{", "}")) + /// { + /// /* Indented writing here */ + /// } + /// /* No longer indented from here on... */ + /// + private class WriterScope : IDisposable { /// - /// Gets or sets the display text of the tree. + /// The IndentWriter that contains this scope. /// - public string Text { get; set; } - - public string LongText { get; set; } + private IndentWriter _writer; /// - /// Gets or sets the ID of the document element to navigate to. + /// An optional string to write upon closing the scope. /// - public string NavigationId { get; set; } + private string _scopeEnd; /// - /// Gets or sets the children of the current node. + /// An optional value indicating whether to add newlines. /// - public IDictionary Children { get; } = new Dictionary(); + private bool _newline; /// - /// Gets a value indicating whether this node is the root. + /// Optional kind of token to write. /// - public bool IsRoot => Parent == null; + private CodeFileTokenKind _kind; /// - /// Gets a value indicating whether this node is a top-level entry. + /// Initializes a new instance of the WriterScope class. /// - public bool IsTopLevel => - Parent?.IsRoot == true && Text switch - { - "paths" => true, - "x-ms-paths" => true, - "parameters" => true, - "definitions" => true, - "responses" => true, - _ => false - }; + /// + /// The IndentWriter containing the scope. + /// + /// The kind of token to write. + public WriterScope(IndentWriter writer) + { + Debug.Assert(writer != null, "writer cannot be null!"); + _writer = writer; + _writer.PushScope(); + } /// - /// Gets the parent of the current node. + /// Initializes a new instance of the WriterScope class. /// - public SwaggerTree Parent { get; private set; } + /// + /// The IndentWriter containing the scope. + /// + /// Text starting the scope. + /// Text ending the scope. + /// Whether to inject a newline. + /// The kind of token to write. + public WriterScope(IndentWriter writer, string scopeStart, string scopeEnd, bool newline, CodeFileTokenKind kind) + { + Debug.Assert(writer != null, "writer cannot be null!"); + Debug.Assert(scopeStart != null, "scopeStart cannot be null!"); + Debug.Assert(scopeEnd != null, "scopeEnd cannot be null!"); + + _writer = writer; + _writer.PushScope(newline, kind, scopeStart); + _scopeEnd = scopeEnd; + _kind = kind; + _newline = newline; + } /// - /// Gets a value indicating whether this node is a path entry. + /// Close the scope. /// - public bool IsPath => - Parent?.Parent?.IsRoot == true && - (Parent.Text == "paths" || Parent.Text == "x-ms-paths"); + public void Dispose() + { + if (_writer != null) + { + // Close the scope with the desired text if given + if (_scopeEnd != null) + { + _writer.PopScope(_newline, _kind, _scopeEnd); + } + else + { + _writer.PopScope(); + } + + // Prevent multiple disposals + _writer = null; + } + } + } + } + + /// + /// Represents the navigation tree for a swagger document. + /// + internal class SwaggerTree + { + /// + /// Gets or sets the display text of the tree. + /// + public string Text { get; set; } - public bool IsResponses => Text switch + public string LongText { get; set; } + + /// + /// Gets or sets the ID of the document element to navigate to. + /// + public string NavigationId { get; set; } + + /// + /// Gets or sets the children of the current node. + /// + public IDictionary Children { get; } = new Dictionary(); + + /// + /// Gets a value indicating whether this node is the root. + /// + public bool IsRoot => Parent == null; + + /// + /// Gets a value indicating whether this node is a top-level entry. + /// + public bool IsTopLevel => + Parent?.IsRoot == true && Text switch { + "paths" => true, + "x-ms-paths" => true, + "parameters" => true, + "definitions" => true, "responses" => true, _ => false }; - /// - /// Gets a value indicating whether this node's children should be - /// added to the navigation tree. - /// - public bool HasNavigableChildren => IsRoot || IsTopLevel || IsPath || IsResponses; + /// + /// Gets the parent of the current node. + /// + public SwaggerTree Parent { get; private set; } - /// - /// Add a child to the navigation tree. - /// - /// Display name of the child. - /// Navigation ID of the child. - /// - /// The child node. - public SwaggerTree Add(string name, string navigationId, string longText = null) - { - if (!Children.TryGetValue(name, out SwaggerTree next)) - { - Children[name] = next = new SwaggerTree {Text = name, LongText = longText, NavigationId = navigationId, Parent = this}; - } + /// + /// Gets a value indicating whether this node is a path entry. + /// + public bool IsPath => + Parent?.Parent?.IsRoot == true && + (Parent.Text == "paths" || Parent.Text == "x-ms-paths"); - return next; - } + public bool IsResponses => Text switch + { + "responses" => true, + _ => false + }; - /// - /// Turn the swagger view into an ApiView navigation item. - /// - /// An ApiView navigation item. - private NavigationItem BuildItem() => - new() {Text = Text, NavigationId = NavigationId, ChildItems = Children.Values.Select(c => c.BuildItem()).ToArray()}; + /// + /// Gets a value indicating whether this node's children should be + /// added to the navigation tree. + /// + public bool HasNavigableChildren => IsRoot || IsTopLevel || IsPath || IsResponses; - /// - /// Turn the swagger view into ApiView navigation items. - /// - /// ApiView navigation items. - public NavigationItem[] Build() => - BuildItem().ChildItems; + /// + /// Add a child to the navigation tree. + /// + /// Display name of the child. + /// Navigation ID of the child. + /// + /// The child node. + public SwaggerTree Add(string name, string navigationId, string longText = null) + { + if (!Children.TryGetValue(name, out SwaggerTree next)) + { + Children[name] = next = new SwaggerTree {Text = name, LongText = longText, NavigationId = navigationId, Parent = this}; + } + + return next; } /// - /// Visitor to generate ApiView listings for Swagger documents. + /// Turn the swagger view into an ApiView navigation item. + /// + /// An ApiView navigation item. + private NavigationItem BuildItem() => + new() {Text = Text, NavigationId = NavigationId, ChildItems = Children.Values.Select(c => c.BuildItem()).ToArray()}; + + /// + /// Turn the swagger view into ApiView navigation items. /// - internal class SwaggerVisitor + /// ApiView navigation items. + public NavigationItem[] Build() => + BuildItem().ChildItems; + } + + /// + /// Visitor to generate ApiView listings for Swagger documents. + /// + internal class SwaggerVisitor + { + private IndentWriter _writer = new(); + private SwaggerTree _nav = new(); + private List _path = new() {"#"}; + + public SwaggerVisitor() { - private IndentWriter _writer = new(); - private SwaggerTree _nav = new(); - private List _path = new() {"#"}; + } - public SwaggerVisitor() + /// + /// Generate the ApiView code listing for a swagger document. + /// + /// The name of the file. + /// The swagger document. + /// An ApiView CodeFile. + public static CodeFile GenerateCodeListing(string originalName, JsonDocument document) + { + var navigationIdPrefix = $"{originalName}"; + // Process the document + SwaggerVisitor visitor = new(); + visitor.Visit(document.RootElement, visitor._nav, navigationIdPrefix); + + // Ensure we're looking at OpenAPI 2.0 + // if (GetString(document, "swagger") != "2.0") + // { + // throw new InvalidOperationException("Only Swagger 2.0 is supported."); + // } + + // Pull the pieces together into a listing + return new CodeFile() { - } + Language = LanguageServiceName, + Name = originalName, + PackageName = GetString(document, "info", "title") ?? originalName, + Tokens = visitor._writer.ToTokens(), + Navigation = visitor._nav.Build() + }; + } - /// - /// Generate the ApiView code listing for a swagger document. - /// - /// The name of the file. - /// The swagger document. - /// An ApiView CodeFile. - public static CodeFile GenerateCodeListing(string originalName, JsonDocument document) - { - var navigationIdPrefix = $"{originalName}"; - // Process the document - SwaggerVisitor visitor = new(); - visitor.Visit(document.RootElement, visitor._nav, navigationIdPrefix); - - // Ensure we're looking at OpenAPI 2.0 - // if (GetString(document, "swagger") != "2.0") - // { - // throw new InvalidOperationException("Only Swagger 2.0 is supported."); - // } - - // Pull the pieces together into a listing - return new CodeFile() - { - Language = LanguageServiceName, - Name = originalName, - PackageName = GetString(document, "info", "title") ?? originalName, - Tokens = visitor._writer.ToTokens(), - Navigation = visitor._nav.Build() - }; - } + public static CodeFileToken[] GenerateCodeFileTokens(JsonDocument document) + { + SwaggerVisitor visitor = new(); + visitor.Visit(document.RootElement, visitor._nav); + return visitor._writer.ToTokens(); + } - public static CodeFileToken[] GenerateCodeFileTokens(JsonDocument document) + /// + /// Generate the listing for a JSON value. + /// + /// The JSON value. + /// Optional document navigation info. + /// + /// + /// + private void Visit(JsonElement element, SwaggerTree nav = null, string navigationIdPrefix = "", string scopeStart = "{ ", string scopeEnd = " }") + { + switch (element.ValueKind) { - SwaggerVisitor visitor = new(); - visitor.Visit(document.RootElement, visitor._nav); - return visitor._writer.ToTokens(); + case JsonValueKind.Object: + VisitObject(element, nav, navigationIdPrefix, scopeStart, scopeEnd); + break; + case JsonValueKind.Array: + VisitArray(element, nav, navigationIdPrefix); + break; + case JsonValueKind.False: + case JsonValueKind.Null: + case JsonValueKind.Number: + case JsonValueKind.String: + case JsonValueKind.True: + case JsonValueKind.Undefined: + VisitLiteral(element); + break; + default: + break; } + } - /// - /// Generate the listing for a JSON value. - /// - /// The JSON value. - /// Optional document navigation info. - /// - /// - /// - private void Visit(JsonElement element, SwaggerTree nav = null, string navigationIdPrefix = "", string scopeStart = "{ ", string scopeEnd = " }") + /// + /// Generate the listing for an object. + /// + /// The JSON object. + /// Optional document navigation info. + /// + /// + /// + private void VisitObject(JsonElement obj, SwaggerTree nav, string navigationIdPrefix, string scopeStart = "{", string scopeEnd = "}") + { + bool multiLine = !FitsOnOneLine(obj); + + using (_writer.Scope(scopeStart, scopeEnd, multiLine)) { - switch (element.ValueKind) + // Optionally sort the values + IEnumerable values = obj.EnumerateObject(); + if (nav?.IsRoot != true) { - case JsonValueKind.Object: - VisitObject(element, nav, navigationIdPrefix, scopeStart, scopeEnd); - break; - case JsonValueKind.Array: - VisitArray(element, nav, navigationIdPrefix); - break; - case JsonValueKind.False: - case JsonValueKind.Null: - case JsonValueKind.Number: - case JsonValueKind.String: - case JsonValueKind.True: - case JsonValueKind.Undefined: - VisitLiteral(element); - break; - default: - break; + values = nav?.IsPath == true ? values.OrderBy(p => GetString(p.Value, "operationId") ?? p.Name, new OperationComparer()) : values.OrderBy(p => p.Name); } - } - /// - /// Generate the listing for an object. - /// - /// The JSON object. - /// Optional document navigation info. - /// - /// - /// - private void VisitObject(JsonElement obj, SwaggerTree nav, string navigationIdPrefix, string scopeStart = "{", string scopeEnd = "}") - { - bool multiLine = !FitsOnOneLine(obj); + Fenceposter fencepost = new(); - using (_writer.Scope(scopeStart, scopeEnd, multiLine)) + // Generate the listing for each property + foreach (JsonProperty property in values) { - // Optionally sort the values - IEnumerable values = obj.EnumerateObject(); - if (nav?.IsRoot != true) + Boolean IsCurObjCollapsible() { - values = nav?.IsPath == true ? values.OrderBy(p => GetString(p.Value, "operationId") ?? p.Name, new OperationComparer()) : values.OrderBy(p => p.Name); + bool isPathScope = nav is {Text: "paths" or "x-ms-paths"}; + bool isMethod = nav is {IsPath: true}; + bool isDefinition = nav is {Text: "definitions"}; + bool isMethodParameters = nav is {Parent: {IsPath: true}} && property.Name == "parameters"; + bool isXmsExamples = nav is {Parent: {IsPath: true}} && property.Name == "x-ms-examples"; + bool isResponses = nav is {Text: "responses"}; + bool isParameters = nav is {Text: "parameters"}; + bool isSecurityDefinitions = nav is {Text: "securityDefinitions"}; + return isPathScope || isDefinition || isParameters || isSecurityDefinitions; } - Fenceposter fencepost = new(); - - // Generate the listing for each property - foreach (JsonProperty property in values) - { - Boolean IsCurObjCollapsible() - { - bool isPathScope = nav is {Text: "paths" or "x-ms-paths"}; - bool isMethod = nav is {IsPath: true}; - bool isDefinition = nav is {Text: "definitions"}; - bool isMethodParameters = nav is {Parent: {IsPath: true}} && property.Name == "parameters"; - bool isXmsExamples = nav is {Parent: {IsPath: true}} && property.Name == "x-ms-examples"; - bool isResponses = nav is {Text: "responses"}; - bool isParameters = nav is {Text: "parameters"}; - bool isSecurityDefinitions = nav is {Text: "securityDefinitions"}; - return isPathScope || isDefinition || isParameters || isSecurityDefinitions; - } + // Add the property to the current path + _path.Add(property.Name); - // Add the property to the current path - _path.Add(property.Name); + var isCollapsible = IsCurObjCollapsible(); + // Write the property name + _writer.Write(CodeFileTokenKind.Punctuation, "\""); + var propertyType = isCollapsible ? CodeFileTokenKind.TypeName : CodeFileTokenKind.MemberName; + _writer.Write(propertyType, property.Name); - var isCollapsible = IsCurObjCollapsible(); - // Write the property name - _writer.Write(CodeFileTokenKind.Punctuation, "\""); - var propertyType = isCollapsible ? CodeFileTokenKind.TypeName : CodeFileTokenKind.MemberName; - _writer.Write(propertyType, property.Name); + // Create an ID for this property - // Create an ID for this property + var idPrefix = navigationIdPrefix.Length == 0 ? "" : $"{navigationIdPrefix}_"; - var idPrefix = navigationIdPrefix.Length == 0 ? "" : $"{navigationIdPrefix}_"; + string id = $"{idPrefix}{string.Join('-', _path).TrimStart('#')}"; + _writer.AnnotateDefinition(id); + if (isCollapsible) + { + _writer.Write(CodeFileTokenKind.FoldableSectionHeading, id); + } - string id = $"{idPrefix}{string.Join('-', _path).TrimStart('#')}"; - _writer.AnnotateDefinition(id); - if (isCollapsible) + // Optionally add a navigation tree node + SwaggerTree next = null; + if (nav?.HasNavigableChildren == true || (nav?.Parent.IsPath == true && property.Name is "responses" or "parameters")) + { + string name = property.Name; + string longText = property.Name; + if (nav.IsPath) { - _writer.Write(CodeFileTokenKind.FoldableSectionHeading, id); + name = GetString(property.Value, "operationId") ?? name; } - // Optionally add a navigation tree node - SwaggerTree next = null; - if (nav?.HasNavigableChildren == true || (nav?.Parent.IsPath == true && property.Name is "responses" or "parameters")) - { - string name = property.Name; - string longText = property.Name; - if (nav.IsPath) - { - name = GetString(property.Value, "operationId") ?? name; - } - - next = nav.Add(name, id, null); - } + next = nav.Add(name, id, null); + } - // Visit the value - if (isCollapsible) - { - _writer.Write(CodeFileTokenKind.Punctuation, "\": "); - this._writer.WriteLine(); - this._writer.Write(CodeFileTokenKind.FoldableSectionContentStart, null); - Visit(property.Value, next, navigationIdPrefix); - if (property.Name != values.Last().Name) - { - _writer.Write(CodeFileTokenKind.Punctuation, ", "); - if (multiLine) { _writer.WriteLine();} - } - this._writer.Write(CodeFileTokenKind.FoldableSectionContentEnd, null); - } - else + // Visit the value + if (isCollapsible) + { + _writer.Write(CodeFileTokenKind.Punctuation, "\": "); + this._writer.WriteLine(); + this._writer.Write(CodeFileTokenKind.FoldableSectionContentStart, null); + Visit(property.Value, next, navigationIdPrefix); + if (property.Name != values.Last().Name) { - _writer.Write(CodeFileTokenKind.Punctuation, "\": "); - Visit(property.Value, next, navigationIdPrefix); - if (property.Name != values.Last().Name) - { - _writer.Write(CodeFileTokenKind.Punctuation, ", "); - if (multiLine) { _writer.WriteLine(); } - } + _writer.Write(CodeFileTokenKind.Punctuation, ", "); + if (multiLine) { _writer.WriteLine();} } - - // Make $refs linked - if (property.Name == "$ref" && - property.Value.ValueKind == JsonValueKind.String && - property.Value.GetString().StartsWith("#/")) // Ignore external docs + this._writer.Write(CodeFileTokenKind.FoldableSectionContentEnd, null); + } + else + { + _writer.Write(CodeFileTokenKind.Punctuation, "\": "); + Visit(property.Value, next, navigationIdPrefix); + if (property.Name != values.Last().Name) { - _writer.AnnotateLink($"{idPrefix}{property.Value.GetString().TrimStart('#').Replace('/', '-')}", CodeFileTokenKind.StringLiteral); + _writer.Write(CodeFileTokenKind.Punctuation, ", "); + if (multiLine) { _writer.WriteLine(); } } + } - // Remove the property from the current path - _path.RemoveAt(_path.Count - 1); + // Make $refs linked + if (property.Name == "$ref" && + property.Value.ValueKind == JsonValueKind.String && + property.Value.GetString().StartsWith("#/")) // Ignore external docs + { + _writer.AnnotateLink($"{idPrefix}{property.Value.GetString().TrimStart('#').Replace('/', '-')}", CodeFileTokenKind.StringLiteral); } + + // Remove the property from the current path + _path.RemoveAt(_path.Count - 1); } } + } - /// - /// Generate the listing for an array. - /// - /// The array. - /// - /// - /// - private void VisitArray(JsonElement array, SwaggerTree nav, string navigationIdPrefix, string scopeStart = "[ ", string scopeEnd = " ]") + /// + /// Generate the listing for an array. + /// + /// The array. + /// + /// + /// + private void VisitArray(JsonElement array, SwaggerTree nav, string navigationIdPrefix, string scopeStart = "[ ", string scopeEnd = " ]") + { + bool multiLine = !FitsOnOneLine(array); + using (_writer.Scope(scopeStart, scopeEnd, multiLine)) { - bool multiLine = !FitsOnOneLine(array); - using (_writer.Scope(scopeStart, scopeEnd, multiLine)) + int index = 0; + Fenceposter fencepost = new(); + + Boolean IsCurObjCollapsible() { - int index = 0; - Fenceposter fencepost = new(); + bool isParameters = nav is {Text: "parameters"}; + return isParameters; + } - Boolean IsCurObjCollapsible() + ; + bool isCollapsible = IsCurObjCollapsible(); + foreach (JsonElement child in array.EnumerateArray()) + { + if (fencepost.RequiresSeparator) { - bool isParameters = nav is {Text: "parameters"}; - return isParameters; + _writer.Write(CodeFileTokenKind.Punctuation, ", "); + if (multiLine) { _writer.WriteLine(); } } - ; - bool isCollapsible = IsCurObjCollapsible(); - foreach (JsonElement child in array.EnumerateArray()) - { - if (fencepost.RequiresSeparator) - { - _writer.Write(CodeFileTokenKind.Punctuation, ", "); - if (multiLine) { _writer.WriteLine(); } - } - - _path.Add(index.ToString()); - Visit(child, null, navigationIdPrefix); - _path.RemoveAt(_path.Count - 1); - index++; - } + _path.Add(index.ToString()); + Visit(child, null, navigationIdPrefix); + _path.RemoveAt(_path.Count - 1); + index++; } } + } - /// - /// Generate the listing for a literal value. - /// - /// The literal value. - private void VisitLiteral(JsonElement value) + /// + /// Generate the listing for a literal value. + /// + /// The literal value. + private void VisitLiteral(JsonElement value) + { + switch (value.ValueKind) { - switch (value.ValueKind) - { - case JsonValueKind.Null: - _writer.Write(CodeFileTokenKind.Keyword, "null"); - break; - case JsonValueKind.Undefined: - _writer.Write(CodeFileTokenKind.Keyword, "undefined"); - break; - case JsonValueKind.True: - _writer.Write(CodeFileTokenKind.Keyword, "true"); - break; - case JsonValueKind.False: - _writer.Write(CodeFileTokenKind.Keyword, "false"); - break; - case JsonValueKind.Number: - _writer.Write(CodeFileTokenKind.Literal, value.GetDouble().ToString()); - break; - case JsonValueKind.String: - _writer.Write(CodeFileTokenKind.Punctuation, "\""); - _writer.Write(CodeFileTokenKind.StringLiteral, value.GetString()); - _writer.Write(CodeFileTokenKind.Punctuation, "\""); - break; - default: - throw new InvalidOperationException($"Expected a literal JSON element, not {value.ValueKind}."); - } + case JsonValueKind.Null: + _writer.Write(CodeFileTokenKind.Keyword, "null"); + break; + case JsonValueKind.Undefined: + _writer.Write(CodeFileTokenKind.Keyword, "undefined"); + break; + case JsonValueKind.True: + _writer.Write(CodeFileTokenKind.Keyword, "true"); + break; + case JsonValueKind.False: + _writer.Write(CodeFileTokenKind.Keyword, "false"); + break; + case JsonValueKind.Number: + _writer.Write(CodeFileTokenKind.Literal, value.GetDouble().ToString()); + break; + case JsonValueKind.String: + _writer.Write(CodeFileTokenKind.Punctuation, "\""); + _writer.Write(CodeFileTokenKind.StringLiteral, value.GetString()); + _writer.Write(CodeFileTokenKind.Punctuation, "\""); + break; + default: + throw new InvalidOperationException($"Expected a literal JSON element, not {value.ValueKind}."); } + } - /// - /// Crude heuristic to determine whether a JsonElement can be - /// rendered on a single line. - /// - /// The JSON to render. - /// Whether it fits on a single line. - private bool FitsOnOneLine(JsonElement element) - { - const int maxObjectProperties = 2; - const int maxArrayElements = 3; - const int maxStringLength = 50; + /// + /// Crude heuristic to determine whether a JsonElement can be + /// rendered on a single line. + /// + /// The JSON to render. + /// Whether it fits on a single line. + private bool FitsOnOneLine(JsonElement element) + { + const int maxObjectProperties = 2; + const int maxArrayElements = 3; + const int maxStringLength = 50; - switch (element.ValueKind) - { - case JsonValueKind.Object: - int properties = 0; - foreach (JsonProperty property in element.EnumerateObject()) + switch (element.ValueKind) + { + case JsonValueKind.Object: + int properties = 0; + foreach (JsonProperty property in element.EnumerateObject()) + { + if (property.Value.ValueKind == JsonValueKind.Array || + property.Value.ValueKind == JsonValueKind.Object || + !FitsOnOneLine(property.Value) || + properties++ > maxObjectProperties) { - if (property.Value.ValueKind == JsonValueKind.Array || - property.Value.ValueKind == JsonValueKind.Object || - !FitsOnOneLine(property.Value) || - properties++ > maxObjectProperties) - { - return false; - } + return false; } + } - return true; - case JsonValueKind.Array: - int values = 0; - foreach (JsonElement value in element.EnumerateArray()) + return true; + case JsonValueKind.Array: + int values = 0; + foreach (JsonElement value in element.EnumerateArray()) + { + if (value.ValueKind == JsonValueKind.Array || + value.ValueKind == JsonValueKind.Object || + !FitsOnOneLine(value) || + values++ > maxArrayElements) { - if (value.ValueKind == JsonValueKind.Array || - value.ValueKind == JsonValueKind.Object || - !FitsOnOneLine(value) || - values++ > maxArrayElements) - { - return false; - } + return false; } + } - return true; - case JsonValueKind.String: - return element.GetString().Length <= maxStringLength; - case JsonValueKind.False: - case JsonValueKind.Null: - case JsonValueKind.Number: - case JsonValueKind.True: - case JsonValueKind.Undefined: - default: - return true; - } + return true; + case JsonValueKind.String: + return element.GetString().Length <= maxStringLength; + case JsonValueKind.False: + case JsonValueKind.Null: + case JsonValueKind.Number: + case JsonValueKind.True: + case JsonValueKind.Undefined: + default: + return true; } + } - /// - /// Get a string in a JSON document. - /// - /// The JSON document. - /// Path to the string. - /// The desired string, or null. - private static string GetString(JsonDocument doc, params string[] path) => - GetString(doc.RootElement, path); + /// + /// Get a string in a JSON document. + /// + /// The JSON document. + /// Path to the string. + /// The desired string, or null. + private static string GetString(JsonDocument doc, params string[] path) => + GetString(doc.RootElement, path); - /// - /// Get a string in a JSON document. - /// - /// The element to start at. - /// Path to the string. - /// The desired string, or null. - private static string GetString(JsonElement element, params string[] path) + /// + /// Get a string in a JSON document. + /// + /// The element to start at. + /// Path to the string. + /// The desired string, or null. + private static string GetString(JsonElement element, params string[] path) + { + foreach (string part in path) { - foreach (string part in path) + if (element.ValueKind != JsonValueKind.Object || + !element.TryGetProperty(part, out element)) { - if (element.ValueKind != JsonValueKind.Object || - !element.TryGetProperty(part, out element)) - { - return null; - } + return null; } - - return element.ValueKind == JsonValueKind.String ? element.GetString() : null; } + return element.ValueKind == JsonValueKind.String ? element.GetString() : null; + } + + /// + /// Comparer to sort the operations within a path. It puts + /// OperationIds before things like parameters. + /// + private class OperationComparer : IComparer + { /// - /// Comparer to sort the operations within a path. It puts - /// OperationIds before things like parameters. + /// Compare two path entry names. /// - private class OperationComparer : IComparer - { - /// - /// Compare two path entry names. - /// - /// The first path entry name. - /// The second path entry name. - /// -1 if the first is smaller, 0 if equal, or 1 if the first is larger. - public int Compare([AllowNull] string x, [AllowNull] string y) => - (x?.Contains('_'), y?.Contains('_')) switch - { - (null, null) => 0, - (null, _) => 1, - (_, null) => -1, - (true, false) => -1, - (false, true) => 1, - _ => string.Compare(x, y) - }; - } + /// The first path entry name. + /// The second path entry name. + /// -1 if the first is smaller, 0 if equal, or 1 if the first is larger. + public int Compare([AllowNull] string x, [AllowNull] string y) => + (x?.Contains('_'), y?.Contains('_')) switch + { + (null, null) => 0, + (null, _) => 1, + (_, null) => -1, + (true, false) => -1, + (false, true) => 1, + _ => string.Compare(x, y) + }; } } } diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/TokenSerializer.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/TokenSerializer.cs index b240db3ecdf..f465733aa30 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/TokenSerializer.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/TokenSerializer.cs @@ -5,596 +5,594 @@ using System.Linq; using System.Reflection; using System.Text.Json; -using APIView; -namespace SwaggerApiParser +namespace SwaggerApiParser; + +public class IteratorPath { - public class IteratorPath + private LinkedList paths; + + public IteratorPath() { - private LinkedList paths; + this.paths = new LinkedList(); + this.Add("#"); + } - public IteratorPath() + public IteratorPath(IteratorPath parent) + { + this.paths = new LinkedList(); + foreach (var path in parent.paths) { - this.paths = new LinkedList(); - this.Add("#"); + this.paths.AddLast(path); } + } - public IteratorPath(IteratorPath parent) - { - this.paths = new LinkedList(); - foreach (var path in parent.paths) - { - this.paths.AddLast(path); - } - } + public string rootPath() + { + LinkedList root = new LinkedList(); + root.AddLast("#"); + root.AddLast(this.paths.ElementAt(1)); + return Utils.BuildDefinitionId(root); + } - public string rootPath() - { - LinkedList root = new LinkedList(); - root.AddLast("#"); - root.AddLast(this.paths.ElementAt(1)); - return Utils.BuildDefinitionId(root); - } + public void Add(string node) + { + this.paths.AddLast(node); + } - public void Add(string node) + public void AddRange(IEnumerable nodes) + { + foreach (var node in nodes) { - this.paths.AddLast(node); + this.Add(node); } + } - public void AddRange(IEnumerable nodes) + public void PopMulti(int number) + { + for (int i = 0; i < number; i++) { - foreach (var node in nodes) - { - this.Add(node); - } + this.Pop(); } + } - public void PopMulti(int number) - { - for (int i = 0; i < number; i++) - { - this.Pop(); - } - } + public void Pop() + { + this.paths.RemoveLast(); + } - public void Pop() - { - this.paths.RemoveLast(); - } + public string CurrentPath() + { + return Utils.BuildDefinitionId(this.paths); + } - public string CurrentPath() - { - return Utils.BuildDefinitionId(this.paths); - } + public string CurrentNextPath(string nextPath) + { + this.Add(nextPath); + var ret = this.CurrentPath(); + this.Pop(); + return ret; + } +} - public string CurrentNextPath(string nextPath) - { - this.Add(nextPath); - var ret = this.CurrentPath(); - this.Pop(); - return ret; - } +public class SerializeContext +{ + public int intent = 0; + public readonly IteratorPath IteratorPath; + + public SerializeContext() + { + this.IteratorPath = new IteratorPath(); } - public class SerializeContext + public SerializeContext(int intent, IteratorPath iteratorPath) { - public int intent = 0; - public readonly IteratorPath IteratorPath; + this.intent = intent; + this.IteratorPath = new IteratorPath(iteratorPath); + } +} - public SerializeContext() +public static class TokenSerializer +{ + private const String IntentText = " "; + + public static CodeFileToken[] TokenSerializeAsJson(JsonElement jsonElement, bool isFoldable = false) + { + List ret = new List(); + Visitor visitor = new Visitor(); + visitor.Visit(jsonElement); + if (isFoldable) { - this.IteratorPath = new IteratorPath(); + ret.Add(TokenSerializer.FoldableContentStart()); } - public SerializeContext(int intent, IteratorPath iteratorPath) + ret.AddRange(visitor.Writer.ToTokens()); + ret.Add(TokenSerializer.NewLine()); + if (isFoldable) { - this.intent = intent; - this.IteratorPath = new IteratorPath(iteratorPath); + ret.Add(TokenSerializer.FoldableContentEnd()); } + + return ret.ToArray(); } - public static class TokenSerializer + /* + * TokenSerialize obj into CodeFileTokens. + * Each line begin with intent + * One line format: ... + * + */ + public static CodeFileToken[] TokenSerialize(object obj, SerializeContext context, String[] serializePropertyName = null) { - private const String IntentText = " "; + List ret = new List(); + Type t = obj.GetType(); + PropertyInfo[] props = t.GetProperties(); - public static CodeFileToken[] TokenSerializeAsJson(JsonElement jsonElement, bool isFoldable = false) - { - List ret = new List(); - Visitor visitor = new Visitor(); - visitor.Visit(jsonElement); - if (isFoldable) - { - ret.Add(TokenSerializer.FoldableContentStart()); - } - ret.AddRange(visitor.Writer.ToTokens()); + if (t.IsPrimitive || t == typeof(Decimal) || t == typeof(String)) + { + // ret.Add(Intent(intent)); + ret.Add(new CodeFileToken(obj.ToString(), CodeFileTokenKind.Literal)); ret.Add(TokenSerializer.NewLine()); - if (isFoldable) - { - ret.Add(TokenSerializer.FoldableContentEnd()); - } - return ret.ToArray(); } - /* - * TokenSerialize obj into CodeFileTokens. - * Each line begin with intent - * One line format: ... - * - */ - public static CodeFileToken[] TokenSerialize(object obj, SerializeContext context, String[] serializePropertyName = null) + foreach (var prop in props) { - List ret = new List(); - Type t = obj.GetType(); - PropertyInfo[] props = t.GetProperties(); - - - if (t.IsPrimitive || t == typeof(Decimal) || t == typeof(String)) + object value = prop.GetValue(obj); + if (value == null || (serializePropertyName != null && (serializePropertyName.All(s => prop.Name != s)))) { - // ret.Add(Intent(intent)); - ret.Add(new CodeFileToken(obj.ToString(), CodeFileTokenKind.Literal)); - ret.Add(TokenSerializer.NewLine()); - return ret.ToArray(); + continue; } - foreach (var prop in props) - { - object value = prop.GetValue(obj); - if (value == null || (serializePropertyName != null && (serializePropertyName.All(s => prop.Name != s)))) - { - continue; - } - - Type propType = prop.PropertyType; - // ret.Add(Intent(intent)); - ret.Add(new CodeFileToken(prop.Name, CodeFileTokenKind.Literal){DefinitionId = context.IteratorPath.CurrentNextPath(prop.Name)}); - ret.Add(Colon()); - string navigationToId = null; - var valueKind = CodeFileTokenKind.Literal; + Type propType = prop.PropertyType; + // ret.Add(Intent(intent)); + ret.Add(new CodeFileToken(prop.Name, CodeFileTokenKind.Literal){DefinitionId = context.IteratorPath.CurrentNextPath(prop.Name)}); + ret.Add(Colon()); + string navigationToId = null; + var valueKind = CodeFileTokenKind.Literal; - if (prop.Name == "Ref") - { - navigationToId = context.IteratorPath.rootPath() + Utils.GetRefDefinitionIdPath(value.ToString()); - valueKind = CodeFileTokenKind.MemberName; + if (prop.Name == "Ref") + { + navigationToId = context.IteratorPath.rootPath() + Utils.GetRefDefinitionIdPath(value.ToString()); + valueKind = CodeFileTokenKind.MemberName; - } + } - if (propType.IsPrimitive || propType == typeof(Decimal) || propType == typeof(String)) - { - ret.Add(new CodeFileToken(value.ToString(), valueKind) { NavigateToId = navigationToId}); - ret.Add(NewLine()); - } - else if (propType.IsGenericType || propType.IsArray) - { - ret.Add(NewLine()); - foreach (var item in (IEnumerable)value) - { - var child = TokenSerializer.TokenSerialize(item, new SerializeContext(intent: context.intent + 1, context.IteratorPath)); - ret.AddRange(child); - } - } - else + if (propType.IsPrimitive || propType == typeof(Decimal) || propType == typeof(String)) + { + ret.Add(new CodeFileToken(value.ToString(), valueKind) { NavigateToId = navigationToId}); + ret.Add(NewLine()); + } + else if (propType.IsGenericType || propType.IsArray) + { + ret.Add(NewLine()); + foreach (var item in (IEnumerable)value) { - ret.Add(NewLine()); - var child = TokenSerializer.TokenSerialize(value, new SerializeContext(intent: context.intent + 1, context.IteratorPath)); + var child = TokenSerializer.TokenSerialize(item, new SerializeContext(intent: context.intent + 1, context.IteratorPath)); ret.AddRange(child); } } - - return ret.ToArray(); + else + { + ret.Add(NewLine()); + var child = TokenSerializer.TokenSerialize(value, new SerializeContext(intent: context.intent + 1, context.IteratorPath)); + ret.AddRange(child); + } } - public static CodeFileToken[] TokenSerializeAsTableFormat(int rowCount, int columnCount, IEnumerable columnNames, CodeFileToken[] rows, string tableDefinitionId) - { - List ret = new List(); + return ret.ToArray(); + } - ret.Add(TokenSerializer.TableBegin(tableDefinitionId)); - ret.AddRange(TokenSerializer.TableSize(rowCount, columnCount)); - ret.AddRange(columnNames.Select(TokenSerializer.TableColumnName)); - ret.AddRange(rows); - ret.Add(TokenSerializer.TableEnd()); - return ret.ToArray(); - } + public static CodeFileToken[] TokenSerializeAsTableFormat(int rowCount, int columnCount, IEnumerable columnNames, CodeFileToken[] rows, string tableDefinitionId) + { + List ret = new List(); + + ret.Add(TokenSerializer.TableBegin(tableDefinitionId)); + ret.AddRange(TokenSerializer.TableSize(rowCount, columnCount)); + ret.AddRange(columnNames.Select(TokenSerializer.TableColumnName)); + ret.AddRange(rows); + ret.Add(TokenSerializer.TableEnd()); + return ret.ToArray(); + } - public static CodeFileToken Intent(int intent) - { - // var ret = new CodeFileToken(String.Concat(Enumerable.Repeat(IntentText, intent)), CodeFileTokenKind.Whitespace); - var ret = new CodeFileToken(intent.ToString(), CodeFileTokenKind.Whitespace); - return ret; - } + public static CodeFileToken Intent(int intent) + { + // var ret = new CodeFileToken(String.Concat(Enumerable.Repeat(IntentText, intent)), CodeFileTokenKind.Whitespace); + var ret = new CodeFileToken(intent.ToString(), CodeFileTokenKind.Whitespace); + return ret; + } - public static CodeFileToken[] TableCell(IEnumerable tokens) - { - List ret = new List(); - ret.Add(new CodeFileToken(null, CodeFileTokenKind.TableCellBegin)); - ret.AddRange(tokens); - ret.Add(new CodeFileToken(null, CodeFileTokenKind.TableCellEnd)); - return ret.ToArray(); - } + public static CodeFileToken[] TableCell(IEnumerable tokens) + { + List ret = new List(); + ret.Add(new CodeFileToken(null, CodeFileTokenKind.TableCellBegin)); + ret.AddRange(tokens); + ret.Add(new CodeFileToken(null, CodeFileTokenKind.TableCellEnd)); + return ret.ToArray(); + } - public static CodeFileToken[] OneLineToken(int intent, IEnumerable contentTokens) - { - List ret = new List(); - // ret.Add(TokenSerializer.Intent(intent)); - ret.AddRange(contentTokens); - ret.Add(NewLine()); - return ret.ToArray(); - } + public static CodeFileToken[] OneLineToken(int intent, IEnumerable contentTokens) + { + List ret = new List(); + // ret.Add(TokenSerializer.Intent(intent)); + ret.AddRange(contentTokens); + ret.Add(NewLine()); + return ret.ToArray(); + } - public static CodeFileToken NewLine() - { - return new CodeFileToken("", CodeFileTokenKind.Newline); - } + public static CodeFileToken NewLine() + { + return new CodeFileToken("", CodeFileTokenKind.Newline); + } - public static CodeFileToken Colon() - { - return new CodeFileToken(": ", CodeFileTokenKind.Punctuation); - } + public static CodeFileToken Colon() + { + return new CodeFileToken(": ", CodeFileTokenKind.Punctuation); + } - public static CodeFileToken NavigableToken(String value, CodeFileTokenKind kind, String definitionId) - { - var ret = new CodeFileToken(value, kind) {DefinitionId = definitionId}; - return ret; - } + public static CodeFileToken NavigableToken(String value, CodeFileTokenKind kind, String definitionId) + { + var ret = new CodeFileToken(value, kind) {DefinitionId = definitionId}; + return ret; + } - public static CodeFileToken[] KeyValueTokens(String key, String value, bool newLine = true, string keyDefinitionId = null) + public static CodeFileToken[] KeyValueTokens(String key, String value, bool newLine = true, string keyDefinitionId = null) + { + List ret = new List(); + ret.Add(TokenSerializer.NavigableToken(key, CodeFileTokenKind.Literal, keyDefinitionId)); + ret.Add(TokenSerializer.Colon()); + ret.Add(new CodeFileToken(value, CodeFileTokenKind.Literal)); + if (newLine) { - List ret = new List(); - ret.Add(TokenSerializer.NavigableToken(key, CodeFileTokenKind.Literal, keyDefinitionId)); - ret.Add(TokenSerializer.Colon()); - ret.Add(new CodeFileToken(value, CodeFileTokenKind.Literal)); - if (newLine) - { - ret.Add(TokenSerializer.NewLine()); - } - - return ret.ToArray(); + ret.Add(TokenSerializer.NewLine()); } - public static CodeFileToken FoldableParentToken(String value) - { - var ret = new CodeFileToken(value, CodeFileTokenKind.FoldableSectionHeading); - return ret; - } + return ret.ToArray(); + } - public static CodeFileToken FoldableContentStart() - { - var ret = new CodeFileToken(null, CodeFileTokenKind.FoldableSectionContentStart); - return ret; - } + public static CodeFileToken FoldableParentToken(String value) + { + var ret = new CodeFileToken(value, CodeFileTokenKind.FoldableSectionHeading); + return ret; + } - public static CodeFileToken TableBegin(string definitionId) - { - var ret = new CodeFileToken(null, CodeFileTokenKind.TableBegin){DefinitionId = definitionId}; - return ret; - } + public static CodeFileToken FoldableContentStart() + { + var ret = new CodeFileToken(null, CodeFileTokenKind.FoldableSectionContentStart); + return ret; + } - public static CodeFileToken TableEnd() - { - var ret = new CodeFileToken(null, CodeFileTokenKind.TableEnd); - return ret; - } + public static CodeFileToken TableBegin(string definitionId) + { + var ret = new CodeFileToken(null, CodeFileTokenKind.TableBegin){DefinitionId = definitionId}; + return ret; + } - public static CodeFileToken[] TableSize(int row, int column) - { - var ret = new List(); - ret.Add(new CodeFileToken(row.ToString(), CodeFileTokenKind.TableRowCount)); - ret.Add(new CodeFileToken(column.ToString(), CodeFileTokenKind.TableColumnCount)); + public static CodeFileToken TableEnd() + { + var ret = new CodeFileToken(null, CodeFileTokenKind.TableEnd); + return ret; + } - return ret.ToArray(); - } + public static CodeFileToken[] TableSize(int row, int column) + { + var ret = new List(); + ret.Add(new CodeFileToken(row.ToString(), CodeFileTokenKind.TableRowCount)); + ret.Add(new CodeFileToken(column.ToString(), CodeFileTokenKind.TableColumnCount)); - public static CodeFileToken TableColumnName(string columnName) - { - var ret = new CodeFileToken(columnName, CodeFileTokenKind.TableColumnName); - return ret; - } + return ret.ToArray(); + } - public static CodeFileToken FoldableContentEnd() - { - var ret = new CodeFileToken(null, CodeFileTokenKind.FoldableSectionContentEnd); - return ret; - } + public static CodeFileToken TableColumnName(string columnName) + { + var ret = new CodeFileToken(columnName, CodeFileTokenKind.TableColumnName); + return ret; } - public class Visitor + public static CodeFileToken FoldableContentEnd() { - public SwaggerTokenSerializer.IndentWriter Writer = new(); - private IteratorPath iteratorPath = new(); + var ret = new CodeFileToken(null, CodeFileTokenKind.FoldableSectionContentEnd); + return ret; + } +} +public class Visitor +{ + public SwaggerTokenSerializer.IndentWriter Writer = new(); + private IteratorPath iteratorPath = new(); - public static CodeFileToken[] GenerateCodeFileTokens(JsonDocument document, Boolean isCurObjCollapsible = false) - { - Visitor visitor = new(); - visitor.Visit(document.RootElement); - return visitor.Writer.ToTokens(); - } + public static CodeFileToken[] GenerateCodeFileTokens(JsonDocument document, Boolean isCurObjCollapsible = false) + { + Visitor visitor = new(); - /// - /// Generate the listing for a JSON value. - /// - /// The JSON value. - /// - /// - public void Visit(JsonElement element, string scopeStart = "{ ", string scopeEnd = " }") - { - switch (element.ValueKind) - { - case JsonValueKind.Object: - VisitObject(element, scopeStart, scopeEnd); - break; - case JsonValueKind.Array: - VisitArray(element); - break; - case JsonValueKind.False: - case JsonValueKind.Null: - case JsonValueKind.Number: - case JsonValueKind.String: - case JsonValueKind.True: - case JsonValueKind.Undefined: - VisitLiteral(element); - break; - default: - break; - } + visitor.Visit(document.RootElement); + return visitor.Writer.ToTokens(); + } + + /// + /// Generate the listing for a JSON value. + /// + /// The JSON value. + /// + /// + public void Visit(JsonElement element, string scopeStart = "{ ", string scopeEnd = " }") + { + switch (element.ValueKind) + { + case JsonValueKind.Object: + VisitObject(element, scopeStart, scopeEnd); + break; + case JsonValueKind.Array: + VisitArray(element); + break; + case JsonValueKind.False: + case JsonValueKind.Null: + case JsonValueKind.Number: + case JsonValueKind.String: + case JsonValueKind.True: + case JsonValueKind.Undefined: + VisitLiteral(element); + break; + default: + break; } + } - /// - /// Generate the listing for an object. - /// - /// The JSON object. - /// - /// - private void VisitObject(JsonElement obj, string scopeStart = "{", string scopeEnd = "}") + /// + /// Generate the listing for an object. + /// + /// The JSON object. + /// + /// + private void VisitObject(JsonElement obj, string scopeStart = "{", string scopeEnd = "}") + { + bool multiLine = AlwaysMultiLine(obj); + + using (this.Writer.Scope(scopeStart, scopeEnd, multiLine)) { - bool multiLine = AlwaysMultiLine(obj); + // Optionally sort the values + IEnumerable values = obj.EnumerateObject(); + SwaggerTokenSerializer.Fenceposter fencepost = new(); - using (this.Writer.Scope(scopeStart, scopeEnd, multiLine)) + // Generate the listing for each property + foreach (JsonProperty property in values) { - // Optionally sort the values - IEnumerable values = obj.EnumerateObject(); - SwaggerTokenSerializer.Fenceposter fencepost = new(); + // Add the property to the current path + iteratorPath.Add(property.Name); - // Generate the listing for each property - foreach (JsonProperty property in values) - { - // Add the property to the current path - iteratorPath.Add(property.Name); + var isCollapsible = IsCurObjCollapsible(property.Name); + // Write the property name + this.Writer.Write(CodeFileTokenKind.Punctuation, "\""); + var propertyType = isCollapsible ? CodeFileTokenKind.TypeName : CodeFileTokenKind.MemberName; + this.Writer.Write(propertyType, property.Name); - var isCollapsible = IsCurObjCollapsible(property.Name); - // Write the property name - this.Writer.Write(CodeFileTokenKind.Punctuation, "\""); - var propertyType = isCollapsible ? CodeFileTokenKind.TypeName : CodeFileTokenKind.MemberName; - this.Writer.Write(propertyType, property.Name); + // Create an ID for this property + string id = this.iteratorPath.CurrentPath(); + this.Writer.AnnotateDefinition(id); + if (isCollapsible) + { + this.Writer.Write(CodeFileTokenKind.FoldableSectionHeading, id); + } - // Create an ID for this property - string id = this.iteratorPath.CurrentPath(); - this.Writer.AnnotateDefinition(id); - if (isCollapsible) + // Visit the value + if (isCollapsible) + { + this.Writer.Write(CodeFileTokenKind.Punctuation, "\": "); + this.Writer.WriteLine(); + this.Writer.Write(CodeFileTokenKind.FoldableSectionHeading, null); + Visit(property.Value); + if (property.Name != values.Last().Name) { - this.Writer.Write(CodeFileTokenKind.FoldableSectionHeading, id); + this.Writer.Write(CodeFileTokenKind.Punctuation, ", "); + if (multiLine) { this.Writer.WriteLine(); } } - // Visit the value - if (isCollapsible) - { - this.Writer.Write(CodeFileTokenKind.Punctuation, "\": "); - this.Writer.WriteLine(); - this.Writer.Write(CodeFileTokenKind.FoldableSectionHeading, null); - Visit(property.Value); - if (property.Name != values.Last().Name) - { - this.Writer.Write(CodeFileTokenKind.Punctuation, ", "); - if (multiLine) { this.Writer.WriteLine(); } - } - - this.Writer.Write(CodeFileTokenKind.FoldableSectionHeading, null); - } - else + this.Writer.Write(CodeFileTokenKind.FoldableSectionHeading, null); + } + else + { + this.Writer.Write(CodeFileTokenKind.Punctuation, "\": "); + Visit(property.Value); + if (property.Name != values.Last().Name) { - this.Writer.Write(CodeFileTokenKind.Punctuation, "\": "); - Visit(property.Value); - if (property.Name != values.Last().Name) - { - this.Writer.Write(CodeFileTokenKind.Punctuation, ", "); - if (multiLine) { this.Writer.WriteLine(); } - } + this.Writer.Write(CodeFileTokenKind.Punctuation, ", "); + if (multiLine) { this.Writer.WriteLine(); } } - - // Remove the property from the current path - this.iteratorPath.Pop(); } + + // Remove the property from the current path + this.iteratorPath.Pop(); } } + } - private static bool IsCurObjCollapsible(string propertyName) - { - return false; - // return propertyName.Equals("General"); - } + private static bool IsCurObjCollapsible(string propertyName) + { + return false; + // return propertyName.Equals("General"); + } - /// - /// Generate the listing for an array. - /// - /// The array. - /// - /// - private void VisitArray(JsonElement array, string scopeStart = "[ ", string scopeEnd = " ]") + /// + /// Generate the listing for an array. + /// + /// The array. + /// + /// + private void VisitArray(JsonElement array, string scopeStart = "[ ", string scopeEnd = " ]") + { + bool multiLine = AlwaysMultiLine(array); + using (this.Writer.Scope(scopeStart, scopeEnd, multiLine)) { - bool multiLine = AlwaysMultiLine(array); - using (this.Writer.Scope(scopeStart, scopeEnd, multiLine)) - { - int index = 0; - SwaggerTokenSerializer.Fenceposter fencepost = new(); + int index = 0; + SwaggerTokenSerializer.Fenceposter fencepost = new(); - foreach (JsonElement child in array.EnumerateArray()) + foreach (JsonElement child in array.EnumerateArray()) + { + if (fencepost.RequiresSeparator) { - if (fencepost.RequiresSeparator) - { - this.Writer.Write(CodeFileTokenKind.Punctuation, ", "); - if (multiLine) { this.Writer.WriteLine(); } - } - - this.iteratorPath.Add(index.ToString()); - Visit(child); - this.iteratorPath.Pop(); - index++; + this.Writer.Write(CodeFileTokenKind.Punctuation, ", "); + if (multiLine) { this.Writer.WriteLine(); } } + + this.iteratorPath.Add(index.ToString()); + Visit(child); + this.iteratorPath.Pop(); + index++; } } + } - /// - /// Generate the listing for a literal value. - /// - /// The literal value. - private void VisitLiteral(JsonElement value) - { - switch (value.ValueKind) - { - case JsonValueKind.Null: - this.Writer.Write(CodeFileTokenKind.Keyword, "null"); - break; - case JsonValueKind.Undefined: - this.Writer.Write(CodeFileTokenKind.Keyword, "undefined"); - break; - case JsonValueKind.True: - this.Writer.Write(CodeFileTokenKind.Keyword, "true"); - break; - case JsonValueKind.False: - this.Writer.Write(CodeFileTokenKind.Keyword, "false"); - break; - case JsonValueKind.Number: - this.Writer.Write(CodeFileTokenKind.Literal, value.GetDouble().ToString()); - break; - case JsonValueKind.String: - this.Writer.Write(CodeFileTokenKind.Punctuation, "\""); - this.Writer.Write(CodeFileTokenKind.StringLiteral, value.GetString()); - this.iteratorPath.Add(value.GetString()); - this.Writer.AnnotateDefinition(this.iteratorPath.CurrentPath()); - this.iteratorPath.Pop(); - this.Writer.Write(CodeFileTokenKind.Punctuation, "\""); - break; - default: - throw new InvalidOperationException($"Expected a literal JSON element, not {value.ValueKind}."); - } + /// + /// Generate the listing for a literal value. + /// + /// The literal value. + private void VisitLiteral(JsonElement value) + { + switch (value.ValueKind) + { + case JsonValueKind.Null: + this.Writer.Write(CodeFileTokenKind.Keyword, "null"); + break; + case JsonValueKind.Undefined: + this.Writer.Write(CodeFileTokenKind.Keyword, "undefined"); + break; + case JsonValueKind.True: + this.Writer.Write(CodeFileTokenKind.Keyword, "true"); + break; + case JsonValueKind.False: + this.Writer.Write(CodeFileTokenKind.Keyword, "false"); + break; + case JsonValueKind.Number: + this.Writer.Write(CodeFileTokenKind.Literal, value.GetDouble().ToString()); + break; + case JsonValueKind.String: + this.Writer.Write(CodeFileTokenKind.Punctuation, "\""); + this.Writer.Write(CodeFileTokenKind.StringLiteral, value.GetString()); + this.iteratorPath.Add(value.GetString()); + this.Writer.AnnotateDefinition(this.iteratorPath.CurrentPath()); + this.iteratorPath.Pop(); + this.Writer.Write(CodeFileTokenKind.Punctuation, "\""); + break; + default: + throw new InvalidOperationException($"Expected a literal JSON element, not {value.ValueKind}."); } + } - private bool AlwaysMultiLine (JsonElement element) - { - return true; - } + private bool AlwaysMultiLine (JsonElement element) + { + return true; + } - /// - /// Crude heuristic to determine whether a JsonElement can be - /// rendered on a single line. - /// - /// The JSON to render. - /// Whether it fits on a single line. - private bool FitsOnOneLine(JsonElement element) - { - const int maxObjectProperties = 2; - const int maxArrayElements = 3; - const int maxStringLength = 50; + /// + /// Crude heuristic to determine whether a JsonElement can be + /// rendered on a single line. + /// + /// The JSON to render. + /// Whether it fits on a single line. + private bool FitsOnOneLine(JsonElement element) + { + const int maxObjectProperties = 2; + const int maxArrayElements = 3; + const int maxStringLength = 50; - switch (element.ValueKind) - { - case JsonValueKind.Object: - int properties = 0; - foreach (JsonProperty property in element.EnumerateObject()) + switch (element.ValueKind) + { + case JsonValueKind.Object: + int properties = 0; + foreach (JsonProperty property in element.EnumerateObject()) + { + if (property.Value.ValueKind == JsonValueKind.Array || + property.Value.ValueKind == JsonValueKind.Object || + !FitsOnOneLine(property.Value) || + properties++ > maxObjectProperties) { - if (property.Value.ValueKind == JsonValueKind.Array || - property.Value.ValueKind == JsonValueKind.Object || - !FitsOnOneLine(property.Value) || - properties++ > maxObjectProperties) - { - return false; - } + return false; } + } - return true; - case JsonValueKind.Array: - int values = 0; - foreach (JsonElement value in element.EnumerateArray()) + return true; + case JsonValueKind.Array: + int values = 0; + foreach (JsonElement value in element.EnumerateArray()) + { + if (value.ValueKind == JsonValueKind.Array || + value.ValueKind == JsonValueKind.Object || + !FitsOnOneLine(value) || + values++ > maxArrayElements) { - if (value.ValueKind == JsonValueKind.Array || - value.ValueKind == JsonValueKind.Object || - !FitsOnOneLine(value) || - values++ > maxArrayElements) - { - return false; - } + return false; } + } - return true; - case JsonValueKind.String: - return element.GetString().Length <= maxStringLength; - case JsonValueKind.False: - case JsonValueKind.Null: - case JsonValueKind.Number: - case JsonValueKind.True: - case JsonValueKind.Undefined: - default: - return true; - } + return true; + case JsonValueKind.String: + return element.GetString().Length <= maxStringLength; + case JsonValueKind.False: + case JsonValueKind.Null: + case JsonValueKind.Number: + case JsonValueKind.True: + case JsonValueKind.Undefined: + default: + return true; } + } - /// - /// Get a string in a JSON document. - /// - /// The JSON document. - /// Path to the string. - /// The desired string, or null. - private static string GetString(JsonDocument doc, params string[] path) => - GetString(doc.RootElement, path); - - /// - /// Get a string in a JSON document. - /// - /// The element to start at. - /// Path to the string. - /// The desired string, or null. - private static string GetString(JsonElement element, params string[] path) + /// + /// Get a string in a JSON document. + /// + /// The JSON document. + /// Path to the string. + /// The desired string, or null. + private static string GetString(JsonDocument doc, params string[] path) => + GetString(doc.RootElement, path); + + /// + /// Get a string in a JSON document. + /// + /// The element to start at. + /// Path to the string. + /// The desired string, or null. + private static string GetString(JsonElement element, params string[] path) + { + foreach (string part in path) { - foreach (string part in path) + if (element.ValueKind != JsonValueKind.Object || + !element.TryGetProperty(part, out element)) { - if (element.ValueKind != JsonValueKind.Object || - !element.TryGetProperty(part, out element)) - { - return null; - } + return null; } - - return element.ValueKind == JsonValueKind.String ? element.GetString() : null; } + return element.ValueKind == JsonValueKind.String ? element.GetString() : null; + } + + /// + /// Comparer to sort the operations within a path. It puts + /// OperationIds before things like parameters. + /// + private class OperationComparer : IComparer + { /// - /// Comparer to sort the operations within a path. It puts - /// OperationIds before things like parameters. + /// Compare two path entry names. /// - private class OperationComparer : IComparer - { - /// - /// Compare two path entry names. - /// - /// The first path entry name. - /// The second path entry name. - /// -1 if the first is smaller, 0 if equal, or 1 if the first is larger. - public int Compare([AllowNull] string x, [AllowNull] string y) => - (x?.Contains('_'), y?.Contains('_')) switch - { - (null, null) => 0, - (null, _) => 1, - (_, null) => -1, - (true, false) => -1, - (false, true) => 1, - _ => string.Compare(x, y) - }; - } + /// The first path entry name. + /// The second path entry name. + /// -1 if the first is smaller, 0 if equal, or 1 if the first is larger. + public int Compare([AllowNull] string x, [AllowNull] string y) => + (x?.Contains('_'), y?.Contains('_')) switch + { + (null, null) => 0, + (null, _) => 1, + (_, null) => -1, + (true, false) => -1, + (false, true) => 1, + _ => string.Compare(x, y) + }; } } diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/Utils.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/Utils.cs index a47a4581d85..7f445da086c 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/Utils.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParser/Utils.cs @@ -4,190 +4,189 @@ using System.Linq; using System.Text.RegularExpressions; -namespace SwaggerApiParser +namespace SwaggerApiParser; + +public struct PathNode { - public struct PathNode + public readonly string CommonPath; + public readonly List Children; + public readonly int Level; + public bool Leaf; + + public PathNode(string commonPath, List children, int level, bool leaf) { - public readonly string CommonPath; - public readonly List Children; - public readonly int Level; - public bool Leaf; + this.Children = children; + this.CommonPath = commonPath; + this.Level = level; + this.Leaf = leaf; + } +} - public PathNode(string commonPath, List children, int level, bool leaf) +public static class Utils +{ + public static string GetCommonPath(IEnumerable paths) + { + var pathsArray = new List>() { }; + if (pathsArray == null) { - this.Children = children; - this.CommonPath = commonPath; - this.Level = level; - this.Leaf = leaf; + throw new ArgumentNullException(nameof(pathsArray)); } - } - public static class Utils - { - public static string GetCommonPath(IEnumerable paths) + pathsArray.AddRange(paths.Select(path => path.Split('/')).Select(pathParts => new List(pathParts))); + + var commonPathList = new List() { }; + + bool found = false; + var idx = 0; + string commonPath = ""; + while (!found) { - var pathsArray = new List>() { }; - if (pathsArray == null) + if (idx < pathsArray[0].Count) { - throw new ArgumentNullException(nameof(pathsArray)); + commonPath = pathsArray[0][idx]; } - pathsArray.AddRange(paths.Select(path => path.Split('/')).Select(pathParts => new List(pathParts))); - - var commonPathList = new List() { }; + if (pathsArray.Any(it => idx >= it.Count || it[idx] != commonPath)) + { + found = true; + } - bool found = false; - var idx = 0; - string commonPath = ""; - while (!found) + if (found) { - if (idx < pathsArray[0].Count) - { - commonPath = pathsArray[0][idx]; - } + continue; + } - if (pathsArray.Any(it => idx >= it.Count || it[idx] != commonPath)) - { - found = true; - } + idx++; + commonPathList.Add(commonPath); + } - if (found) - { - continue; - } + return string.Join("/", commonPathList); + } - idx++; - commonPathList.Add(commonPath); - } + public static string GetResourceProviderFromPath(string path) + { + const string resourceProviderPattern = "/providers/(:?[^{/]+)"; + var match = Regex.Match(path, resourceProviderPattern, RegexOptions.RightToLeft); + return match.Success ? match.Groups[1].Value : ""; + } - return string.Join("/", commonPathList); - } + public static PathNode BuildPathTree(IEnumerable paths) + { + var sortedPath = paths.OrderBy(s => s); - public static string GetResourceProviderFromPath(string path) + PathNode root = new PathNode("", new List() { }, level: 0, leaf: false); + foreach (var path in sortedPath) { - const string resourceProviderPattern = "/providers/(:?[^{/]+)"; - var match = Regex.Match(path, resourceProviderPattern, RegexOptions.RightToLeft); - return match.Success ? match.Groups[1].Value : ""; + root.Children.Add(new PathNode(commonPath: path, + children: new List(), level: 1, leaf: true)); } - public static PathNode BuildPathTree(IEnumerable paths) - { - var sortedPath = paths.OrderBy(s => s); + return BuildPathTreeInternal(root, 0); + } - PathNode root = new PathNode("", new List() { }, level: 0, leaf: false); - foreach (var path in sortedPath) + public static string VisualizePathTree(PathNode root) + { + Queue qu = new Queue(); + qu.Enqueue(root); + string res = ""; + while (qu.TryDequeue(out var cur)) + { + Console.WriteLine($"{cur.Level}, {cur.CommonPath}"); + res += $"{cur.Level}, {cur.CommonPath}\n"; + foreach (var child in cur.Children) { - root.Children.Add(new PathNode(commonPath: path, - children: new List(), level: 1, leaf: true)); + qu.Enqueue(child); } - - return BuildPathTreeInternal(root, 0); } - public static string VisualizePathTree(PathNode root) - { - Queue qu = new Queue(); - qu.Enqueue(root); - string res = ""; - while (qu.TryDequeue(out var cur)) - { - Console.WriteLine($"{cur.Level}, {cur.CommonPath}"); - res += $"{cur.Level}, {cur.CommonPath}\n"; - foreach (var child in cur.Children) - { - qu.Enqueue(child); - } - } + return res; + } - return res; - } + private static PathNode BuildPathTreeInternal(PathNode root, int level) + { + var index = 0; + var prevCommonPath = ""; - private static PathNode BuildPathTreeInternal(PathNode root, int level) + var newRoot = new PathNode(root.CommonPath, new List(), level, false); + while (index < root.Children.Count) { - var index = 0; - var prevCommonPath = ""; - - var newRoot = new PathNode(root.CommonPath, new List(), level, false); - while (index < root.Children.Count) + // item has common path with next item. + var currentCommonPath = ""; + if (index + 1 < root.Children.Count && + (currentCommonPath = GetCommonPath(new List() {root.Children[index].CommonPath, root.Children[index + 1].CommonPath})) != "") { - // item has common path with next item. - var currentCommonPath = ""; - if (index + 1 < root.Children.Count && - (currentCommonPath = GetCommonPath(new List() {root.Children[index].CommonPath, root.Children[index + 1].CommonPath})) != "") + var childNode = new PathNode(currentCommonPath, new List(), level + 1, false); + while (index < root.Children.Count && (prevCommonPath == "" || prevCommonPath == currentCommonPath)) { - var childNode = new PathNode(currentCommonPath, new List(), level + 1, false); - while (index < root.Children.Count && (prevCommonPath == "" || prevCommonPath == currentCommonPath)) - { - prevCommonPath = currentCommonPath; - childNode.Children.Add(new PathNode(root.Children[index].CommonPath.Substring(currentCommonPath.Length, root.Children[index].CommonPath.Length - currentCommonPath.Length), new List(), level + 2, false)); - - index++; - if (index < root.Children.Count) - { - currentCommonPath = GetCommonPath(new List() {currentCommonPath, root.Children[index].CommonPath}); - } - } + prevCommonPath = currentCommonPath; + childNode.Children.Add(new PathNode(root.Children[index].CommonPath.Substring(currentCommonPath.Length, root.Children[index].CommonPath.Length - currentCommonPath.Length), new List(), level + 2, false)); - prevCommonPath = ""; - newRoot.Children.Add(childNode); - } - else - { - // single leaf node - var childNode = root.Children[index]; - newRoot.Children.Add(childNode); index++; + if (index < root.Children.Count) + { + currentCommonPath = GetCommonPath(new List() {currentCommonPath, root.Children[index].CommonPath}); + } } - } - if (newRoot.Children.Count == 0) - { - newRoot.Leaf = true; + prevCommonPath = ""; + newRoot.Children.Add(childNode); } - - // build child nodes - for (var i = 0; i < newRoot.Children.Count; i++) + else { - newRoot.Children[i] = BuildPathTreeInternal(newRoot.Children[i], level + 1); + // single leaf node + var childNode = root.Children[index]; + newRoot.Children.Add(childNode); + index++; } - - return newRoot; } - public static string GetOperationIdPrefix(string operationId) + if (newRoot.Children.Count == 0) { - return operationId.Split("_")[0]; + newRoot.Leaf = true; } - public static string GetOperationIdAction(string operationId) + // build child nodes + for (var i = 0; i < newRoot.Children.Count; i++) { - var items = operationId.Split("_"); - return items.Length < 2 ? "" : items[1]; + newRoot.Children[i] = BuildPathTreeInternal(newRoot.Children[i], level + 1); } - public static string BuildDefinitionId(IEnumerable paths) - { - return $"{string.Join('-', paths).TrimStart('#')}"; - } + return newRoot; + } + + public static string GetOperationIdPrefix(string operationId) + { + return operationId.Split("_")[0]; + } - public static string GetDefinitionType(string Ref) + public static string GetOperationIdAction(string operationId) + { + var items = operationId.Split("_"); + return items.Length < 2 ? "" : items[1]; + } + + public static string BuildDefinitionId(IEnumerable paths) + { + return $"{string.Join('-', paths).TrimStart('#')}"; + } + + public static string GetDefinitionType(string Ref) + { + return Ref == null ? "" : Ref.Split("/").Last(); + } + + public static string GetRefDefinitionIdPath(string Ref) + { + if (Ref.Contains("parameters")) { - return Ref == null ? "" : Ref.Split("/").Last(); + return $"-Parameters-{GetDefinitionType(Ref)}"; } - public static string GetRefDefinitionIdPath(string Ref) + if (Ref.Contains("definitions")) { - if (Ref.Contains("parameters")) - { - return $"-Parameters-{GetDefinitionType(Ref)}"; - } - - if (Ref.Contains("definitions")) - { - return $"-Definitions-{GetDefinitionType(Ref)}"; - } - - return ""; + return $"-Definitions-{GetDefinitionType(Ref)}"; } + + return ""; } -} +} \ No newline at end of file diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParserTest/SchemaCacheTest.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParserTest/SchemaCacheTest.cs index f2a9d5e233d..2a6ebca594f 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParserTest/SchemaCacheTest.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParserTest/SchemaCacheTest.cs @@ -63,7 +63,6 @@ public async Task TestResolveErrorDetail() Assert.Equal("#/definitions/ErrorDetail", details?.items.Ref); Assert.Null(details?.items.properties); - - } + } diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParserTest/SwaggerAPIViewGeneratorTest.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParserTest/SwaggerAPIViewGeneratorTest.cs index 3da0e2717c0..5ed0a6858df 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParserTest/SwaggerAPIViewGeneratorTest.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParserTest/SwaggerAPIViewGeneratorTest.cs @@ -1,5 +1,3 @@ -using System.IO; -using System.Linq; using System.Threading.Tasks; using SwaggerApiParser; using Xunit; diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParserTest/SwaggerAPIViewTest.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParserTest/SwaggerAPIViewTest.cs index 42d4ec2a9ff..001043dedc5 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParserTest/SwaggerAPIViewTest.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParserTest/SwaggerAPIViewTest.cs @@ -103,7 +103,23 @@ public async Task TestDeviceUpdate() await using FileStream writer = File.Open(outputFilePath, FileMode.Create); await codeFile.SerializeAsync(writer); } - + + [Fact] + public async Task TestService() + { + const string deviceUpdatePath = "./fixtures/service.json"; + var serviceSwagger = await SwaggerDeserializer.Deserialize(deviceUpdatePath); + + SwaggerApiViewRoot root = new SwaggerApiViewRoot("Microsoft.Service", "Microsoft.Service"); + root.AddSwaggerSpec(serviceSwagger, Path.GetFullPath(deviceUpdatePath), "Microsoft.Service"); + + var codeFile = root.GenerateCodeFile(); + var outputFilePath = Path.GetFullPath("./service_codefile.json"); + this.output.WriteLine($"Write output to: {outputFilePath}"); + await using FileStream writer = File.Open(outputFilePath, FileMode.Create); + await codeFile.SerializeAsync(writer); + } + [Fact] public async Task TestDeviceUpdateSmall() { diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParserTest/TokenSerializerTest.cs b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParserTest/TokenSerializerTest.cs index 5148dee361e..21164b4ef89 100644 --- a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParserTest/TokenSerializerTest.cs +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParserTest/TokenSerializerTest.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Threading.Tasks; -using APIView; using SwaggerApiParser; using Xunit; using Xunit.Abstractions; diff --git a/tools/apiview/parsers/swagger-api-parser/SwaggerApiParserTest/fixtures/service.json b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParserTest/fixtures/service.json new file mode 100644 index 00000000000..62ba047d7b7 --- /dev/null +++ b/tools/apiview/parsers/swagger-api-parser/SwaggerApiParserTest/fixtures/service.json @@ -0,0 +1,2672 @@ +{ + "swagger": "2.0", + "info": { + "version": "2023-02-01-preview", + "title": "ProvisioningServiceClient", + "description": "API for service operations with the Azure IoT Hub Device Provisioning Service" + }, + "host": "your-dps.azure-devices-provisioning.net", + "schemes": [ + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/certificateAuthorities/{id}": { + "get": { + "tags": [ + "GET" + ], + "summary": "Get a certificate authority. This operation requires the certificateAuthorities/read permission.", + "operationId": "CertificateAuthority_Get", + "consumes": [], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The certificate authority id. A case-insensitive string (up to 128 characters long) of alphanumeric characters plus certain special characters : . _ -. No special characters allowed at start or end.", + "required": true, + "type": "string" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/CertificateAuthority" + }, + "headers": { + "ETag": { + "description": "The ETag of the resource to update.", + "type": "string" + } + } + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "CertificateAuthority_Get_MaximumSet_Gen": { + "$ref": "./examples/CertificateAuthority_Get_MaximumSet_Gen.json" + }, + "CertificateAuthority_Get_MinimumSet_Gen": { + "$ref": "./examples/CertificateAuthority_Get_MinimumSet_Gen.json" + } + } + }, + "put": { + "tags": [ + "PUT" + ], + "summary": "Create or replace a certificate authority with the specified certificate authority source type. This operation requires the certificateAuthorities/write permission.", + "operationId": "CertificateAuthority_CreateOrUpdate", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The desired certificate authority name. A case-insensitive string (up to 128 characters long) of alphanumeric characters plus certain special characters : . _ -. No special characters allowed at start or end.", + "required": true, + "type": "string" + }, + { + "name": "certificateAuthority", + "in": "body", + "description": "The created certificate authority object.", + "required": true, + "schema": { + "$ref": "#/definitions/CertificateAuthority" + } + }, + { + "name": "If-Match", + "in": "header", + "description": "The ETag of the certificate authority.", + "required": false, + "type": "string" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/CertificateAuthority" + } + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "CertificateAuthority_CreateOrUpdate_MaximumSet_Gen": { + "$ref": "./examples/CertificateAuthority_CreateOrUpdate_MaximumSet_Gen.json" + }, + "CertificateAuthority_CreateOrUpdate_MinimumSet_Gen": { + "$ref": "./examples/CertificateAuthority_CreateOrUpdate_MinimumSet_Gen.json" + } + } + }, + "delete": { + "tags": [ + "DELETE" + ], + "summary": "Delete the certificate authority. This operation requires the certificateAuthorities/delete permission.", + "operationId": "CertificateAuthority_Delete", + "consumes": [], + "produces": [], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The certificate authority name. A case-insensitive string (up to 128 characters long) of alphanumeric characters plus certain special characters : . _ -. No special characters allowed at start or end.", + "required": true, + "type": "string" + }, + { + "name": "If-Match", + "in": "header", + "description": "The ETag of the certificate authority.", + "required": true, + "type": "string" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "204": { + "description": "Success" + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "CertificateAuthority_Delete_MaximumSet_Gen": { + "$ref": "./examples/CertificateAuthority_Delete_MaximumSet_Gen.json" + }, + "CertificateAuthority_Delete_MinimumSet_Gen": { + "$ref": "./examples/CertificateAuthority_Delete_MinimumSet_Gen.json" + } + } + } + }, + "/enrollments/{id}": { + "get": { + "tags": [ + "GET", + "IndividualEnrollment" + ], + "summary": "Get a device enrollment record.", + "operationId": "IndividualEnrollment_Get", + "consumes": [], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "This id is used to uniquely identify a device registration of an enrollment. A case-insensitive string (up to 128 characters long) of alphanumeric characters plus certain special characters : . _ -. No special characters allowed at start or end.", + "required": true, + "type": "string" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/IndividualEnrollment" + } + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "IndividualEnrollment_Get_MaximumSet_Gen": { + "$ref": "./examples/IndividualEnrollment_Get_MaximumSet_Gen.json" + }, + "IndividualEnrollment_Get_MinimumSet_Gen": { + "$ref": "./examples/IndividualEnrollment_Get_MinimumSet_Gen.json" + } + } + }, + "put": { + "tags": [ + "PUT", + "IndividualEnrollment" + ], + "summary": "Create or update a device enrollment record.", + "operationId": "IndividualEnrollment_CreateOrUpdate", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "This id is used to uniquely identify a device registration of an enrollment. A case-insensitive string (up to 128 characters long) of alphanumeric characters plus certain special characters : . _ -. No special characters allowed at start or end.", + "required": true, + "type": "string" + }, + { + "name": "enrollment", + "in": "body", + "description": "The device enrollment record.", + "required": true, + "schema": { + "$ref": "#/definitions/IndividualEnrollment" + } + }, + { + "name": "If-Match", + "in": "header", + "description": "The ETag of the enrollment record.", + "required": false, + "type": "string" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/IndividualEnrollment" + } + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "IndividualEnrollment_CreateOrUpdate_MaximumSet_Gen": { + "$ref": "./examples/IndividualEnrollment_CreateOrUpdate_MaximumSet_Gen.json" + }, + "IndividualEnrollment_CreateOrUpdate_MinimumSet_Gen": { + "$ref": "./examples/IndividualEnrollment_CreateOrUpdate_MinimumSet_Gen.json" + } + } + }, + "delete": { + "tags": [ + "DELETE", + "IndividualEnrollment" + ], + "summary": "Delete a device enrollment record.", + "operationId": "IndividualEnrollment_Delete", + "consumes": [], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "This id is used to uniquely identify a device registration of an enrollment. A case-insensitive string (up to 128 characters long) of alphanumeric characters plus certain special characters : . _ -. No special characters allowed at start or end.", + "required": true, + "type": "string" + }, + { + "name": "If-Match", + "in": "header", + "description": "The ETag of the enrollment record.", + "required": false, + "type": "string" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "204": { + "description": "Success" + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "IndividualEnrollment_Delete_MaximumSet_Gen": { + "$ref": "./examples/IndividualEnrollment_Delete_MaximumSet_Gen.json" + }, + "IndividualEnrollment_Delete_MinimumSet_Gen": { + "$ref": "./examples/IndividualEnrollment_Delete_MinimumSet_Gen.json" + } + } + } + }, + "/enrollmentGroups/{id}": { + "get": { + "tags": [ + "GET", + "EnrollmentGroup" + ], + "summary": "Get a device enrollment group.", + "operationId": "EnrollmentGroup_Get", + "consumes": [], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Enrollment group ID.", + "required": true, + "type": "string" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/EnrollmentGroup" + } + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "EnrollmentGroup_Get_MaximumSet_Gen": { + "$ref": "./examples/EnrollmentGroup_Get_MaximumSet_Gen.json" + }, + "EnrollmentGroup_Get_MinimumSet_Gen": { + "$ref": "./examples/EnrollmentGroup_Get_MinimumSet_Gen.json" + } + } + }, + "put": { + "tags": [ + "PUT", + "EnrollmentGroup" + ], + "summary": "Create or update a device enrollment group.", + "operationId": "EnrollmentGroup_CreateOrUpdate", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Enrollment group ID.", + "required": true, + "type": "string" + }, + { + "name": "enrollmentGroup", + "in": "body", + "description": "The device enrollment group.", + "required": true, + "schema": { + "$ref": "#/definitions/EnrollmentGroup" + } + }, + { + "name": "If-Match", + "in": "header", + "description": "The ETag of the enrollment record.", + "required": false, + "type": "string" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/EnrollmentGroup" + } + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "EnrollmentGroup_CreateOrUpdate_MaximumSet_Gen": { + "$ref": "./examples/EnrollmentGroup_CreateOrUpdate_MaximumSet_Gen.json" + }, + "EnrollmentGroup_CreateOrUpdate_MinimumSet_Gen": { + "$ref": "./examples/EnrollmentGroup_CreateOrUpdate_MinimumSet_Gen.json" + } + } + }, + "delete": { + "tags": [ + "DELETE", + "EnrollmentGroup" + ], + "summary": "Delete a device enrollment group.", + "operationId": "EnrollmentGroup_Delete", + "consumes": [], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Enrollment group ID.", + "required": true, + "type": "string" + }, + { + "name": "If-Match", + "in": "header", + "description": "The ETag of the enrollment group record.", + "required": false, + "type": "string" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "204": { + "description": "Success" + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "EnrollmentGroup_Delete_MaximumSet_Gen": { + "$ref": "./examples/EnrollmentGroup_Delete_MaximumSet_Gen.json" + }, + "EnrollmentGroup_Delete_MinimumSet_Gen": { + "$ref": "./examples/EnrollmentGroup_Delete_MinimumSet_Gen.json" + } + } + } + }, + "/registrations/{id}": { + "get": { + "tags": [ + "GET", + "DeviceRegistrationState" + ], + "summary": "Gets the device registration state.", + "operationId": "DeviceRegistrationState_Get", + "consumes": [], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Registration ID.", + "required": true, + "type": "string" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/DeviceRegistrationState" + } + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "DeviceRegistrationState_Get_MaximumSet_Gen": { + "$ref": "./examples/DeviceRegistrationState_Get_MaximumSet_Gen.json" + }, + "DeviceRegistrationState_Get_MinimumSet_Gen": { + "$ref": "./examples/DeviceRegistrationState_Get_MinimumSet_Gen.json" + } + } + }, + "delete": { + "tags": [ + "DELETE", + "DeviceRegistrationState" + ], + "summary": "Deletes the device registration", + "operationId": "DeviceRegistrationState_Delete", + "consumes": [], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Registration ID.", + "required": true, + "type": "string" + }, + { + "name": "If-Match", + "in": "header", + "description": "The ETag of the registration status record.", + "required": false, + "type": "string" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "204": { + "description": "Success" + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "DeviceRegistrationState_Delete_MaximumSet_Gen": { + "$ref": "./examples/DeviceRegistrationState_Delete_MaximumSet_Gen.json" + }, + "DeviceRegistrationState_Delete_MinimumSet_Gen": { + "$ref": "./examples/DeviceRegistrationState_Delete_MinimumSet_Gen.json" + } + } + } + }, + "/trustBundles/{id}": { + "get": { + "tags": [ + "GET", + "TrustBundle" + ], + "summary": "Get a specific trust bundle and it contents.", + "operationId": "TrustBundle_Get", + "consumes": [], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The trust bundle id. A case-insensitive string (up to 128 characters long) of alphanumeric characters plus certain special characters : . _ -. No special characters allowed at start or end.", + "required": true, + "type": "string" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/TrustBundle" + } + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "TrustBundle_Get_MaximumSet_Gen": { + "$ref": "./examples/TrustBundle_Get_MaximumSet_Gen.json" + }, + "TrustBundle_Get_MinimumSet_Gen": { + "$ref": "./examples/TrustBundle_Get_MinimumSet_Gen.json" + } + } + }, + "put": { + "tags": [ + "PUT", + "TrustBundle" + ], + "summary": "Create or update a trust bundle.", + "operationId": "TrustBundle_CreateOrUpdate", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The trust bundle id. A case-insensitive string (up to 128 characters long) of alphanumeric characters plus certain special characters : . _ -. No special characters allowed at start or end.", + "required": true, + "type": "string" + }, + { + "name": "trustBundle", + "in": "body", + "description": "The trust bundle.", + "required": true, + "schema": { + "$ref": "#/definitions/TrustBundle" + } + }, + { + "name": "If-Match", + "in": "header", + "description": "The ETag of the trust bundle.", + "required": false, + "type": "string" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/TrustBundle" + } + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "TrustBundle_CreateOrUpdate_MaximumSet_Gen": { + "$ref": "./examples/TrustBundle_CreateOrUpdate_MaximumSet_Gen.json" + }, + "TrustBundle_CreateOrUpdate_MinimumSet_Gen": { + "$ref": "./examples/TrustBundle_CreateOrUpdate_MinimumSet_Gen.json" + } + } + }, + "delete": { + "tags": [ + "DELETE" + ], + "summary": "Delete the trust bundle. This operation requires the trustBundle/delete permission.", + "operationId": "TrustBundle_Delete", + "consumes": [], + "produces": [], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The trust bundle id. A case-insensitive string (up to 128 characters long) of alphanumeric characters plus certain special characters : . _ -. No special characters allowed at start or end.", + "required": true, + "type": "string" + }, + { + "name": "If-Match", + "in": "header", + "description": "The ETag of the trust bundle record.", + "required": true, + "type": "string" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "204": { + "description": "Success" + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "TrustBundle_Delete_MaximumSet_Gen": { + "$ref": "./examples/TrustBundle_Delete_MaximumSet_Gen.json" + }, + "TrustBundle_Delete_MinimumSet_Gen": { + "$ref": "./examples/TrustBundle_Delete_MinimumSet_Gen.json" + } + } + } + }, + "/certificateAuthorities/query": { + "post": { + "tags": [ + "POST" + ], + "summary": "Retrieves a list of the certificate authorities and a continuation token to retrieve the next page. This operation requires the certificateAuthorities/read permission.", + "operationId": "CertificateAuthority_Query", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "x-ms-max-item-count", + "in": "header", + "description": "Page size", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "x-ms-continuation", + "in": "header", + "description": "Continuation token", + "required": false, + "type": "string" + }, + { + "name": "querySpecification", + "in": "body", + "description": "The query specification.", + "required": true, + "schema": { + "$ref": "#/definitions/QuerySpecification" + } + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/CertificateAuthority" + } + }, + "headers": { + "x-ms-continuation": { + "description": "The continuation token for getting the next page of results.", + "type": "string" + }, + "x-ms-max-item-count": { + "description": "The maximum number of items returned in a page.", + "type": "integer" + }, + "x-ms-item-type": { + "description": "The list of values returned from the query.", + "type": "string" + } + } + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "CertificateAuthority_Query_MaximumSet_Gen": { + "$ref": "./examples/CertificateAuthority_Query_MaximumSet_Gen.json" + }, + "CertificateAuthority_Query_MinimumSet_Gen": { + "$ref": "./examples/CertificateAuthority_Query_MinimumSet_Gen.json" + } + } + } + }, + "/enrollments/query": { + "post": { + "tags": [ + "POST", + "IndividualEnrollment" + ], + "summary": "Query the device enrollment records.", + "operationId": "IndividualEnrollment_Query", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "x-ms-max-item-count", + "in": "header", + "description": "Page size", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "x-ms-continuation", + "in": "header", + "description": "Continuation token", + "required": false, + "type": "string" + }, + { + "name": "querySpecification", + "in": "body", + "description": "The query specification.", + "required": true, + "schema": { + "$ref": "#/definitions/QuerySpecification" + } + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/IndividualEnrollment" + } + }, + "headers": { + "x-ms-continuation": { + "description": "The continuation token for getting the next page of results.", + "type": "string" + }, + "x-ms-max-item-count": { + "description": "The maximum number of items returned in a page.", + "type": "integer" + }, + "x-ms-item-type": { + "description": "The list of values returned from the query.", + "type": "string" + } + } + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "IndividualEnrollment_Query_MaximumSet_Gen": { + "$ref": "./examples/IndividualEnrollment_Query_MaximumSet_Gen.json" + }, + "IndividualEnrollment_Query_MinimumSet_Gen": { + "$ref": "./examples/IndividualEnrollment_Query_MinimumSet_Gen.json" + } + } + } + }, + "/enrollments/{id}/attestationmechanism": { + "post": { + "tags": [ + "POST", + "IndividualEnrollment" + ], + "summary": "Get the attestation mechanism in the device enrollment record.", + "operationId": "IndividualEnrollment_GetAttestationMechanism", + "consumes": [], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "This id is used to uniquely identify a device registration of an enrollment. A case-insensitive string (up to 128 characters long) of alphanumeric characters plus certain special characters : . _ -. No special characters allowed at start or end.", + "required": true, + "type": "string" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/AttestationMechanism" + } + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "IndividualEnrollment_GetAttestationMechanism_MaximumSet_Gen": { + "$ref": "./examples/IndividualEnrollment_GetAttestationMechanism_MaximumSet_Gen.json" + }, + "IndividualEnrollment_GetAttestationMechanism_MinimumSet_Gen": { + "$ref": "./examples/IndividualEnrollment_GetAttestationMechanism_MinimumSet_Gen.json" + } + } + } + }, + "/enrollments": { + "post": { + "tags": [ + "POST", + "IndividualEnrollment" + ], + "summary": "Bulk device enrollment operation with maximum of 10 enrollments.", + "operationId": "IndividualEnrollment_RunBulkOperation", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "bulkOperation", + "in": "body", + "description": "Bulk operation.", + "required": true, + "schema": { + "$ref": "#/definitions/BulkEnrollmentOperation" + } + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/BulkEnrollmentOperationResult" + } + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "IndividualEnrollment_RunBulkOperation_MaximumSet_Gen": { + "$ref": "./examples/IndividualEnrollment_RunBulkOperation_MaximumSet_Gen.json" + }, + "IndividualEnrollment_RunBulkOperation_MinimumSet_Gen": { + "$ref": "./examples/IndividualEnrollment_RunBulkOperation_MinimumSet_Gen.json" + } + } + } + }, + "/enrollmentGroups/query": { + "post": { + "tags": [ + "POST", + "EnrollmentGroup" + ], + "summary": "Query the device enrollment groups.", + "operationId": "EnrollmentGroup_Query", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "x-ms-max-item-count", + "in": "header", + "description": "Page size", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "x-ms-continuation", + "in": "header", + "description": "Continuation token", + "required": false, + "type": "string" + }, + { + "name": "querySpecification", + "in": "body", + "description": "The query specification.", + "required": true, + "schema": { + "$ref": "#/definitions/QuerySpecification" + } + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/EnrollmentGroup" + } + }, + "headers": { + "x-ms-continuation": { + "description": "The continuation token for getting the next page of results.", + "type": "string" + }, + "x-ms-max-item-count": { + "description": "The maximum number of items returned in a page.", + "type": "integer" + }, + "x-ms-item-type": { + "description": "The list of values returned from the query.", + "type": "string" + } + } + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "EnrollmentGroup_Query_MaximumSet_Gen": { + "$ref": "./examples/EnrollmentGroup_Query_MaximumSet_Gen.json" + }, + "EnrollmentGroup_Query_MinimumSet_Gen": { + "$ref": "./examples/EnrollmentGroup_Query_MinimumSet_Gen.json" + } + } + } + }, + "/enrollmentGroups/{id}/attestationmechanism": { + "post": { + "tags": [ + "POST", + "EnrollmentGroup" + ], + "summary": "Get the attestation mechanism in the device enrollment group record.", + "operationId": "EnrollmentGroup_GetAttestationMechanism", + "consumes": [], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Enrollment group ID", + "required": true, + "type": "string" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/AttestationMechanism" + } + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "EnrollmentGroup_GetAttestationMechanism_MaximumSet_Gen": { + "$ref": "./examples/EnrollmentGroup_GetAttestationMechanism_MaximumSet_Gen.json" + }, + "EnrollmentGroup_GetAttestationMechanism_MinimumSet_Gen": { + "$ref": "./examples/EnrollmentGroup_GetAttestationMechanism_MinimumSet_Gen.json" + } + } + } + }, + "/enrollmentGroups": { + "post": { + "tags": [ + "POST", + "EnrollmentGroup" + ], + "summary": "Bulk device enrollment group operation with maximum of 10 groups.", + "operationId": "EnrollmentGroup_RunBulkOperation", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "bulkOperation", + "in": "body", + "description": "Bulk operation.", + "required": true, + "schema": { + "$ref": "#/definitions/BulkEnrollmentGroupOperation" + } + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/BulkEnrollmentGroupOperationResult" + } + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "EnrollmentGroup_RunBulkOperation_MaximumSet_Gen": { + "$ref": "./examples/EnrollmentGroup_RunBulkOperation_MaximumSet_Gen.json" + }, + "EnrollmentGroup_RunBulkOperation_MinimumSet_Gen": { + "$ref": "./examples/EnrollmentGroup_RunBulkOperation_MinimumSet_Gen.json" + } + } + } + }, + "/registrations/{id}/query": { + "post": { + "tags": [ + "POST", + "DeviceRegistrationState" + ], + "summary": "Gets the registration state of devices in this enrollmentGroup.", + "operationId": "DeviceRegistrationState_Query", + "consumes": [], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Enrollment group ID.", + "required": true, + "type": "string" + }, + { + "name": "x-ms-max-item-count", + "in": "header", + "description": "pageSize", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "x-ms-continuation", + "in": "header", + "description": "continuation token", + "required": false, + "type": "string" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/DeviceRegistrationState" + } + }, + "headers": { + "x-ms-continuation": { + "description": "The continuation token for getting the next page of results.", + "type": "string" + }, + "x-ms-max-item-count": { + "description": "The maximum number of items returned in a page.", + "type": "integer" + }, + "x-ms-item-type": { + "description": "The list of values returned from the query.", + "type": "string" + } + } + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "DeviceRegistrationState_Query_MaximumSet_Gen": { + "$ref": "./examples/DeviceRegistrationState_Query_MaximumSet_Gen.json" + }, + "DeviceRegistrationState_Query_MinimumSet_Gen": { + "$ref": "./examples/DeviceRegistrationState_Query_MinimumSet_Gen.json" + } + } + } + }, + "/trustBundles/query": { + "post": { + "tags": [ + "POST", + "TrustBundle" + ], + "summary": "Query the trust bundles in a DPS instance.", + "operationId": "TrustBundle_Query", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "x-ms-max-item-count", + "in": "header", + "description": "Page size", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "x-ms-continuation", + "in": "header", + "description": "Continuation token", + "required": false, + "type": "string" + }, + { + "name": "withCertificateData", + "in": "query", + "description": "Flag to indicate if X509 certificate data is required or not", + "required": false, + "type": "boolean" + }, + { + "name": "querySpecification", + "in": "body", + "description": "The query specification.", + "required": true, + "schema": { + "$ref": "#/definitions/QuerySpecification" + } + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/TrustBundle" + } + }, + "headers": { + "x-ms-continuation": { + "description": "The continuation token for getting the next page of results.", + "type": "string" + }, + "x-ms-max-item-count": { + "description": "The maximum number of items returned in a page.", + "type": "integer" + }, + "x-ms-item-type": { + "description": "The list of values returned from the query.", + "type": "string" + } + } + }, + "default": { + "description": "Error response", + "schema": { + "$ref": "#/definitions/ProvisioningServiceErrorDetails" + }, + "headers": { + "x-ms-error-code": { + "description": "The error code for specific error that occurred.", + "type": "string" + } + } + } + }, + "x-ms-examples": { + "TrustBundle_Query_MaximumSet_Gen": { + "$ref": "./examples/TrustBundle_Query_MaximumSet_Gen.json" + }, + "TrustBundle_Query_MinimumSet_Gen": { + "$ref": "./examples/TrustBundle_Query_MinimumSet_Gen.json" + } + } + } + } + }, + "definitions": { + "ProvisioningServiceErrorDetails": { + "description": "Contains the properties of an error returned by the Azure IoT Hub Provisioning Service.", + "type": "object", + "properties": { + "errorCode": { + "format": "int32", + "type": "integer" + }, + "trackingId": { + "type": "string" + }, + "message": { + "type": "string" + }, + "info": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "timestampUtc": { + "format": "date-time", + "type": "string" + } + } + }, + "CertificateAuthority": { + "required": [ + "certificateAuthorityType" + ], + "type": "object", + "properties": { + "id": { + "description": "The certificate authority identifier. A case-insensitive string (up to 64 characters long) of ASCII 7-bit alphanumeric\r\ncharacters plus certain special characters . _ -. No special characters allowed at start or end.", + "type": "string", + "readOnly": true + }, + "createdDateTimeUtc": { + "format": "date-time", + "description": "The DateTime this resource was created in UTC.", + "type": "string", + "readOnly": true + }, + "lastUpdatedDateTimeUtc": { + "format": "date-time", + "description": "The DateTime this resource was last updated in UTC.", + "type": "string", + "readOnly": true + }, + "etag": { + "description": "The ETag of the device record.", + "type": "string", + "readOnly": true + }, + "certificates": { + "description": "The certificates affiliated with this Certificate Authority.", + "type": "array", + "items": { + "$ref": "#/definitions/CertificateWithMetadata" + }, + "readOnly": true + }, + "certificateAuthorityType": { + "type": "string" + } + }, + "discriminator": "certificateAuthorityType" + }, + "CertificateWithMetadata": { + "type": "object", + "properties": { + "certificateMetadata": { + "$ref": "#/definitions/CertificateMetadata", + "readOnly": true + }, + "certificate": { + "type": "string" + } + } + }, + "CertificateMetadata": { + "type": "object", + "properties": { + "subjectName": { + "type": "string", + "readOnly": true + }, + "sha1Thumbprint": { + "type": "string", + "readOnly": true + }, + "sha256Thumbprint": { + "type": "string", + "readOnly": true + }, + "issuerName": { + "type": "string", + "readOnly": true + }, + "notBeforeUtc": { + "format": "date-time", + "type": "string", + "readOnly": true + }, + "notAfterUtc": { + "format": "date-time", + "type": "string", + "readOnly": true + }, + "serialNumber": { + "type": "string", + "readOnly": true + }, + "version": { + "format": "int32", + "type": "integer", + "readOnly": true + } + } + }, + "IndividualEnrollment": { + "description": "The device enrollment record.", + "required": [ + "registrationId", + "attestation" + ], + "type": "object", + "properties": { + "registrationId": { + "description": "This id is used to uniquely identify a device registration of an enrollment.\r\nA case-insensitive string (up to 128 characters long) of alphanumeric characters plus certain special characters : . _ -. No special characters allowed at start or end.", + "type": "string" + }, + "deviceId": { + "description": "Desired IoT Hub device ID (optional).", + "type": "string" + }, + "registrationState": { + "$ref": "#/definitions/DeviceRegistrationState", + "description": "Current registration status.", + "readOnly": true + }, + "optionalDeviceInformation": { + "$ref": "#/definitions/TwinCollection", + "description": "Optional Device Information." + }, + "attestation": { + "$ref": "#/definitions/AttestationMechanism", + "description": "Attestation method used by the device." + }, + "deviceHostname": { + "description": "Hostname of the device (optional). Will be lower-cased.\r\nMust adhere to the domain name spec: https://datatracker.ietf.org/doc/html/rfc1035.", + "type": "string" + }, + "deviceIpAddress": { + "description": "IP address of the device (optional).\r\nThis can be an IPv4 or IPv6 address.", + "type": "string" + }, + "etag": { + "description": "The entity tag associated with the resource.", + "type": "string" + }, + "provisioningStatus": { + "description": "The provisioning status.", + "default": "enabled", + "enum": [ + "enabled", + "disabled" + ], + "type": "string" + }, + "createdDateTimeUtc": { + "format": "date-time", + "description": "The DateTime this resource was created.", + "type": "string", + "readOnly": true + }, + "lastUpdatedDateTimeUtc": { + "format": "date-time", + "description": "The DateTime this resource was last updated.", + "type": "string", + "readOnly": true + }, + "provisioningSettings": { + "$ref": "#/definitions/ProvisioningSettings", + "description": "The Enrollment Provisioning Settings." + }, + "trustBundleId": { + "description": "Optional trust bundle id to associate with the enrollment.", + "type": "string" + }, + "clientCertificateIssuancePolicy": { + "$ref": "#/definitions/CertificateIssuancePolicy", + "description": "Certificate issuance policy for device client certificates." + }, + "serverCertificateIssuancePolicy": { + "$ref": "#/definitions/CertificateIssuancePolicy", + "description": "Certificate issuance policy for device server certificates." + } + } + }, + "DeviceRegistrationState": { + "description": "Device registration state.", + "type": "object", + "properties": { + "registrationId": { + "description": "This id is used to uniquely identify a device registration of an enrollment.\r\nA case-insensitive string (up to 128 characters long) of alphanumeric characters plus certain special characters : . _ -. No special characters allowed at start or end.", + "type": "string", + "readOnly": true + }, + "createdDateTimeUtc": { + "format": "date-time", + "description": "Registration create date time (in UTC).", + "type": "string", + "readOnly": true + }, + "assignedEndpoint": { + "$ref": "#/definitions/AssignedEndpoint", + "description": "Assigned Endpoint.", + "readOnly": true + }, + "deviceId": { + "description": "Device ID.", + "type": "string", + "readOnly": true + }, + "status": { + "description": "Enrollment status.", + "enum": [ + "unassigned", + "assigning", + "assigned", + "failed", + "disabled" + ], + "type": "string", + "readOnly": true + }, + "substatus": { + "description": "Substatus for 'Assigned' devices. Possible values include - 'initialAssignment': Device has been assigned to an IoT hub for the first time, 'deviceDataMigrated': Device has been assigned to a different IoT hub and its device data was migrated from the previously assigned IoT hub. Device data was removed from the previously assigned IoT hub, 'deviceDataReset': Device has been assigned to a different IoT hub and its device data was populated from the initial state stored in the enrollment. Device data was removed from the previously assigned IoT hub, 'reprovisionedToInitialAssignment': Device has been re-provisioned to a previously assigned IoT hub.", + "enum": [ + "initialAssignment", + "deviceDataMigrated", + "deviceDataReset", + "reprovisionedToInitialAssignment" + ], + "type": "string", + "readOnly": true + }, + "errorCode": { + "format": "int32", + "description": "Error code.", + "type": "integer", + "readOnly": true + }, + "errorMessage": { + "description": "Error message.", + "type": "string", + "readOnly": true + }, + "lastUpdatedDateTimeUtc": { + "format": "date-time", + "description": "Last updated date time (in UTC).", + "type": "string", + "readOnly": true + }, + "etag": { + "description": "The entity tag associated with the resource.", + "type": "string", + "readOnly": true + }, + "payload": { + "description": "Custom allocation payload returned from the webhook to the device.", + "type": "object", + "readOnly": true + }, + "trustBundle": { + "$ref": "#/definitions/TrustBundle", + "description": "The optional trust bundle result returned after a successful device registration.", + "readOnly": true + }, + "issuedClientCertificate": { + "description": "Client certificate issued to the device in PEM format.", + "type": "string", + "readOnly": true + }, + "deviceCertificateIssuanceSettings": { + "$ref": "#/definitions/DeviceCertificateIssuanceSettings", + "description": "Certificate issuance settings specification for the device.", + "readOnly": true + }, + "deviceHostname": { + "description": "Optional configured hostname for the device.", + "type": "string", + "readOnly": true + }, + "deviceIpAddress": { + "description": "Optional configured IP address for the device.", + "type": "string", + "readOnly": true + } + } + }, + "AttestationMechanism": { + "description": "Attestation mechanism for individualEnrollment as well as enrollmentGroup.", + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "description": "Attestation Type.", + "enum": [ + "none", + "tpm", + "x509", + "symmetricKey" + ], + "type": "string" + }, + "tpm": { + "$ref": "#/definitions/TpmAttestation", + "description": "TPM attestation method." + }, + "x509": { + "$ref": "#/definitions/X509Attestation", + "description": "X509 attestation method." + }, + "symmetricKey": { + "$ref": "#/definitions/SymmetricKeyAttestation", + "description": "Symmetric Key attestation method." + } + } + }, + "AllocationSettings": { + "description": "Enrollment Allocation Settings.", + "type": "object", + "properties": { + "allocationPolicy": { + "description": "The allocation policy of this resource. This policy overrides the tenant level allocation policy for this individual enrollment or enrollment group. Possible values include 'hashed': Linked endpoints are equally likely to have devices provisioned to them, 'geoLatency': Devices are provisioned to an endpoint with the lowest latency to the device.If multiple linked endpoints would provide the same lowest latency, the provisioning service hashes devices across those endpoints, 'static' : Specification of the desired endpoint in the enrollment list takes priority over the service-level allocation policy, 'custom': Devices are provisioned to an endpoint based on your own custom logic. The provisioning service passes information about the device to the logic, and the logic returns the desired endpoint as well as the desired initial configuration. We recommend using Azure Functions to host your logic.", + "enum": [ + "hashed", + "geoLatency", + "static", + "custom" + ], + "type": "string" + }, + "endpoints": { + "description": "The list of endpoints the device(s) in this resource can be allocated to. Must be a subset of tenant level list of endpoints.", + "type": "array", + "items": { + "$ref": "#/definitions/EndpointDescription" + } + }, + "customAllocationDefinition": { + "$ref": "#/definitions/CustomAllocationDefinition", + "description": "This tells DPS which webhook to call when using custom allocation." + } + } + }, + "DeviceCapabilities": { + "description": "Device capabilities.", + "required": [ + "iotEdge" + ], + "type": "object", + "properties": { + "iotEdge": { + "description": "If set to true, this device is an IoTEdge device.", + "default": false, + "type": "boolean" + } + } + }, + "InitialTwin": { + "description": "Initial device twin. Contains a subset of the properties of Twin.", + "type": "object", + "properties": { + "tags": { + "$ref": "#/definitions/TwinCollection", + "description": "Twin tags." + }, + "properties": { + "$ref": "#/definitions/InitialTwinProperties", + "description": "Twin desired properties." + } + } + }, + "ReprovisionPolicy": { + "description": "The behavior of the service when a device is re-provisioned to an endpoint.", + "required": [ + "migrateDeviceData", + "updateEndpointAssignment" + ], + "type": "object", + "properties": { + "migrateDeviceData": { + "description": "When set to true (default), the Device Provisioning Service will migrate the device's data (twin, device capabilities, and device ID) from one endpoint to another during an endpoint assignment update. If set to false, the Device Provisioning Service will reset the device's data to the initial desired configuration stored in the corresponding enrollment list.", + "default": true, + "type": "boolean" + }, + "updateEndpointAssignment": { + "description": "When set to true (default), the Device Provisioning Service will evaluate the device's endpoint assignment and update it if necessary for any provisioning requests beyond the first from a given device. If set to false, the device will stay assigned to its current endpoint.", + "default": true, + "type": "boolean" + } + } + }, + "CustomAllocationDefinition": { + "description": "This tells DPS which webhook to call when using custom allocation.", + "required": [ + "webhookUrl", + "apiVersion" + ], + "type": "object", + "properties": { + "webhookUrl": { + "description": "The webhook URL used for allocation requests.", + "type": "string" + }, + "apiVersion": { + "description": "The API version of the provisioning service types (such as IndividualEnrollment) sent in the custom allocation request. Minimum supported version: \"2018-09-01-preview\".", + "type": "string" + } + } + }, + "CertificateIssuancePolicy": { + "description": "Certificate issuance policy.", + "type": "object", + "properties": { + "certificateAuthorityName": { + "description": "Certificate authority name used to issue certificates.", + "type": "string" + } + } + }, + "TrustBundle": { + "description": "A collection of trusted root or intermediate certificates associated with one or more device enrollments.", + "required": [ + "certificates" + ], + "type": "object", + "properties": { + "certificates": { + "description": "The certificates in the trust bundle.", + "type": "array", + "items": { + "$ref": "#/definitions/X509CertificateWithMetadata" + } + }, + "id": { + "description": "The trust bundle ID. A case-insensitive string (up to 128 characters long) of alphanumeric characters plus certain special characters : . _ -. No special characters allowed at start or end.", + "type": "string", + "readOnly": true + }, + "createdDateTime": { + "format": "date-time", + "description": "The DateTime this resource was created in UTC.", + "type": "string", + "readOnly": true + }, + "lastModifiedDateTime": { + "format": "date-time", + "description": "The DateTime this resource was last updated in UTC.", + "type": "string", + "readOnly": true + }, + "etag": { + "description": "The ETag of the trust bundle.", + "type": "string", + "readOnly": true + } + } + }, + "DeviceCertificateIssuanceSettings": { + "description": "Certificate issuance policy.", + "type": "object", + "properties": { + "certificateIssuanceEndpoint": { + "description": "Certificate issuance endpoint which devices can access", + "type": "string", + "readOnly": true + }, + "certificateType": { + "description": "The certificate issuance type.", + "enum": [ + "none", + "serverCertificate" + ], + "type": "string", + "readOnly": true + } + } + }, + "TpmAttestation": { + "description": "Attestation via TPM.", + "required": [ + "endorsementKey" + ], + "type": "object", + "properties": { + "endorsementKey": { + "type": "string" + }, + "storageRootKey": { + "type": "string" + } + } + }, + "X509Attestation": { + "description": "Attestation via X509.", + "type": "object", + "properties": { + "clientCertificates": { + "$ref": "#/definitions/X509Certificates" + }, + "signingCertificates": { + "$ref": "#/definitions/X509Certificates" + }, + "caReferences": { + "$ref": "#/definitions/X509CAReferences" + } + } + }, + "SymmetricKeyAttestation": { + "description": "Attestation via SymmetricKey.", + "type": "object", + "properties": { + "primaryKey": { + "description": "Primary symmetric key.", + "type": "string" + }, + "secondaryKey": { + "description": "Secondary symmetric key.", + "type": "string" + } + } + }, + "InitialTwinProperties": { + "description": "Represents the initial properties that will be set on the device twin.", + "type": "object", + "properties": { + "desired": { + "$ref": "#/definitions/TwinCollection", + "description": "Gets and sets the InitialTwin desired properties." + } + } + }, + "X509CertificateWithMetadata": { + "required": [ + "certificate" + ], + "type": "object", + "properties": { + "certificate": { + "description": "Certificates in PEM format enclosed within these headers:\r\n-----BEGIN CERTIFICATE-----\\r\\n\r\n-----END CERTIFICATE-----\\r\\n", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/X509CertificateMetadata", + "description": "The certificate information.", + "readOnly": true + } + } + }, + "X509Certificates": { + "description": "Primary and secondary certificates", + "type": "object", + "properties": { + "primary": { + "$ref": "#/definitions/X509CertificateWithInfo" + }, + "secondary": { + "$ref": "#/definitions/X509CertificateWithInfo" + } + } + }, + "X509CAReferences": { + "description": "Primary and secondary CA references.", + "type": "object", + "properties": { + "primary": { + "type": "string" + }, + "secondary": { + "type": "string" + } + } + }, + "X509CertificateMetadata": { + "type": "object", + "properties": { + "subjectName": { + "description": "Distinguished name from the certificate.", + "type": "string", + "readOnly": true + }, + "sha1Thumbprint": { + "description": "SHA-1 hash value of the certificate as a hexadecimal string.", + "type": "string", + "readOnly": true + }, + "sha256Thumbprint": { + "description": "SHA-256 hash value of the certificate as a hexadecimal string.", + "type": "string", + "readOnly": true + }, + "issuerName": { + "description": "Issuer distinguished name.", + "type": "string", + "readOnly": true + }, + "notBeforeUtc": { + "format": "date-time", + "description": "The date on which the certificate becomes valid.", + "type": "string", + "readOnly": true + }, + "notAfterUtc": { + "format": "date-time", + "description": "The date on which the certificate is no longer valid.", + "type": "string", + "readOnly": true + }, + "serialNumber": { + "description": "The serial number.", + "type": "string", + "readOnly": true + }, + "version": { + "format": "int32", + "description": "The X509 format version.", + "type": "integer", + "readOnly": true + } + } + }, + "X509CertificateWithInfo": { + "description": "Certificate and Certificate info", + "type": "object", + "properties": { + "certificate": { + "type": "string" + }, + "info": { + "$ref": "#/definitions/X509CertificateInfo" + } + } + }, + "X509CertificateInfo": { + "description": "X509 certificate info.", + "required": [ + "subjectName", + "sha1Thumbprint", + "sha256Thumbprint", + "issuerName", + "notBeforeUtc", + "notAfterUtc", + "serialNumber", + "version" + ], + "type": "object", + "properties": { + "subjectName": { + "type": "string" + }, + "sha1Thumbprint": { + "type": "string" + }, + "sha256Thumbprint": { + "type": "string" + }, + "issuerName": { + "type": "string" + }, + "notBeforeUtc": { + "format": "date-time", + "type": "string" + }, + "notAfterUtc": { + "format": "date-time", + "type": "string" + }, + "serialNumber": { + "type": "string" + }, + "version": { + "format": "int32", + "type": "integer" + } + } + }, + "EnrollmentGroup": { + "description": "Enrollment group record.", + "required": [ + "enrollmentGroupId", + "attestation" + ], + "type": "object", + "properties": { + "enrollmentGroupId": { + "description": "Enrollment Group ID.", + "type": "string" + }, + "attestation": { + "$ref": "#/definitions/AttestationMechanism", + "description": "Attestation method used by the device." + }, + "etag": { + "description": "The entity tag associated with the resource.", + "type": "string" + }, + "provisioningStatus": { + "description": "The provisioning status.", + "default": "enabled", + "enum": [ + "enabled", + "disabled" + ], + "type": "string" + }, + "createdDateTimeUtc": { + "format": "date-time", + "description": "The DateTime this resource was created.", + "type": "string", + "readOnly": true + }, + "lastUpdatedDateTimeUtc": { + "format": "date-time", + "description": "The DateTime this resource was last updated.", + "type": "string", + "readOnly": true + }, + "provisioningSettings": { + "$ref": "#/definitions/ProvisioningSettings", + "description": "The Enrollment Provisioning Settings" + }, + "trustBundleId": { + "description": "Optional trust bundle id to associate with the enrollment.", + "type": "string" + }, + "clientCertificateIssuancePolicy": { + "$ref": "#/definitions/CertificateIssuancePolicy", + "description": "Certificate issuance policy for device client certificates." + }, + "serverCertificateIssuancePolicy": { + "$ref": "#/definitions/CertificateIssuancePolicy", + "description": "Certificate issuance policy for device server certificates." + } + } + }, + "QuerySpecification": { + "required": [ + "query" + ], + "type": "object", + "properties": { + "query": { + "type": "string" + } + } + }, + "BulkEnrollmentOperation": { + "description": "Bulk enrollment operation.", + "required": [ + "enrollments", + "mode" + ], + "type": "object", + "properties": { + "enrollments": { + "description": "Enrollment items", + "type": "array", + "items": { + "$ref": "#/definitions/IndividualEnrollment" + } + }, + "mode": { + "description": "Operation mode.", + "enum": [ + "create", + "update", + "updateIfMatchETag", + "delete" + ], + "type": "string" + } + } + }, + "BulkEnrollmentOperationResult": { + "description": "Results of a bulk enrollment operation", + "required": [ + "isSuccessful" + ], + "type": "object", + "properties": { + "errors": { + "description": "Registration errors", + "type": "array", + "items": { + "$ref": "#/definitions/BulkEnrollmentOperationError" + } + }, + "isSuccessful": { + "description": "Indicates if the operation was successful in its entirety.", + "type": "boolean" + } + } + }, + "BulkEnrollmentOperationError": { + "description": "Bulk enrollment operation error", + "required": [ + "registrationId", + "errorCode", + "errorStatus" + ], + "type": "object", + "properties": { + "registrationId": { + "description": "This id is used to uniquely identify a device registration of an enrollment.\r\nA case-insensitive string (up to 128 characters long) of alphanumeric characters plus certain special characters : . _ -. No special characters allowed at start or end.", + "type": "string" + }, + "errorCode": { + "format": "int32", + "description": "Error code", + "type": "integer" + }, + "errorStatus": { + "description": "Error status.", + "type": "string" + } + } + }, + "BulkEnrollmentGroupOperation": { + "description": "Bulk enrollment operation.", + "required": [ + "enrollmentGroups", + "mode" + ], + "type": "object", + "properties": { + "enrollmentGroups": { + "description": "Enrollment items", + "type": "array", + "items": { + "$ref": "#/definitions/EnrollmentGroup" + } + }, + "mode": { + "description": "Operation mode.", + "enum": [ + "create", + "update", + "updateIfMatchETag", + "delete" + ], + "type": "string" + } + } + }, + "BulkEnrollmentGroupOperationResult": { + "description": "Results of a bulk enrollment group operation.", + "required": [ + "isSuccessful" + ], + "type": "object", + "properties": { + "errors": { + "description": "Registration errors", + "type": "array", + "items": { + "$ref": "#/definitions/BulkEnrollmentGroupOperationError" + } + }, + "isSuccessful": { + "description": "Indicates if the operation was successful in its entirety.", + "type": "boolean" + } + } + }, + "BulkEnrollmentGroupOperationError": { + "description": "Bulk enrollment operation error.", + "required": [ + "enrollmentGroupId", + "errorCode", + "errorStatus" + ], + "type": "object", + "properties": { + "enrollmentGroupId": { + "description": "Enrollment group id.", + "type": "string" + }, + "errorCode": { + "format": "int32", + "description": "Error code", + "type": "integer" + }, + "errorStatus": { + "description": "Error status.", + "type": "string" + } + } + }, + "TwinCollection": { + "description": "Represents a collection of properties within a Twin", + "type": "object", + "properties": { + "version": { + "format": "int64", + "description": "Version of the TwinCollection", + "type": "integer" + }, + "count": { + "description": "Number of properties in the TwinCollection", + "type": "integer" + }, + "metadata": { + "$ref": "#/definitions/Metadata", + "description": "Metadata for the TwinCollection" + } + }, + "additionalProperties": { + "type": "object" + } + }, + "Metadata": { + "description": "Metadata for the TwinCollection", + "type": "object", + "properties": { + "lastUpdated": { + "format": "date-time", + "description": "Last time the TwinCollection was updated", + "type": "string" + }, + "lastUpdatedVersion": { + "format": "int64", + "description": "This is null for reported properties metadata and is not null for desired properties metadata.", + "type": "integer" + } + } + }, + "AzurePkiCertificateAuthority": { + "type": "object", + "allOf": [ + { + "$ref": "#/definitions/CertificateAuthority" + } + ], + "properties": { + "pkiUrl": { + "description": "Azure PKI Url", + "type": "string", + "example": "https://contoso-pki.eastus.pki.azure.net" + }, + "policyName": { + "description": "Azure PKI policy name", + "type": "string", + "example": "contoso-issuance-policy" + }, + "tenantId": { + "description": "The ID of tenant where the Azure PKI instance lives", + "type": "string" + } + } + }, + "DigiCertCertificateAuthority": { + "type": "object", + "allOf": [ + { + "$ref": "#/definitions/CertificateAuthority" + } + ], + "properties": { + "profileName": { + "description": "The customer specific DigiCert certificate authority profile name needed to identify their issuing CA instance.", + "type": "string" + }, + "apiKey": { + "description": "The API key required to authenticate with DigiCert.", + "type": "string" + } + } + }, + "GlobalSignCertificateAuthority": { + "type": "object", + "allOf": [ + { + "$ref": "#/definitions/CertificateAuthority" + } + ], + "properties": { + "apiKey": { + "description": "The customer specific GlobalSign certificate authority API key needed to identify their issuing CA instance.", + "type": "string" + }, + "apiSecret": { + "description": "The API secret required to authenticate with GlobalSign.", + "type": "string" + } + } + }, + "PrivatePreviewManagedCertificateAuthority": { + "type": "object", + "allOf": [ + { + "$ref": "#/definitions/CertificateAuthority" + } + ], + "properties": {} + }, + "DeviceState": { + "description": "Device state.", + "type": "object", + "properties": { + "initialTwin": { + "$ref": "#/definitions/InitialTwin" + }, + "deviceTags": { + "description": "Tags for the Device in Azure Resource Manager.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "capabilities": { + "$ref": "#/definitions/DeviceCapabilities" + } + } + }, + "AssignedEndpoint": { + "description": "Assigned Endpoint.", + "type": "object", + "properties": { + "hostName": { + "description": "The Host Name corresponding to the assigned endpoint.", + "type": "string", + "readOnly": true + }, + "type": { + "description": "The type of endpoint.", + "enum": [ + "iotHub", + "mqttBroker" + ], + "type": "string", + "readOnly": true + } + } + }, + "EndpointDescription": { + "description": "Endpoint Description.", + "type": "object", + "properties": { + "name": { + "description": "", + "type": "string" + }, + "type": { + "description": "", + "enum": [ + "iotHub", + "mqttBroker" + ], + "type": "string" + } + } + }, + "AzureAdIdentityProvisioningMetadata": { + "description": "Device identity metadata set at the enrollment level.", + "type": "object", + "properties": { + "deviceTemplateId": { + "description": "The deviceTemplateId the device should be provisioned as.", + "type": "string" + }, + "tenantId": { + "description": "The tenantId for the Azure AD instance.", + "type": "string" + }, + "initialGroups": { + "description": "AzureAd ObjectIds of groups the device should be registered to.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "IdentityProvisioningSettings": { + "description": "Settings for Identity Provisioning", + "type": "object", + "properties": { + "type": { + "description": "Identity Type. Currently supports AzureAd.", + "enum": [ + "azureAd" + ], + "type": "string" + }, + "azureAdIdentityMetadata": { + "$ref": "#/definitions/AzureAdIdentityProvisioningMetadata" + } + } + }, + "ProvisioningSettings": { + "description": "The Enrollment Provisioning Settings.", + "type": "object", + "properties": { + "resourceGroup": { + "description": "The device's resource group.", + "type": "string" + }, + "subscriptionId": { + "description": "The device's subscriptionId.", + "type": "string" + }, + "region": { + "description": "The region of the device registry.", + "type": "string" + }, + "initialDeviceState": { + "$ref": "#/definitions/DeviceState" + }, + "reprovisionPolicy": { + "$ref": "#/definitions/ReprovisionPolicy" + }, + "identitySettings": { + "$ref": "#/definitions/IdentityProvisioningSettings" + }, + "allocationSettings": { + "$ref": "#/definitions/AllocationSettings" + } + } + } + }, + "parameters": { + "ApiVersionParameter": { + "name": "api-version", + "in": "query", + "description": "The API version to use for the request. Supported versions include: 2023-02-01-preview", + "required": true, + "type": "string", + "default": "2023-02-01-preview" + } + }, + "tags": [] +} \ No newline at end of file diff --git a/tools/apiview/parsers/swagger-api-parser/global.json b/tools/apiview/parsers/swagger-api-parser/global.json new file mode 100644 index 00000000000..9e5e1fd1dce --- /dev/null +++ b/tools/apiview/parsers/swagger-api-parser/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "6.0.0", + "rollForward": "latestMajor", + "allowPrerelease": true + } +} \ No newline at end of file diff --git a/tools/apiview/parsers/swagger-api-parser/swagger-api-parser.sln b/tools/apiview/parsers/swagger-api-parser/swagger-api-parser.sln index 8c35b8f8881..750a46fc3e0 100644 --- a/tools/apiview/parsers/swagger-api-parser/swagger-api-parser.sln +++ b/tools/apiview/parsers/swagger-api-parser/swagger-api-parser.sln @@ -5,10 +5,6 @@ VisualStudioVersion = 17.0.32014.148 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SwaggerApiParser", "SwaggerApiParser\SwaggerApiParser.csproj", "{0444F9A1-1230-49B0-B280-D2B887F14EFD}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "APIViewWeb", "..\..\..\..\src\dotnet\APIView\APIViewWeb\APIViewWeb.csproj", "{04A2E9C9-C5D4-4D5C-92CE-66B4BCFCD79A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "APIView", "..\..\..\..\src\dotnet\APIView\APIView\APIView.csproj", "{33DFC38B-8A02-48C4-8BCB-334B0D0F5BEE}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SwaggerApiParserTest", "SwaggerApiParserTest\SwaggerApiParserTest.csproj", "{26CFF247-A6EE-41D8-9405-254A05B6150A}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{28C36B96-D86C-4126-BD67-7B508C6668D1}"