Init WFuzz state

This commit is contained in:
Jan Stárek
2019-10-09 13:24:01 +02:00
parent 7c3ed5ef0b
commit a5eb2a97e1
114 changed files with 6221 additions and 0 deletions

63
parser/.gitattributes vendored Normal file
View File

@@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

265
parser/.gitignore vendored Normal file
View File

@@ -0,0 +1,265 @@
## Custom ignores
*.json
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
project.fragment.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
#*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc

29
parser/Models/Endpoint.cs Normal file
View File

@@ -0,0 +1,29 @@
using System.Collections.Generic;
using System.Text;
namespace Models
{
public class Endpoint
{
public string Uri { get; }
public List<Request> Requests { get; } = new List<Request>();
public Endpoint(string uri)
{
Uri = uri;
}
public override string ToString()
{
StringBuilder builder = new StringBuilder();
builder.Append("Uri: ");
builder.AppendLine(Uri);
builder.AppendLine("Requests: ");
foreach (var request in Requests)
{
builder.AppendLine(request.ToString());
}
return builder.ToString();
}
}
}

View File

@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
</Project>

43
parser/Models/Request.cs Normal file
View File

@@ -0,0 +1,43 @@
using System.Collections.Generic;
using System.Text;
namespace Models
{
public class Request
{
public string Method { get; }
public string Summary { get; set; }
public List<UriAttribute> UriAttributes { get; set; }
public List<Response> Responses { get; set; }
public string BodyExample { get; set; }
public Dictionary<string, object> BodySchema { get; set; }
public Request(string method)
{
Method = method;
}
public override string ToString()
{
StringBuilder builder = new StringBuilder();
builder.Append("Type: ");
builder.AppendLine(Method);
builder.Append("Summary: ");
builder.AppendLine(Summary);
builder.AppendLine("Uri Attributes: ");
foreach (var attribute in UriAttributes)
{
builder.AppendLine(attribute.ToString());
}
builder.AppendLine("Responses: ");
foreach (var response in Responses)
{
builder.AppendLine(response.ToString());
}
builder.Append("BodyExample: ");
builder.AppendLine(BodyExample);
return builder.ToString();
}
}
}

15
parser/Models/Response.cs Normal file
View File

@@ -0,0 +1,15 @@
using System;
namespace Models
{
public class Response
{
public int StatusCode { get; set; }
public string Example { get; set; }
public override string ToString()
{
return $"Status code: {StatusCode}{Environment.NewLine}Example: {Example}";
}
}
}

View File

@@ -0,0 +1,25 @@
using System;
namespace Models
{
public class UriAttribute
{
public string Name { get; }
public bool Required { get; }
public string ExampleValue { get; set; }
public string Type { get; set; }
public string Format { get; set; }
public UriAttribute(string name, bool required)
{
Name = name;
Required = required;
}
public override string ToString()
{
return $"Name: {Name}{Environment.NewLine}Required: {Required}{Environment.NewLine}Example value: {ExampleValue}";
}
}
}

43
parser/OpenApiParser.sln Normal file
View File

@@ -0,0 +1,43 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29009.5
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Models", "Models\Models.csproj", "{168C8169-8DF0-45C2-97C0-A9D565F4A59A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Parser", "Parser\Parser.csproj", "{5B556283-E412-482E-BFC3-BF2C99FB1D41}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenApiParserCLI", "OpenApiParserCLI\OpenApiParserCLI.csproj", "{476E98FC-1E96-4FD1-ACC5-996A30D58F09}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Parser.Tests", "Parser.Tests\Parser.Tests.csproj", "{7250D212-A4AC-405C-8ECE-86046CC3B3F4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{168C8169-8DF0-45C2-97C0-A9D565F4A59A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{168C8169-8DF0-45C2-97C0-A9D565F4A59A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{168C8169-8DF0-45C2-97C0-A9D565F4A59A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{168C8169-8DF0-45C2-97C0-A9D565F4A59A}.Release|Any CPU.Build.0 = Release|Any CPU
{5B556283-E412-482E-BFC3-BF2C99FB1D41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5B556283-E412-482E-BFC3-BF2C99FB1D41}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5B556283-E412-482E-BFC3-BF2C99FB1D41}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5B556283-E412-482E-BFC3-BF2C99FB1D41}.Release|Any CPU.Build.0 = Release|Any CPU
{476E98FC-1E96-4FD1-ACC5-996A30D58F09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{476E98FC-1E96-4FD1-ACC5-996A30D58F09}.Debug|Any CPU.Build.0 = Debug|Any CPU
{476E98FC-1E96-4FD1-ACC5-996A30D58F09}.Release|Any CPU.ActiveCfg = Release|Any CPU
{476E98FC-1E96-4FD1-ACC5-996A30D58F09}.Release|Any CPU.Build.0 = Release|Any CPU
{7250D212-A4AC-405C-8ECE-86046CC3B3F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7250D212-A4AC-405C-8ECE-86046CC3B3F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7250D212-A4AC-405C-8ECE-86046CC3B3F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7250D212-A4AC-405C-8ECE-86046CC3B3F4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8701D52B-F8CB-4BB0-A09A-0E5BA566BA9D}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Parser\Parser.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.OpenApi.Models;
using Models;
using Newtonsoft.Json;
using Parser;
namespace OpenApiParserCLI
{
static class Program
{
static int Main(string[] args)
{
if (args.Length != 2)
{
Console.WriteLine("Bad arguments provided.");
PrintHelp();
return 1;
}
string openApiDocFilePath = args[0];
string outputEndpointsFilePath = args[1];
if (!ValidateArguments(openApiDocFilePath, outputEndpointsFilePath))
{
PrintHelp();
return 1;
}
OpenApiDocument openApiDocument = OpenApiDocumentParser.ParseOpenApiDocument(openApiDocFilePath);
List<Endpoint> endpoints = EndpointParser.ParseAllEndpoints(openApiDocument);
string json = JsonConvert.SerializeObject(endpoints, Formatting.Indented);
File.WriteAllText(outputEndpointsFilePath, json);
return 0;
}
static bool ValidateArguments(string openApiDocFilePath, string outputEndpointsFilePath)
{
if (!File.Exists(openApiDocFilePath))
{
Console.WriteLine("Cannot find specified OpenApi file");
return false;
}
try
{
File.Create(outputEndpointsFilePath).Close();
}
catch
{
Console.WriteLine("Cannot create output file on defined path.");
return false;
}
return true;
}
static void PrintHelp()
{
Console.WriteLine("Arguments:");
Console.WriteLine("1] OpenAPI doc. file");
Console.WriteLine("2] Output file with JSON endpoints specification");
Console.WriteLine("Example: parser.exe api.yaml output.json");
}
}
}

View File

@@ -0,0 +1,118 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using NUnit.Framework;
namespace Parser.Tests
{
public class AttributeParserTests
{
[TestCase(ParameterLocation.Cookie)]
[TestCase(ParameterLocation.Header)]
public void ParsingAttributeElsewhereThanInPathOrQueryShouldReturnNull(ParameterLocation parameterLocation)
{
OpenApiParameter parameter = new OpenApiParameter {In = parameterLocation};
var parsedAttribute = AttributeParser.ParseAttribute(parameter);
Assert.IsNull(parsedAttribute);
}
[Test]
public void ParsingAttributeWithNoTypeOrFormatShouldReturnNull()
{
OpenApiParameter parameter = new OpenApiParameter {Schema = new OpenApiSchema {Type = null, Format = null}};
var parsedAttribute = AttributeParser.ParseAttribute(parameter);
Assert.IsNull(parsedAttribute);
}
[Test]
public void ParsingPathAttributeWithValidContentExample()
{
string attributeContent = "test";
OpenApiParameter parameter = new OpenApiParameter
{
In = ParameterLocation.Path, Schema = new OpenApiSchema {Type = "string", Format = null},
Example = new OpenApiString(attributeContent)
};
var parsedAttribute = AttributeParser.ParseAttribute(parameter);
Assert.AreEqual(attributeContent, parsedAttribute.ExampleValue);
}
[Test]
public void ParsingQueryAttributeWithValidContentExample()
{
string attributeContent = "test";
OpenApiParameter parameter = new OpenApiParameter
{
In = ParameterLocation.Query,
Schema = new OpenApiSchema { Type = "string", Format = null },
Example = new OpenApiString(attributeContent)
};
var parsedAttribute = AttributeParser.ParseAttribute(parameter);
Assert.AreEqual(attributeContent, parsedAttribute.ExampleValue);
}
[Test]
public void ParsingAttributeWithValidContentExamples()
{
string attributeContent = "test";
OpenApiParameter parameter = new OpenApiParameter
{
In = ParameterLocation.Path, Schema = new OpenApiSchema {Type = "string", Format = null},
Examples = new Dictionary<string, OpenApiExample>
{
{ "testKey 1", new OpenApiExample {Value = new OpenApiString(attributeContent)} },
{ "testKey 2", new OpenApiExample {Value = new OpenApiString(attributeContent)} }
}
};
var parsedAttribute = AttributeParser.ParseAttribute(parameter);
Assert.AreEqual(attributeContent, parsedAttribute.ExampleValue);
}
[Test]
public void ParsingAttributeWithInheritingExampleJustFromDataType()
{
OpenApiParameter parameter = new OpenApiParameter
{
In = ParameterLocation.Path,
Schema = new OpenApiSchema { Type = "string", Format = null },
Example = null,
Examples = new Dictionary<string, OpenApiExample>()
};
var parsedAttribute = AttributeParser.ParseAttribute(parameter);
Assert.IsNotNull(parsedAttribute);
Assert.IsTrue(!string.IsNullOrEmpty(parsedAttribute.ExampleValue));
}
[Test]
public void CheckThatParsedAttributeHasCorrectlySetDataTypeAndFormat()
{
OpenApiParameter parameter = new OpenApiParameter
{
In = ParameterLocation.Path,
Schema = new OpenApiSchema { Type = "string", Format = null },
Example = null,
Examples = new Dictionary<string, OpenApiExample>()
};
var parsedAttribute = AttributeParser.ParseAttribute(parameter);
Assert.IsNotNull(parsedAttribute);
Assert.IsTrue(!string.IsNullOrEmpty(parsedAttribute.ExampleValue));
Assert.AreEqual(parameter.Schema.Type, parsedAttribute.Type);
Assert.AreEqual(parameter.Schema.Format, parsedAttribute.Format);
}
}
}

View File

@@ -0,0 +1,45 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using NUnit.Framework;
namespace Parser.Tests
{
public class ContentParserTests
{
[Test]
public void NoExampleFoundShouldReturnNull()
{
string parsedExample = ContentParser.GetStringExampleFromContent(null, new Dictionary<string, OpenApiExample>());
Assert.IsNull(parsedExample);
}
[Test]
public void ParsingValidContentExample()
{
string attributeContent = "test";
OpenApiString example = new OpenApiString(attributeContent);
Dictionary<string, OpenApiExample> examples = new Dictionary<string, OpenApiExample>();
string parsedExample = ContentParser.GetStringExampleFromContent(example, examples);
Assert.AreEqual(attributeContent, parsedExample);
}
[Test]
public void ParsingContentExamples()
{
string attributeContent = "test";
Dictionary<string, OpenApiExample> examples = new Dictionary<string, OpenApiExample>
{
{"testKey 1", new OpenApiExample {Value = new OpenApiString(attributeContent)}},
{"testKey 2", new OpenApiExample {Value = new OpenApiString(attributeContent)}}
};
string parsedExample = ContentParser.GetStringExampleFromContent(null, examples);
Assert.AreEqual(attributeContent, parsedExample);
}
}
}

View File

@@ -0,0 +1,62 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Models;
using NUnit.Framework;
using Models;
namespace Parser.Tests
{
public class EndpointParserTests
{
readonly OpenApiDocument _document = new OpenApiDocument {Paths = new OpenApiPaths()};
private void AddTwoTestingPaths()
{
_document.Paths.Add("/path1", new OpenApiPathItem
{
Operations = new Dictionary<OperationType, OpenApiOperation>
{
{ OperationType.Get, new OpenApiOperation
{
Responses = new OpenApiResponses
{
{ "200", new OpenApiResponse()}
}
}
}
}
});
_document.Paths.Add("/path2", new OpenApiPathItem
{
Operations = new Dictionary<OperationType, OpenApiOperation>
{
{ OperationType.Get, new OpenApiOperation
{
Responses = new OpenApiResponses
{
{ "201", new OpenApiResponse()}
}
}
}
}
});
}
[Test]
public void DocumentWithoutAnyPathsShouldReturnEmptyList()
{
List<Endpoint> endpoints = EndpointParser.ParseAllEndpoints(_document);
Assert.IsEmpty(endpoints);
}
[Test]
public void DocumentWithTwoPathsEachHavingSingleResponseShouldReturnTwoEndpoints()
{
AddTwoTestingPaths();
List<Endpoint> endpoints = EndpointParser.ParseAllEndpoints(_document);
Assert.AreEqual(2, endpoints.Count);
}
}
}

View File

@@ -0,0 +1,56 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using NUnit.Framework;
namespace Parser.Tests
{
public class ExamplesParserTests
{
[TestCase("application/octet-stream")]
[TestCase("application/pdf")]
[TestCase("application/zip")]
public void ParsingNotSupportedContentTypeShouldReturnNull(string contentType)
{
Assert.IsNull(ExamplesParser.ParseExample(new Dictionary<string, OpenApiMediaType> { { contentType, null } }));
}
[Test]
public void ParsingPlainTextContent()
{
string plainText = "test";
Dictionary<string, OpenApiMediaType> content = new Dictionary<string, OpenApiMediaType>
{
{"text/plain", new OpenApiMediaType {Example = new OpenApiString(plainText)}}
};
string example = ExamplesParser.ParseExample(content);
Assert.AreEqual(plainText, example);
}
[Test]
public void ParsingJsonContent()
{
string jsonTemplate = "{\n \"testKey1\": \"testValue\",\n \"testKey2\": \"testValue\"\n}";
Dictionary<string, OpenApiMediaType> content = new Dictionary<string, OpenApiMediaType>
{
{
"application/json", new OpenApiMediaType
{
Example = new OpenApiObject
{
{"testKey1", new OpenApiString("testValue") },
{"testKey2", new OpenApiString("testValue") }
}
}
}
};
string example = ExamplesParser.ParseExample(content);
Assert.AreEqual(jsonTemplate, example);
}
}
}

View File

@@ -0,0 +1,88 @@
using System;
using Microsoft.OpenApi.Any;
using NUnit.Framework;
namespace Parser.Tests
{
public class OpenApiAnyConvertorTests
{
[Test]
public void ConvertStringPrimitiveShouldReturnCorrectValue()
{
string value = "test";
Assert.AreEqual(value, OpenApiAnyConvertor.GetPrimitiveValue(new OpenApiString(value)));
}
[Test]
public void ConvertBooleanPrimitiveShouldReturnCorrectValue()
{
Assert.AreEqual("True", OpenApiAnyConvertor.GetPrimitiveValue(new OpenApiBoolean(true)));
}
[Test]
public void ConvertIntegerPrimitiveShouldReturnCorrectValue()
{
Assert.AreEqual("5", OpenApiAnyConvertor.GetPrimitiveValue(new OpenApiInteger(5)));
}
[Test]
public void ConvertBytePrimitiveShouldReturnCorrectValue()
{
var primitiveValue = OpenApiAnyConvertor.GetPrimitiveValue(new OpenApiByte(new byte[] { 7, 8 }));
Assert.AreEqual("\a\b", primitiveValue);
}
[Test]
public void ConvertBinaryPrimitiveShouldReturnCorrectValue()
{
var primitiveValue = OpenApiAnyConvertor.GetPrimitiveValue(new OpenApiBinary(new byte[] { 7, 8 }));
Assert.AreEqual("0000011100001000", primitiveValue);
}
[Test]
public void ConvertDatePrimitiveShouldReturnCorrectValue()
{
var primitiveValue = OpenApiAnyConvertor.GetPrimitiveValue(new OpenApiDate(DateTime.UnixEpoch));
Assert.AreEqual("01/01/1970 00:00:00", primitiveValue);
}
[Test]
public void ConvertDateTimePrimitiveShouldReturnCorrectValue()
{
var primitiveValue = OpenApiAnyConvertor.GetPrimitiveValue(new OpenApiDateTime(DateTime.UnixEpoch));
Assert.AreEqual("1/1/1970 12:00:00 AM +00:00", primitiveValue);
}
[Test]
public void ConvertObjectShouldReturnCorrectJson()
{
string expectedJson = "{\n \"testKey1\": \"testValue\",\n \"testKey2\": \"testValue\"\n}";
var openApiObject = new OpenApiObject
{
{"testKey1", new OpenApiString("testValue")},
{"testKey2", new OpenApiString("testValue")}
};
var jsonValue = OpenApiAnyConvertor.GetJsonValue(openApiObject);
Assert.AreEqual(expectedJson, jsonValue);
}
[Test]
public void ConvertArrayShouldReturnCorrectJson()
{
string expectedJson = "[\n {\n \"testKey1\": \"testValue\",\n \"testKey2\": \"testValue\"\n }\n]";
var openApiObject = new OpenApiObject
{
{"testKey1", new OpenApiString("testValue")},
{"testKey2", new OpenApiString("testValue")}
};
var openApiArray = new OpenApiArray {openApiObject};
var jsonValue = OpenApiAnyConvertor.GetJsonValue(openApiArray);
Assert.AreEqual(expectedJson, jsonValue);
}
}
}

View File

@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="nunit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Models\Models.csproj" />
<ProjectReference Include="..\Parser\Parser.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.OpenApi">
<HintPath>..\..\..\..\..\.nuget\packages\microsoft.openapi\1.1.3\lib\netstandard2.0\Microsoft.OpenApi.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,30 @@
using System;
using NUnit.Framework;
namespace Parser.Tests
{
public class PrimitiveDataTypeExampleGeneratorTests
{
[TestCase("integer", null, "42")]
[TestCase("number", null, "42.0")]
[TestCase("boolean", null, "True")]
[TestCase("string", null, "example")]
[TestCase("string", "byte", "example")]
[TestCase("string", "binary", "01234567")]
[TestCase("string", "date", "2002-10-02")]
[TestCase("string", "date-time", "2002-10-02T10:00:00-05:00")]
[TestCase("string", "password", "example")]
public void ValidCombinationsShouldReturnValidValue(string type, string format, string expected)
{
string example = PrimitiveDataTypeExampleGenerator.GenerateExampleValueByType(type, format);
Assert.AreEqual(expected, example);
}
[Test]
public void InvalidCombinationsShouldThrowAnException()
{
Assert.Throws<NotImplementedException>(() => PrimitiveDataTypeExampleGenerator.GenerateExampleValueByType("test", null));
}
}
}

View File

@@ -0,0 +1,49 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Models;
using NUnit.Framework;
namespace Parser.Tests
{
public class RequestParserTests
{
[Test]
public void ValidRequestParsingShouldHaveCorrectValues()
{
string summary = "summary";
string example = "test";
KeyValuePair<OperationType, OpenApiOperation> operation =
new KeyValuePair<OperationType, OpenApiOperation>(OperationType.Get, new OpenApiOperation
{
Summary = summary,
Parameters = new List<OpenApiParameter> { new OpenApiParameter
{
In = ParameterLocation.Path,
Example = new OpenApiString(example),
Schema = new OpenApiSchema {Type = "string", Format = null}
} },
RequestBody = new OpenApiRequestBody
{
Content = new Dictionary<string, OpenApiMediaType>
{
{ "text/plain", new OpenApiMediaType
{
Example = new OpenApiString(example)
} }
}
}
});
Request request = RequestParser.ParseRequest(operation);
Assert.AreEqual(summary, request.Summary);
Assert.AreEqual(example, request.BodyExample);
Assert.AreEqual(example, request.UriAttributes[0].ExampleValue);
}
}
}

View File

@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Models;
using NUnit.Framework;
namespace Parser.Tests
{
public class ResponseParserTests
{
[Test]
public void ValidResponseParsingShouldHaveCorrectValues()
{
int statusCode = 200;
string example = "example";
KeyValuePair<string, OpenApiResponse> openApiResponse = new KeyValuePair<string, OpenApiResponse>(statusCode.ToString(), new OpenApiResponse
{
Content = new Dictionary<string, OpenApiMediaType>
{
{ "text/plain", new OpenApiMediaType
{
Schema = new OpenApiSchema {Type = "string", Format = null},
Example = new OpenApiString(example)
} }
}
});
Response response = ResponseParser.ParseResponse(openApiResponse);
Assert.AreEqual(statusCode, response.StatusCode);
Assert.AreEqual(example, response.Example);
}
[Test]
public void InvalidResponseStatusCodeShouldThrowException()
{
KeyValuePair<string, OpenApiResponse> openApiResponse = new KeyValuePair<string, OpenApiResponse>("invalid", new OpenApiResponse());
Assert.Throws<NotImplementedException>(() => ResponseParser.ParseResponse(openApiResponse));
}
}
}

View File

@@ -0,0 +1,164 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using NUnit.Framework;
namespace Parser.Tests
{
public class SchemaParserTests
{
[TestCase("application/octet-stream")]
[TestCase("application/pdf")]
[TestCase("application/zip")]
public void ParsingNotSupportedContentTypeShouldReturnNull(string contentType)
{
Assert.IsNull(SchemaParser.ParseSchema(new Dictionary<string, OpenApiMediaType> { { contentType, null } }));
}
[Test]
public void SchemaWithRegularProperties()
{
string testingPropertyName = "testKey";
string testingPropertyType = "string";
string testingPropertyExample = "test";
string testingPropertyFormat = null;
Dictionary<string, OpenApiMediaType> content = new Dictionary<string, OpenApiMediaType>
{
{
"application/json", new OpenApiMediaType
{
Schema = new OpenApiSchema
{
Properties = new Dictionary<string, OpenApiSchema>
{
{
testingPropertyName, new OpenApiSchema
{
Type = testingPropertyType,
Example = new OpenApiString(testingPropertyExample),
Title = testingPropertyName,
Format = testingPropertyFormat
}
}
}
}
}
}
};
Dictionary<string, object> parsedSchema = SchemaParser.ParseSchema(content);
Dictionary<string, object> testingProperty = (Dictionary<string, object>) parsedSchema.First().Value;
Assert.AreEqual(testingPropertyName, parsedSchema.First().Key);
Assert.That(testingProperty.ContainsKey("Title"));
Assert.That(testingProperty.ContainsKey("Type"));
Assert.That(testingProperty.ContainsKey("Format"));
Assert.That(testingProperty.ContainsKey("Example"));
Assert.AreEqual(testingPropertyName, testingProperty["Title"]);
Assert.AreEqual(testingPropertyType, testingProperty["Type"]);
Assert.AreEqual(testingPropertyFormat, testingProperty["Format"]);
Assert.AreEqual(testingPropertyExample, testingProperty["Example"]);
}
[Test]
public void SchemaWithRegularArrayOfDoublesProperty()
{
string testingPropertyName = "testKey";
string testingPropertyType = "array";
string testingPropertyFormat = null;
string testingArrayItemType = "double";
string testingArrayItemFormat = "number";
Dictionary<string, OpenApiMediaType> content = new Dictionary<string, OpenApiMediaType>
{
{
"application/json", new OpenApiMediaType
{
Schema = new OpenApiSchema
{
Properties = new Dictionary<string, OpenApiSchema>
{
{
testingPropertyName, new OpenApiSchema
{
Type = testingPropertyType,
Title = testingPropertyName,
Items = new OpenApiSchema { Type = testingArrayItemType, Format = testingArrayItemFormat },
Format = testingPropertyFormat
}
}
}
}
}
}
};
Dictionary<string, object> parsedSchema = SchemaParser.ParseSchema(content);
Dictionary<string, object> testingProperty = (Dictionary<string, object>) parsedSchema.First().Value;
Dictionary<string, object> arrayTypeDictionary = (Dictionary<string, object>) testingProperty["ArrayItemSchema"];
Assert.AreEqual(testingPropertyName, parsedSchema.First().Key);
Assert.That(arrayTypeDictionary.ContainsKey("Type"));
Assert.That(arrayTypeDictionary.ContainsKey("Format"));
Assert.AreEqual(testingArrayItemType, arrayTypeDictionary["Type"]);
Assert.AreEqual(testingArrayItemFormat, arrayTypeDictionary["Format"]);
}
[Test]
public void SchemaWithAdditionalProperties()
{
string testingPropertyName = "testKey";
string testingPropertyType = "boolean";
bool testingPropertyExample = true;
string testingPropertyFormat = null;
Dictionary<string, OpenApiMediaType> content = new Dictionary<string, OpenApiMediaType>
{
{
"application/json", new OpenApiMediaType
{
Schema = new OpenApiSchema
{
AdditionalPropertiesAllowed = true,
AdditionalProperties = new OpenApiSchema {Properties = new Dictionary<string, OpenApiSchema>
{
{
testingPropertyName, new OpenApiSchema
{
Type = testingPropertyType,
Example = new OpenApiBoolean(testingPropertyExample),
Title = testingPropertyName,
Format = testingPropertyFormat
}
}
}}
}
}
}
};
Dictionary<string, object> firstAdditionalPropertyDictionary = (Dictionary<string, object>) SchemaParser.ParseSchema(content).First().Value;
Dictionary<string, object> firstAdditionalPropertyItemDictionary = (Dictionary<string, object>)firstAdditionalPropertyDictionary.First().Value;
Assert.AreEqual(testingPropertyName, firstAdditionalPropertyDictionary.First().Key);
Assert.That(firstAdditionalPropertyItemDictionary.ContainsKey("Title"));
Assert.That(firstAdditionalPropertyItemDictionary.ContainsKey("Type"));
Assert.That(firstAdditionalPropertyItemDictionary.ContainsKey("Format"));
Assert.That(firstAdditionalPropertyItemDictionary.ContainsKey("Example"));
Assert.AreEqual(testingPropertyName, firstAdditionalPropertyItemDictionary["Title"]);
Assert.AreEqual(testingPropertyType, firstAdditionalPropertyItemDictionary["Type"]);
Assert.AreEqual(testingPropertyFormat, firstAdditionalPropertyItemDictionary["Format"]);
Assert.AreEqual(testingPropertyExample.ToString(), firstAdditionalPropertyItemDictionary["Example"]);
}
}
}

View File

@@ -0,0 +1,31 @@
using System;
using Microsoft.OpenApi.Models;
using Models;
namespace Parser
{
public static class AttributeParser
{
public static UriAttribute ParseAttribute(OpenApiParameter parameter)
{
if (parameter.In == ParameterLocation.Path || parameter.In == ParameterLocation.Query)
{
if (parameter.Schema == null || parameter.Schema.Type == null && parameter.Schema.Format == null)
{
throw new ArgumentException("We do not know anything useful about passed URI parameter.");
}
UriAttribute attribute = new UriAttribute(parameter.Name, parameter.Required)
{
ExampleValue = ContentParser.GetStringExampleFromContent(parameter.Example, parameter.Examples) ??
ContentParser.GetSingleExample(parameter.Schema?.Example) ??
PrimitiveDataTypeExampleGenerator.GenerateExampleValueByType(parameter.Schema.Type, parameter.Schema.Format),
Type = parameter.Schema.Type,
Format = parameter.Schema.Format
};
return attribute;
}
return null;
}
}
}

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
namespace Parser
{
public static class ContentParser
{
public static string GetStringExampleFromContent(IOpenApiAny example, IDictionary<string, OpenApiExample> examples)
{
return GetSingleExample(example) ?? GetExampleFromExamplesDict(examples);
}
public static string GetSingleExample(IOpenApiAny example)
{
return example != null ? GetStringFromAnyType(example) : null;
}
// If there are more examples, take the first one
// To the future consider creating request for each example
static string GetExampleFromExamplesDict(IDictionary<string, OpenApiExample> examples)
{
return examples.Count > 0 ? GetSingleExample(examples.First().Value.Value) : null;
}
static string GetStringFromAnyType(IOpenApiAny value)
{
switch (value.AnyType)
{
case AnyType.Primitive:
return OpenApiAnyConvertor.GetPrimitiveValue(value);
case AnyType.Object:
case AnyType.Array:
return OpenApiAnyConvertor.GetJsonValue(value);
default:
throw new NotImplementedException("This data example type is not supported yet!");
}
}
}
}

View File

@@ -0,0 +1,30 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Models;
using Models;
namespace Parser
{
public static class EndpointParser
{
public static List<Endpoint> ParseAllEndpoints(OpenApiDocument openApiDocument)
{
List<Endpoint> endpoints = new List<Endpoint>();
foreach (var path in openApiDocument.Paths)
{
endpoints.Add(ParseEndpoint(path));
}
return endpoints;
}
static Endpoint ParseEndpoint(KeyValuePair<string, OpenApiPathItem> path)
{
Endpoint endpoint = new Endpoint(path.Key);
foreach (KeyValuePair<OperationType, OpenApiOperation> operation in path.Value.Operations)
{
endpoint.Requests.Add(RequestParser.ParseRequest(operation));
}
return endpoint;
}
}
}

View File

@@ -0,0 +1,37 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Models;
namespace Parser
{
public static class ExamplesParser
{
static readonly List<string> SupportedContentTypes = new List<string> { "application/json", "text/plain" };
public static string ParseExample(IDictionary<string, OpenApiMediaType> contentDict)
{
foreach (var supportedContentType in SupportedContentTypes)
{
if (contentDict.ContainsKey(supportedContentType))
return GetSpecificContentTypeExample(contentDict, supportedContentType);
}
return null;
}
static string GetSpecificContentTypeExample(IDictionary<string, OpenApiMediaType> contentDict, string contentType)
{
var content = contentDict[contentType];
return GetRealExample(content) ?? GetExampleFromSchema(content);
}
static string GetRealExample(OpenApiMediaType content)
{
return ContentParser.GetStringExampleFromContent(content.Example, content.Examples);
}
static string GetExampleFromSchema(OpenApiMediaType content)
{
return content.Schema != null ? ContentParser.GetStringExampleFromContent(content.Schema.Example, new Dictionary<string, OpenApiExample>()) : null;
}
}
}

View File

@@ -0,0 +1,68 @@
using System;
using System.Globalization;
using System.IO;
using System.Text;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Writers;
namespace Parser
{
public static class OpenApiAnyConvertor
{
public static string GetPrimitiveValue(IOpenApiAny value)
{
IOpenApiPrimitive primitive = (IOpenApiPrimitive) value;
switch (primitive.PrimitiveType)
{
case PrimitiveType.String:
OpenApiString stringValue = (OpenApiString) primitive;
return stringValue.Value;
case PrimitiveType.Boolean:
OpenApiBoolean booleanValue = (OpenApiBoolean) primitive;
return booleanValue.Value.ToString();
case PrimitiveType.Integer:
OpenApiInteger integerValue = (OpenApiInteger) primitive;
return integerValue.Value.ToString();
case PrimitiveType.Long:
OpenApiLong longValue = (OpenApiLong) primitive;
return longValue.Value.ToString();
case PrimitiveType.Float:
OpenApiFloat floatValue = (OpenApiFloat) primitive;
return floatValue.Value.ToString(CultureInfo.InvariantCulture);
case PrimitiveType.Double:
OpenApiDouble doubleValue = (OpenApiDouble) primitive;
return doubleValue.Value.ToString(CultureInfo.InvariantCulture);
case PrimitiveType.Byte:
OpenApiByte byteValue = (OpenApiByte) primitive;
return Encoding.Default.GetString(byteValue.Value);
case PrimitiveType.Binary:
OpenApiBinary binaryValue = (OpenApiBinary) primitive;
StringBuilder builder = new StringBuilder();
foreach (byte byteVal in binaryValue.Value)
{
builder.Append(Convert.ToString(byteVal, 2).PadLeft(8, '0'));
}
return builder.ToString();
case PrimitiveType.Date:
OpenApiDate dateValue = (OpenApiDate) primitive;
return dateValue.Value.ToString(CultureInfo.InvariantCulture);
case PrimitiveType.DateTime:
OpenApiDateTime dateTimeValue = (OpenApiDateTime) primitive;
return dateTimeValue.Value.ToString();
case PrimitiveType.Password:
OpenApiPassword passwordValue = (OpenApiPassword) primitive;
return passwordValue.Value;
default:
throw new NotImplementedException("This data example type is not supported yet!");
}
}
public static string GetJsonValue(IOpenApiAny value)
{
StringBuilder builder = new StringBuilder();
value.Write(new OpenApiJsonWriter(new StringWriter(builder)), OpenApiDocumentParser.Version);
return builder.ToString();
}
}
}

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.OpenApi;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers;
namespace Parser
{
public static class OpenApiDocumentParser
{
public static OpenApiSpecVersion Version;
public static OpenApiDocument ParseOpenApiDocument(string openApiDocFilePath)
{
OpenApiDocument openApiDocument;
using (FileStream stream = File.Open(openApiDocFilePath, FileMode.Open))
{
openApiDocument = new OpenApiStreamReader().Read(stream, out var diagnostic);
StoreDocumentVersion(diagnostic.SpecificationVersion);
PrintParsingErrors(diagnostic.Errors);
}
return openApiDocument;
}
static void PrintParsingErrors(IList<OpenApiError> errors)
{
foreach (var openApiError in errors)
{
Console.WriteLine("WARNING: Following parsing error occurs: " + openApiError.Message);
}
}
static void StoreDocumentVersion(OpenApiSpecVersion version)
{
Version = version;
}
}
}

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.OpenApi.Readers" Version="1.1.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Models\Models.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,48 @@
using System;
using System.Text;
namespace Parser
{
// Data types for Open API 2 and OpenAPI 3 are basically the same:
// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md
// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md
public static class PrimitiveDataTypeExampleGenerator
{
public static string GenerateExampleValueByType(string type, string format)
{
switch (type)
{
case "integer":
return "42";
case "number":
return "42.0";
case "boolean":
return "True";
case "string":
{
const string example = "example";
switch (format)
{
case null:
return example;
case "byte":
var plainTextBytes = Encoding.UTF8.GetBytes(example);
return Encoding.Default.GetString(plainTextBytes);
case "binary":
return "01234567";
case "date":
return "2002-10-02";
case "date-time":
return "2002-10-02T10:00:00-05:00";
case "password":
return example;
}
break;
}
}
throw new NotImplementedException("Unrecognized value data type! Check the OpenAPI documentation for new types!");
}
}
}

View File

@@ -0,0 +1,59 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Models;
using Models;
namespace Parser
{
public static class RequestParser
{
public static Request ParseRequest(KeyValuePair<OperationType, OpenApiOperation> operation)
{
var request = new Request(operation.Key.ToString())
{
Summary = operation.Value.Summary,
BodyExample = GetBodyExample(operation.Value.RequestBody),
BodySchema = GetBodySchema(operation.Value.RequestBody),
UriAttributes = ParseUriAttributes(operation.Value),
Responses = ParseResponses(operation.Value)
};
return request;
}
static string GetBodyExample(OpenApiRequestBody body)
{
return body != null ? ExamplesParser.ParseExample(body.Content) : null;
}
static Dictionary<string, object> GetBodySchema(OpenApiRequestBody body)
{
return body != null ? SchemaParser.ParseSchema(body.Content) : null;
}
static List<UriAttribute> ParseUriAttributes(OpenApiOperation operation)
{
List<UriAttribute> attributes = new List<UriAttribute>();
foreach (var parameter in operation.Parameters)
{
var attribute = AttributeParser.ParseAttribute(parameter);
if (attribute != null)
{
attributes.Add(attribute);
}
}
return attributes;
}
static List<Response> ParseResponses(OpenApiOperation operation)
{
List<Response> responses = new List<Response>();
foreach (var openApiResponse in operation.Responses)
{
responses.Add(ResponseParser.ParseResponse(openApiResponse));
}
return responses;
}
}
}

View File

@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using Microsoft.OpenApi.Models;
using Models;
namespace Parser
{
public static class ResponseParser
{
public static Response ParseResponse(KeyValuePair<string, OpenApiResponse> openApiResponse)
{
string example = null;
if (openApiResponse.Value != null)
example = ExamplesParser.ParseExample(openApiResponse.Value.Content);
var response = new Response
{
Example = example,
StatusCode = ParseStatusCode(openApiResponse.Key)
};
return response;
}
static int ParseStatusCode(string responseKey)
{
if (responseKey == "default")
{
return -1;
}
if (!int.TryParse(responseKey, out var statusCode))
{
throw new NotImplementedException("Provided status code is not supported: " + responseKey);
}
return statusCode;
}
}
}

View File

@@ -0,0 +1,78 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Models;
namespace Parser
{
public static class SchemaParser
{
public static Dictionary<string, object> ParseSchema(IDictionary<string, OpenApiMediaType> contentDict)
{
return contentDict.ContainsKey("application/json") ? ParseSchemaProperties(contentDict["application/json"].Schema) : null;
}
static Dictionary<string, object> ParseSchemaProperties(OpenApiSchema schema)
{
Dictionary<string, object> parsedSchema = new Dictionary<string, object>();
if (schema.AdditionalPropertiesAllowed && schema.AdditionalProperties != null)
{
Dictionary<string, object> nestedSchema = ParseSchemaProperties(schema.AdditionalProperties);
// Create single object with string key: "AdditionalPropertyExample1" and with nested schema as it is in documentation
// There can be multiple nested objects, but we want to reduce number of generated test cases
parsedSchema.Add("AdditionalPropertyExample1", nestedSchema);
}
if (schema.AllOf.Count > 0)
{
foreach (var openApiSchema in schema.AllOf)
{
Dictionary<string, object> nestedSchema = ParseSchemaProperties(openApiSchema);
parsedSchema = MergeTwoDictionaries(nestedSchema, parsedSchema);
}
}
if (schema.OneOf.Count > 0)
{
Dictionary<string, object> nestedSchema = ParseSchemaProperties(schema.OneOf.First());
parsedSchema = MergeTwoDictionaries(nestedSchema, parsedSchema);
}
if (schema.AnyOf.Count > 0)
{
Dictionary<string, object> nestedSchema = ParseSchemaProperties(schema.AnyOf.First());
parsedSchema = MergeTwoDictionaries(nestedSchema, parsedSchema);
}
if (schema.Properties != null && schema.Properties.Count > 0)
{
foreach (var property in schema.Properties)
{
Dictionary<string, object> nestedSchema = ParseSchemaProperties(property.Value);
parsedSchema.Add(property.Key, nestedSchema);
}
}
else if (schema.Type != null && schema.Type.ToLower() == "array")
{
parsedSchema.Add("Type", schema.Type);
Dictionary<string, object> arrayItemsSchema = ParseSchemaProperties(schema.Items);
parsedSchema.Add("ArrayItemSchema", arrayItemsSchema);
}
else if (schema.Type != "object")
{
parsedSchema.Add("Title", schema.Title);
parsedSchema.Add("Type", schema.Type);
parsedSchema.Add("Format", schema.Format);
parsedSchema.Add("Example", ContentParser.GetSingleExample(schema.Example));
}
return parsedSchema;
}
static Dictionary<string, object> MergeTwoDictionaries(Dictionary<string, object> first, Dictionary<string, object> second)
{
return new List<Dictionary<string, object>> { first, second }.SelectMany(dict => dict).ToDictionary(pair => pair.Key, pair => pair.Value);
}
}
}