From fdfc5a4fb016fbc33f622bfdb4e8fd01f0e696ef Mon Sep 17 00:00:00 2001 From: Adam Schiavone Date: Fri, 30 Jul 2021 10:39:39 -0400 Subject: [PATCH 1/4] Support more formats --- .../Examples/ExampleFormats.cs | 59 +++++++++++++++++++ .../Examples/ExamplesConverter.cs | 25 +++----- .../Examples/RequestExample.cs | 39 ++++-------- .../Examples/ResponseExample.cs | 39 ++++-------- 4 files changed, 92 insertions(+), 70 deletions(-) create mode 100644 src/Swashbuckle.AspNetCore.Filters/Examples/ExampleFormats.cs diff --git a/src/Swashbuckle.AspNetCore.Filters/Examples/ExampleFormats.cs b/src/Swashbuckle.AspNetCore.Filters/Examples/ExampleFormats.cs new file mode 100644 index 0000000..595499c --- /dev/null +++ b/src/Swashbuckle.AspNetCore.Filters/Examples/ExampleFormats.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Net.Http.Headers; +using Swashbuckle.AspNetCore.Filters.Extensions; + +namespace Swashbuckle.AspNetCore.Filters.Examples +{ + internal static class ExampleFormats + { + public static readonly ExampleFormat Xml = new XmlExampleFormat(); + public static readonly ExampleFormat Json = new ExampleFormat("application/json; charset=utf-8"); + public static readonly ExampleFormat Yaml = new ExampleFormat("application/yaml; charset=utf-8"); + + public static IEnumerable All() + { + yield return Xml; + yield return Json; + yield return Yaml; + } + + public static ExampleFormat GetFormat(string mime) + { + return All().First(x => new MediaTypeHeaderValue(mime).IsSubsetOf(x.MimeType)); + } + } + + internal class ExampleFormat + { + public ExampleFormat(string mime) + { + MimeType = MediaTypeHeaderValue.Parse(mime); + } + + public MediaTypeHeaderValue MimeType { get; private set; } + + public virtual string Format(string s) + { + // NoOp in base + return s; + } + } + + internal class XmlExampleFormat : ExampleFormat + { + public XmlExampleFormat() : base("application/xml; charset=utf-8") + { + + } + + public override string Format(string s) + { + return s.FormatXml(); + } + } + + +} diff --git a/src/Swashbuckle.AspNetCore.Filters/Examples/ExamplesConverter.cs b/src/Swashbuckle.AspNetCore.Filters/Examples/ExamplesConverter.cs index 37a91fe..50ba4d5 100644 --- a/src/Swashbuckle.AspNetCore.Filters/Examples/ExamplesConverter.cs +++ b/src/Swashbuckle.AspNetCore.Filters/Examples/ExamplesConverter.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Swashbuckle.AspNetCore.Filters.Examples; namespace Swashbuckle.AspNetCore.Filters { @@ -12,6 +13,7 @@ internal class ExamplesConverter { private static readonly MediaTypeHeaderValue ApplicationXml = MediaTypeHeaderValue.Parse("application/xml; charset=utf-8"); private static readonly MediaTypeHeaderValue ApplicationJson = MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); + private static readonly MediaTypeHeaderValue ApplicationYaml = MediaTypeHeaderValue.Parse("application/yaml; charset=utf-8"); private readonly MvcOutputFormatter mvcOutputFormatter; @@ -20,26 +22,17 @@ public ExamplesConverter(MvcOutputFormatter mvcOutputFormatter) this.mvcOutputFormatter = mvcOutputFormatter; } - public IOpenApiAny SerializeExampleXml(object value) + public IOpenApiAny SerializeExample(object value, ExampleFormat format) { - return new OpenApiString(mvcOutputFormatter.Serialize(value, ApplicationXml).FormatXml()); + var stringValue = format.Format(mvcOutputFormatter.Serialize(value, format.MimeType)); + return new OpenApiRawString(stringValue); } - public IOpenApiAny SerializeExampleJson(object value) - { - return new OpenApiRawString(mvcOutputFormatter.Serialize(value, ApplicationJson)); - } - - public IDictionary ToOpenApiExamplesDictionaryXml( - IEnumerable> examples) - { - return ToOpenApiExamplesDictionary(examples, SerializeExampleXml); - } - - public IDictionary ToOpenApiExamplesDictionaryJson( - IEnumerable> examples) + public IDictionary ToOpenApiExamplesDictionary( + IEnumerable> examples, + ExampleFormat format) { - return ToOpenApiExamplesDictionary(examples, SerializeExampleJson); + return ToOpenApiExamplesDictionary(examples, x => SerializeExample(x, format)); } private static IDictionary ToOpenApiExamplesDictionary( diff --git a/src/Swashbuckle.AspNetCore.Filters/Examples/RequestExample.cs b/src/Swashbuckle.AspNetCore.Filters/Examples/RequestExample.cs index fae3083..694e699 100755 --- a/src/Swashbuckle.AspNetCore.Filters/Examples/RequestExample.cs +++ b/src/Swashbuckle.AspNetCore.Filters/Examples/RequestExample.cs @@ -7,6 +7,8 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Net.Http.Headers; +using Swashbuckle.AspNetCore.Filters.Examples; namespace Swashbuckle.AspNetCore.Filters { @@ -78,19 +80,13 @@ private IOpenApiAny SetSingleRequestExampleForOperation( object example, ExamplesConverter examplesConverter) { - var jsonExample = new Lazy(() => examplesConverter.SerializeExampleJson(example)); - var xmlExample = new Lazy(() => examplesConverter.SerializeExampleXml(example)); - foreach (var content in operation.RequestBody.Content) { - if (content.Key.Contains("xml")) - { - content.Value.Example = xmlExample.Value; - } - else - { - content.Value.Example = jsonExample.Value; - } + var format = ExampleFormats.GetFormat(content.Key); + if (format == null) + continue; // fail more gracefully? + + content.Value.Example = examplesConverter.SerializeExample(example, format); } return operation.RequestBody.Content.FirstOrDefault().Value?.Example; @@ -105,24 +101,13 @@ private IOpenApiAny SetMultipleRequestExamplesForOperation( IEnumerable> examples, ExamplesConverter examplesConverter) { - var jsonExamples = new Lazy>(() => - examplesConverter.ToOpenApiExamplesDictionaryJson(examples) - ); - - var xmlExamples = new Lazy>(() => - examplesConverter.ToOpenApiExamplesDictionaryXml(examples) - ); - foreach (var content in operation.RequestBody.Content) { - if (content.Key.Contains("xml")) - { - content.Value.Examples = xmlExamples.Value; - } - else - { - content.Value.Examples = jsonExamples.Value; - } + var format = ExampleFormats.GetFormat(content.Key); + if (format == null) + continue; // fail more gracefully? + + content.Value.Examples = examplesConverter.ToOpenApiExamplesDictionary(examples, format); } return operation.RequestBody.Content.FirstOrDefault().Value?.Examples?.FirstOrDefault().Value?.Value; diff --git a/src/Swashbuckle.AspNetCore.Filters/Examples/ResponseExample.cs b/src/Swashbuckle.AspNetCore.Filters/Examples/ResponseExample.cs index 85619ae..36af25d 100755 --- a/src/Swashbuckle.AspNetCore.Filters/Examples/ResponseExample.cs +++ b/src/Swashbuckle.AspNetCore.Filters/Examples/ResponseExample.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Swashbuckle.AspNetCore.Filters.Examples; namespace Swashbuckle.AspNetCore.Filters { @@ -52,20 +53,15 @@ private void SetSingleResponseExampleForStatusCode( object example, ExamplesConverter examplesConverter) { - var jsonExample = new Lazy(() => examplesConverter.SerializeExampleJson(example)); - var xmlExample = new Lazy(() => examplesConverter.SerializeExampleXml(example)); - foreach (var content in response.Value.Content) { - if (content.Key.Contains("xml")) - { - content.Value.Example = xmlExample.Value; - } - else - { - content.Value.Example = jsonExample.Value; - } + var format = ExampleFormats.GetFormat(content.Key); + if (format == null) + continue; // fail more gracefully? + + content.Value.Example = examplesConverter.SerializeExample(example, format); } + } private void SetMultipleResponseExampleForStatusCode( @@ -73,24 +69,13 @@ private void SetMultipleResponseExampleForStatusCode( IEnumerable> examples, ExamplesConverter examplesConverter) { - var jsonExamples = new Lazy>(() => - examplesConverter.ToOpenApiExamplesDictionaryJson(examples) - ); - - var xmlExamples = new Lazy>(() => - examplesConverter.ToOpenApiExamplesDictionaryXml(examples) - ); - foreach (var content in response.Value.Content) { - if (content.Key.Contains("xml")) - { - content.Value.Examples = xmlExamples.Value; - } - else - { - content.Value.Examples = jsonExamples.Value; - } + var format = ExampleFormats.GetFormat(content.Key); + if (format == null) + continue; // fail more gracefully? + + content.Value.Examples = examplesConverter.ToOpenApiExamplesDictionary(examples, format); } } } From f6469e6223e34113f6034afd233c6f18c1a48fbb Mon Sep 17 00:00:00 2001 From: Adam Schiavone Date: Fri, 30 Jul 2021 11:49:58 -0400 Subject: [PATCH 2/4] Cleanup --- CHANGELOG.md | 5 ++ src/Directory.Build.props | 4 +- .../Examples/ExampleFormats.cs | 59 ------------------- .../Examples/ExamplesConverter.cs | 7 +-- .../Examples/Formats/ExampleFormat.cs | 20 +++++++ .../Examples/Formats/ExampleFormats.cs | 27 +++++++++ .../Examples/Formats/JsonExampleFormat.cs | 7 +++ .../Examples/Formats/XmlExampleFormat.cs | 18 ++++++ .../Examples/Formats/YamlExampleFormat.cs | 14 +++++ 9 files changed, 94 insertions(+), 67 deletions(-) delete mode 100644 src/Swashbuckle.AspNetCore.Filters/Examples/ExampleFormats.cs create mode 100644 src/Swashbuckle.AspNetCore.Filters/Examples/Formats/ExampleFormat.cs create mode 100644 src/Swashbuckle.AspNetCore.Filters/Examples/Formats/ExampleFormats.cs create mode 100644 src/Swashbuckle.AspNetCore.Filters/Examples/Formats/JsonExampleFormat.cs create mode 100644 src/Swashbuckle.AspNetCore.Filters/Examples/Formats/XmlExampleFormat.cs create mode 100644 src/Swashbuckle.AspNetCore.Filters/Examples/Formats/YamlExampleFormat.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index b3f885e..74b5e4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [7.0.3] - 2021-07-30 +### Added +- Support for YAML examples +- Infrastructure to easily add more supported formats + ## [7.0.2] - 2021-04-03 ### Fixed - Fixed License diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 7a8c2c8..c9d5b6c 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -2,7 +2,7 @@ netstandard2.0;netcoreapp3.1;net5.0 - 7.0.2 + 7.0.3 Matt Frear Some additional useful filters for Swashbuckle.AspNetCore. This package replaces Swashbuckle.AspNetCore.Examples. false @@ -19,7 +19,7 @@ - 7.0.2 + 7.0.3 True ..\key.snk diff --git a/src/Swashbuckle.AspNetCore.Filters/Examples/ExampleFormats.cs b/src/Swashbuckle.AspNetCore.Filters/Examples/ExampleFormats.cs deleted file mode 100644 index 595499c..0000000 --- a/src/Swashbuckle.AspNetCore.Filters/Examples/ExampleFormats.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Microsoft.Net.Http.Headers; -using Swashbuckle.AspNetCore.Filters.Extensions; - -namespace Swashbuckle.AspNetCore.Filters.Examples -{ - internal static class ExampleFormats - { - public static readonly ExampleFormat Xml = new XmlExampleFormat(); - public static readonly ExampleFormat Json = new ExampleFormat("application/json; charset=utf-8"); - public static readonly ExampleFormat Yaml = new ExampleFormat("application/yaml; charset=utf-8"); - - public static IEnumerable All() - { - yield return Xml; - yield return Json; - yield return Yaml; - } - - public static ExampleFormat GetFormat(string mime) - { - return All().First(x => new MediaTypeHeaderValue(mime).IsSubsetOf(x.MimeType)); - } - } - - internal class ExampleFormat - { - public ExampleFormat(string mime) - { - MimeType = MediaTypeHeaderValue.Parse(mime); - } - - public MediaTypeHeaderValue MimeType { get; private set; } - - public virtual string Format(string s) - { - // NoOp in base - return s; - } - } - - internal class XmlExampleFormat : ExampleFormat - { - public XmlExampleFormat() : base("application/xml; charset=utf-8") - { - - } - - public override string Format(string s) - { - return s.FormatXml(); - } - } - - -} diff --git a/src/Swashbuckle.AspNetCore.Filters/Examples/ExamplesConverter.cs b/src/Swashbuckle.AspNetCore.Filters/Examples/ExamplesConverter.cs index 50ba4d5..5eb5a49 100644 --- a/src/Swashbuckle.AspNetCore.Filters/Examples/ExamplesConverter.cs +++ b/src/Swashbuckle.AspNetCore.Filters/Examples/ExamplesConverter.cs @@ -11,10 +11,6 @@ namespace Swashbuckle.AspNetCore.Filters { internal class ExamplesConverter { - private static readonly MediaTypeHeaderValue ApplicationXml = MediaTypeHeaderValue.Parse("application/xml; charset=utf-8"); - private static readonly MediaTypeHeaderValue ApplicationJson = MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); - private static readonly MediaTypeHeaderValue ApplicationYaml = MediaTypeHeaderValue.Parse("application/yaml; charset=utf-8"); - private readonly MvcOutputFormatter mvcOutputFormatter; public ExamplesConverter(MvcOutputFormatter mvcOutputFormatter) @@ -24,8 +20,7 @@ public ExamplesConverter(MvcOutputFormatter mvcOutputFormatter) public IOpenApiAny SerializeExample(object value, ExampleFormat format) { - var stringValue = format.Format(mvcOutputFormatter.Serialize(value, format.MimeType)); - return new OpenApiRawString(stringValue); + return format.Format(mvcOutputFormatter.Serialize(value, format.MimeType)); } public IDictionary ToOpenApiExamplesDictionary( diff --git a/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/ExampleFormat.cs b/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/ExampleFormat.cs new file mode 100644 index 0000000..f4f321c --- /dev/null +++ b/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/ExampleFormat.cs @@ -0,0 +1,20 @@ +using Microsoft.Net.Http.Headers; +using Microsoft.OpenApi.Any; + +namespace Swashbuckle.AspNetCore.Filters.Examples +{ + internal abstract class ExampleFormat + { + public ExampleFormat(string mime) + { + MimeType = MediaTypeHeaderValue.Parse(mime); + } + + public MediaTypeHeaderValue MimeType { get; } + + public virtual IOpenApiAny Format(string s) + { + return new OpenApiRawString(s); + } + } +} \ No newline at end of file diff --git a/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/ExampleFormats.cs b/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/ExampleFormats.cs new file mode 100644 index 0000000..07f5762 --- /dev/null +++ b/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/ExampleFormats.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Net.Http.Headers; + +namespace Swashbuckle.AspNetCore.Filters.Examples +{ + internal static class ExampleFormats + { + public static readonly ExampleFormat Xml = new XmlExampleFormat(); + public static readonly ExampleFormat Json = new JsonExampleFormat(); + public static readonly ExampleFormat Yaml = new YamlExampleFormat(); + + public static IEnumerable All() + { + yield return Xml; + yield return Json; + yield return Yaml; + } + + public static ExampleFormat GetFormat(string mime) + { + return All().FirstOrDefault(x => new MediaTypeHeaderValue(mime).IsSubsetOf(x.MimeType)); + } + } +} diff --git a/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/JsonExampleFormat.cs b/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/JsonExampleFormat.cs new file mode 100644 index 0000000..2e1bfbc --- /dev/null +++ b/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/JsonExampleFormat.cs @@ -0,0 +1,7 @@ +namespace Swashbuckle.AspNetCore.Filters.Examples +{ + internal class JsonExampleFormat : ExampleFormat + { + public JsonExampleFormat() : base("application/json") { } + } +} \ No newline at end of file diff --git a/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/XmlExampleFormat.cs b/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/XmlExampleFormat.cs new file mode 100644 index 0000000..3581a9c --- /dev/null +++ b/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/XmlExampleFormat.cs @@ -0,0 +1,18 @@ +using Microsoft.OpenApi.Any; +using Swashbuckle.AspNetCore.Filters.Extensions; + +namespace Swashbuckle.AspNetCore.Filters.Examples +{ + internal class XmlExampleFormat : ExampleFormat + { + public XmlExampleFormat() : base("application/xml; charset=utf-8") + { + + } + + public override IOpenApiAny Format(string s) + { + return base.Format(s.FormatXml()); + } + } +} \ No newline at end of file diff --git a/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/YamlExampleFormat.cs b/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/YamlExampleFormat.cs new file mode 100644 index 0000000..4255232 --- /dev/null +++ b/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/YamlExampleFormat.cs @@ -0,0 +1,14 @@ +using Microsoft.OpenApi.Any; + +namespace Swashbuckle.AspNetCore.Filters.Examples +{ + internal class YamlExampleFormat : ExampleFormat + { + public YamlExampleFormat() : base("application/yaml") { } + + public override IOpenApiAny Format(string s) + { + return new OpenApiString(s); + } + } +} \ No newline at end of file From d2d10f65aadc4d544a9fc26123b4145d45074071 Mon Sep 17 00:00:00 2001 From: Adam Schiavone Date: Fri, 30 Jul 2021 12:00:58 -0400 Subject: [PATCH 3/4] Missed one --- .../Examples/Formats/XmlExampleFormat.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/XmlExampleFormat.cs b/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/XmlExampleFormat.cs index 3581a9c..e2fbb93 100644 --- a/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/XmlExampleFormat.cs +++ b/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/XmlExampleFormat.cs @@ -5,7 +5,7 @@ namespace Swashbuckle.AspNetCore.Filters.Examples { internal class XmlExampleFormat : ExampleFormat { - public XmlExampleFormat() : base("application/xml; charset=utf-8") + public XmlExampleFormat() : base("application/xml") { } From e8bca5967a5ee95cba8a1c46f74c3f80a590b8f7 Mon Sep 17 00:00:00 2001 From: Adam Schiavone Date: Fri, 30 Jul 2021 12:04:28 -0400 Subject: [PATCH 4/4] XML to OpenApiString --- .../Examples/Formats/XmlExampleFormat.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/XmlExampleFormat.cs b/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/XmlExampleFormat.cs index e2fbb93..61063e3 100644 --- a/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/XmlExampleFormat.cs +++ b/src/Swashbuckle.AspNetCore.Filters/Examples/Formats/XmlExampleFormat.cs @@ -12,7 +12,7 @@ public XmlExampleFormat() : base("application/xml") public override IOpenApiAny Format(string s) { - return base.Format(s.FormatXml()); + return new OpenApiString(s.FormatXml()); } } } \ No newline at end of file