Can I enforce constraints set in OpenApiCustomiser to regular API calls? - spring

In my Spring project, I use OpenAPI 3 definitions for my api docs, derived from Gradle springdoc-openapi-ui library:
compile group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.5.0'
There are constraints that I set from the Swagger Config, including default length and pattern for those not custom set at the lower level, as seen below:
public int setStringContraints(Schema schema){
if(schema.getMaxLength() == null){
schema.setMaxLength(255);
}
if(schema.getPattern() == null){
schema.setPattern("^.*$");
}
return 0;
}
This enforces the maximum length in my Swagger UI. For example, using ID path variable "11111...<256+ chars>" will result in the API command not firing and highlight the invalid ID.
However, if I try the same ID as the path variable in my browser, the API function still executes. Is it possible to enforce the OpenAPI restriction on the actual server as well?

Related

Problem with Protostream and UUID in Infinispan 13.0.0.Final

I'm using Infinispan 13.0.0.final with the default marshaller (protobuf). When I try to use UUID fields in my datatypes
data class CounterState(
#get:ProtoField(number = 1) var index: Long? = null,
#get:ProtoField(number = 2) var uuid: UUID? = null
)
I get the following error at build time:
.../gradle-kotlin-protobuf/build/tmp/kapt3/stubs/main/io/radiosphere/ProtoSchema.java:8: error: org.infinispan.protostream.annotations.ProtoSchemaBuilderException: The class java.util.UUID must be instantiable using an accessible no-argument constructor.
public abstract interface ProtoSchema extends org.infinispan.protostream.GeneratedSchema {
It seems like I'm not allowed to use UUID in my types unless I generate a protoschema for it, but since UUID is a class outside of my control I can't do this.
Previous questions on the topic have gotten the suggestion to use the JavaSerializationMarshaller, but I want to solve this while still using the Protostream Marshaller. It has also been suggested that this would be fixed in version 12.0.0 here.
An example of this not working can be found here. Note that this project will not build because of the annotation processing failing as mentioned above. If it would build the proof that it is working would be shown by running the main project (ie. not the tests).
The question becomes: What do I need to do to configure UUID to be usable in my protobuf marshalled classes in Infinispan 13? Both for embedded and for a program using the hotrod client?
EDIT:
Based on a given answer I have also tried doing the following:
#AutoProtoSchemaBuilder(
includeClasses = [UUIDAdapter::class, CounterState::class],
schemaPackageName = "tutorial")
interface ProtoSchema : GeneratedSchema {
}
This makes the build work, but when starting Quarkus I get the following error:
Caused by: org.infinispan.protostream.DescriptorParserException: Duplicate type id 1005 for type org.infinispan.protostream.commons.UUID. Already used by tutorial.UUID
at org.infinispan.protostream.descriptors.ResolutionContext.checkUniqueTypeId(ResolutionContext.java:151)
at org.infinispan.protostream.descriptors.ResolutionContext.addGenericDescriptor(ResolutionContext.java:97)
at org.infinispan.protostream.descriptors.FileDescriptor.collectDescriptors(FileDescriptor.java:313)
at org.infinispan.protostream.descriptors.FileDescriptor.resolveDependencies(FileDescriptor.java:245)
at org.infinispan.protostream.descriptors.FileDescriptor.resolveDependencies(FileDescriptor.java:210)
at org.infinispan.protostream.descriptors.ResolutionContext.resolve(ResolutionContext.java:57)
at org.infinispan.protostream.impl.SerializationContextImpl.registerProtoFiles(SerializationContextImpl.java:127)
at org.infinispan.protostream.types.java.CommonTypesSchema.registerSchema(CommonTypesSchema.java:49)
at org.infinispan.client.hotrod.RemoteCacheManager.registerSerializationContextInitializer(RemoteCacheManager.java:422)
at org.infinispan.client.hotrod.RemoteCacheManager.registerDefaultSchemas(RemoteCacheManager.java:437)
at org.infinispan.client.hotrod.RemoteCacheManager.initializeProtoStreamMarshaller(RemoteCacheManager.java:409)
at org.infinispan.client.hotrod.RemoteCacheManager.actualStart(RemoteCacheManager.java:365)
at org.infinispan.client.hotrod.RemoteCacheManager.start(RemoteCacheManager.java:334)
at org.infinispan.client.hotrod.RemoteCacheManager.<init>(RemoteCacheManager.java:192)
at org.infinispan.client.hotrod.RemoteCacheManager.<init>(RemoteCacheManager.java:149)
at io.quarkus.infinispan.client.runtime.InfinispanClientProducer.initialize(InfinispanClientProducer.java:68)
If I instead change to use dependsOn like this:
#AutoProtoSchemaBuilder(
includeClasses = [CounterState::class],
dependsOn = [org.infinispan.protostream.types.java.CommonTypes::class, org.infinispan.protostream.types.java.CommonContainerTypes::class],
schemaPackageName = "tutorial")
I'm back to the build failing with:
error: org.infinispan.protostream.annotations.ProtoSchemaBuilderException: The class java.util.UUID must be instantiable using an accessible no-argument constructor.
public abstract interface ProtoSchema extends org.infinispan.protostream.GeneratedSchema {
It seems to be like Quarkus and the Annotation processor are getting in each others way here when it comes to having a simple working solution for UUID marshalling.
You have to include the org.infinispan.protostream.types.java.util.UUIDAdapter class in your annotation:
#AutoProtoSchemaBuilder(includeClasses = [CounterState::class, UUIDAdapter::class] , schemaPackageName = "tutorial")
For more info, check the documentation page.

Need wire to emit interfaces for client and server in same gradle project

I want to use interfaces for both client and server in the same android app. Usecase is to run a okhttpmockwebserver serving gRPC requests within the same app the client is running in. For this i created two library projects with their own wire configuration for client and server similar to those
wire {
kotlin {
includes = ['com..caompany.android.proto.*']
out "${buildDir}/protos"
rpcCallStyle = 'suspending'
rpcRole = 'client'
}
}
wire {
kotlin {
includes = ['com..company.android.proto.*']
out "${buildDir}/protos"
rpcCallStyle = 'suspending'
rpcRole = 'server'
}
}
Executing the wire-gradle-plugin fails with this exception:
com.company.android.proto.HelloReply$Companion$ADAPTER$1 is defined multiple times.
Caused by: com.android.tools.r8.CompilationFailedException: Compilation failed to complete, origin: .../com/company/android/proto/HelloReply$Companion$ADAPTER$1.dex
It would help me if wire could either
Generate all classes and interfaces at once including server and client role or
Exclude the generation of class files, only generating service interfaces for client or server
Is there a workaround i can achieve a similar result without gradle plugin support?
You can have multiple kotlin blocks at the same time. Wire will throw if you generate the same class twice so you need to define the rule as unique between both.
You need one block which will generate client role interfaces. You need one block to generate server roles interfaces. Lastly, you need to generate regular types in yet another block, or in one of them (but not both).
Something like this
wire {
kotlin {
includes = ['all.services.or.package']
rpcCallStyle = 'suspending'
rpcRole = 'client'
}
kotlin {
includes = ['all.services.or.package']
rpcCallStyle = 'suspending'
rpcRole = 'server'
}
kotlin {
excludes = ['all.services.or.package']
rpcRole = 'none'
}
}

How to get values provided by Groovy DSL from Kotlin in Gradle

Suppose following configuration:
build.dependencies.gradle:
ext {
libraries = [:]
}
libraries += [
library : [group: 'com.example', name: 'library', version: '1.1.1']
]
build.gradle.kts:
apply(from = "build.dependencies.gradle")
dependencies {
implementation(libraries["library"]) // does not work
}
Is there a way to get values provided by Groovy script in build.gradle.kts?
It doesn’t work because Kotlin is statically/strongly typed language unlike Groovy. libraries is not defined on any object from Gradle’s API.
You can access it like so:
dependencies {
implementation((project.extra["libraries"] as LinkedHashMap<*, *>)["library"]!!)
}
println(project.extra["libraries"])
project.extra[“libraries”] returns an Object so we need to cast it correctly in order to get the next value. It is also marked as #Nullable so hence the !! operator.
—
A better way to manage dependency versions is to leverage Java Platform plugin.

ConfigurationProperties loading list from YML

I'm trying to load Configuration from YML. I can load value and I can also load list if these are comma seperated values. But i can't load a typical YML List.
Configuration Class
#Component
#PropertySource("classpath:routing.yml")
#ConfigurationProperties
class RoutingProperties(){
var angular = listOf("nothing")
var value: String = ""
}
Working routing.yml
angular: /init, /home
value: Hello World
Not Working routing.yml
angular:
- init
- home
value: Hello World
Why can't i load the second version of yml / do I have a syntaxt error?
ENV: Kotlin, Spring 2.0.0.M3
As #flyx say, #PropetySource not worked with yaml files. But in spring you may override almost everything :)
PropertySource has additional parameter: factory. It's possible to create your own PropertySourceFactory base on DefaultPropertySourceFactory
open class YamlPropertyLoaderFactory : DefaultPropertySourceFactory() {
override fun createPropertySource(name: String?, resource: EncodedResource?): org.springframework.core.env.PropertySource<*> {
if (resource == null)
return super.createPropertySource(name, resource)
return YamlPropertySourceLoader().load(resource.resource.filename, resource.resource, null)
}
}
And when use this factory in propertysource annotation:
#PropertySource("classpath:/routing.yml", factory = YamlPropertyLoaderFactory::class)
Last that you need is to initialized variable angular with mutableList
Full code sample:
#Component
#PropertySource("classpath:/routing.yml", factory = YamlPropertyLoaderFactory::class)
#ConfigurationProperties
open class RoutingProperties {
var angular = mutableListOf("nothing")
var value: String = ""
override fun toString(): String {
return "RoutingProperties(angular=$angular, value='$value')"
}
}
open class YamlPropertyLoaderFactory : DefaultPropertySourceFactory() {
override fun createPropertySource(name: String?, resource: EncodedResource?): org.springframework.core.env.PropertySource<*> {
if (resource == null)
return super.createPropertySource(name, resource)
return YamlPropertySourceLoader().load(resource.resource.filename, resource.resource, null)
}
}
#SpringBootApplication
#EnableAutoConfiguration(exclude = arrayOf(DataSourceAutoConfiguration::class))
open class Application {
companion object {
#JvmStatic
fun main(args: Array<String>) {
val context = SpringApplication.run(Application::class.java, *args)
val bean = context.getBean(RoutingProperties::class.java)
println(bean)
}
}
}
Kinda old post, i know. But i am at the very same topic right now.
As of now, it seems that PropertySource does indeed work with yaml Files. Given the restriction that it only allows for primitive types (it seems) and it cant handle nested elements. I'm probably gonna dig a bit deeper and update my answer accordingly, but as of now, the accepted answer seems like a functioning workaround.
Well, according to the docs, your YAML file will be rewritten into a property file. The first YAML file becomes:
angular=/init, /home
value=Hello World
While the second one becomes:
angular[0]=init
angular[1]=home
value=Hello World
These are obviously two very different things and therefore behave differently.
Moreover, later in the docs, it is stated that YAML does not even work with #PropertySource:
24.6.4 YAML shortcomings
YAML files can’t be loaded via the #PropertySource annotation. So in the case that you need to load values that way, you need to use a properties file.
That makes me kind of wonder why the first case works for you at all.
The docs say this about the generated …[index] properties:
To bind to properties like that using the Spring DataBinder utilities (which is what #ConfigurationProperties does) you need to have a property in the target bean of type java.util.List (or Set) and you either need to provide a setter, or initialize it with a mutable value, e.g. this will bind to the properties above
So, let's have a look at Kotlin docs: listOf returns a new read-only list of given elements. So the list is not mutable as required by the docs, which I assume is why it doesn't work. Try using a mutable list (since I have never used Kotlin, I cannot give you working code). Also try to declare it as java.util.List if that's possible in Kotlin.

Getting started with swashbuckle for blank web.api in vs2015

This is a beginner question. In 2015 I created a blank web.api project. I then added Swashbuckle 5.5.3 nuget package. I created a common controller listed below.
namespace StarterApi.Controllers
{
public class CommonController : ApiController
{
[HttpGet]
public HttpResponseMessage GetTestList(int id)
{
HttpResponseMessage msg = null;
try
{
//var principal = User as ClaimsPrincipal;
List<DDLDispValueVM> ctrList = new List<DDLDispValueVM> {
new DDLDispValueVM { Disp="one", Value="1"},
new DDLDispValueVM { Disp="two", Value="2"}
};
msg = Request.CreateResponse(HttpStatusCode.OK, ctrList);
}
catch (Exception ex)
{
msg = Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
}
return msg;
}
}
}
When I go to the url I get the swagger screen, but it is missing the Controller.
See Image.
I have a GitHub of the project at...
TestSwashbuckle
Here is the SwaggerConfig file
using System.Web.Http;
using WebActivatorEx;
using StarterApi;
using Swashbuckle.Application;
[assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")]
namespace StarterApi
{
public class SwaggerConfig
{
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
GlobalConfiguration.Configuration.EnableSwagger(c =>
{
// By default, the service root url is inferred from the request used to access the docs.
// However, there may be situations (e.g. proxy and load-balanced environments) where this does not
// resolve correctly. You can workaround this by providing your own code to determine the root URL.
//
//c.RootUrl(req => GetRootUrlFromAppConfig());
// If schemes are not explicitly provided in a Swagger 2.0 document, then the scheme used to access
// the docs is taken as the default. If your API supports multiple schemes and you want to be explicit
// about them, you can use the "Schemes" option as shown below.
//
//c.Schemes(new[] { "http", "https" });
// Use "SingleApiVersion" to describe a single version API. Swagger 2.0 includes an "Info" object to
// hold additional metadata for an API. Version and title are required but you can also provide
// additional fields by chaining methods off SingleApiVersion.
//
c.SingleApiVersion("v1", "StarterApi");
// If your API has multiple versions, use "MultipleApiVersions" instead of "SingleApiVersion".
// In this case, you must provide a lambda that tells Swashbuckle which actions should be
// included in the docs for a given API version. Like "SingleApiVersion", each call to "Version"
// returns an "Info" builder so you can provide additional metadata per API version.
//
//c.MultipleApiVersions(
// (apiDesc, targetApiVersion) => ResolveVersionSupportByRouteConstraint(apiDesc, targetApiVersion),
// (vc) =>
// {
// vc.Version("v2", "Swashbuckle Dummy API V2");
// vc.Version("v1", "Swashbuckle Dummy API V1");
// });
// You can use "BasicAuth", "ApiKey" or "OAuth2" options to describe security schemes for the API.
// See https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md for more details.
// NOTE: These only define the schemes and need to be coupled with a corresponding "security" property
// at the document or operation level to indicate which schemes are required for an operation. To do this,
// you'll need to implement a custom IDocumentFilter and/or IOperationFilter to set these properties
// according to your specific authorization implementation
//
//c.BasicAuth("basic")
// .Description("Basic HTTP Authentication");
//
// NOTE: You must also configure 'EnableApiKeySupport' below in the SwaggerUI section
//c.ApiKey("apiKey")
// .Description("API Key Authentication")
// .Name("apiKey")
// .In("header");
//
//c.OAuth2("oauth2")
// .Description("OAuth2 Implicit Grant")
// .Flow("implicit")
// .AuthorizationUrl("http://petstore.swagger.wordnik.com/api/oauth/dialog")
// //.TokenUrl("https://tempuri.org/token")
// .Scopes(scopes =>
// {
// scopes.Add("read", "Read access to protected resources");
// scopes.Add("write", "Write access to protected resources");
// });
// Set this flag to omit descriptions for any actions decorated with the Obsolete attribute
//c.IgnoreObsoleteActions();
// Each operation be assigned one or more tags which are then used by consumers for various reasons.
// For example, the swagger-ui groups operations according to the first tag of each operation.
// By default, this will be controller name but you can use the "GroupActionsBy" option to
// override with any value.
//
//c.GroupActionsBy(apiDesc => apiDesc.HttpMethod.ToString());
// You can also specify a custom sort order for groups (as defined by "GroupActionsBy") to dictate
// the order in which operations are listed. For example, if the default grouping is in place
// (controller name) and you specify a descending alphabetic sort order, then actions from a
// ProductsController will be listed before those from a CustomersController. This is typically
// used to customize the order of groupings in the swagger-ui.
//
//c.OrderActionGroupsBy(new DescendingAlphabeticComparer());
// If you annotate Controllers and API Types with
// Xml comments (http://msdn.microsoft.com/en-us/library/b2s063f7(v=vs.110).aspx), you can incorporate
// those comments into the generated docs and UI. You can enable this by providing the path to one or
// more Xml comment files.
//
//c.IncludeXmlComments(GetXmlCommentsPath());
// Swashbuckle makes a best attempt at generating Swagger compliant JSON schemas for the various types
// exposed in your API. However, there may be occasions when more control of the output is needed.
// This is supported through the "MapType" and "SchemaFilter" options:
//
// Use the "MapType" option to override the Schema generation for a specific type.
// It should be noted that the resulting Schema will be placed "inline" for any applicable Operations.
// While Swagger 2.0 supports inline definitions for "all" Schema types, the swagger-ui tool does not.
// It expects "complex" Schemas to be defined separately and referenced. For this reason, you should only
// use the "MapType" option when the resulting Schema is a primitive or array type. If you need to alter a
// complex Schema, use a Schema filter.
//
//c.MapType<ProductType>(() => new Schema { type = "integer", format = "int32" });
// If you want to post-modify "complex" Schemas once they've been generated, across the board or for a
// specific type, you can wire up one or more Schema filters.
//
//c.SchemaFilter<ApplySchemaVendorExtensions>();
// In a Swagger 2.0 document, complex types are typically declared globally and referenced by unique
// Schema Id. By default, Swashbuckle does NOT use the full type name in Schema Ids. In most cases, this
// works well because it prevents the "implementation detail" of type namespaces from leaking into your
// Swagger docs and UI. However, if you have multiple types in your API with the same class name, you'll
// need to opt out of this behavior to avoid Schema Id conflicts.
//
//c.UseFullTypeNameInSchemaIds();
// Alternatively, you can provide your own custom strategy for inferring SchemaId's for
// describing "complex" types in your API.
//
//c.SchemaId(t => t.FullName.Contains('`') ? t.FullName.Substring(0, t.FullName.IndexOf('`')) : t.FullName);
// Set this flag to omit schema property descriptions for any type properties decorated with the
// Obsolete attribute
//c.IgnoreObsoleteProperties();
// In accordance with the built in JsonSerializer, Swashbuckle will, by default, describe enums as integers.
// You can change the serializer behavior by configuring the StringToEnumConverter globally or for a given
// enum type. Swashbuckle will honor this change out-of-the-box. However, if you use a different
// approach to serialize enums as strings, you can also force Swashbuckle to describe them as strings.
//
//c.DescribeAllEnumsAsStrings();
// Similar to Schema filters, Swashbuckle also supports Operation and Document filters:
//
// Post-modify Operation descriptions once they've been generated by wiring up one or more
// Operation filters.
//
//c.OperationFilter<AddDefaultResponse>();
//
// If you've defined an OAuth2 flow as described above, you could use a custom filter
// to inspect some attribute on each action and infer which (if any) OAuth2 scopes are required
// to execute the operation
//
//c.OperationFilter<AssignOAuth2SecurityRequirements>();
// Post-modify the entire Swagger document by wiring up one or more Document filters.
// This gives full control to modify the final SwaggerDocument. You should have a good understanding of
// the Swagger 2.0 spec. - https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md
// before using this option.
//
//c.DocumentFilter<ApplyDocumentVendorExtensions>();
// In contrast to WebApi, Swagger 2.0 does not include the query string component when mapping a URL
// to an action. As a result, Swashbuckle will raise an exception if it encounters multiple actions
// with the same path (sans query string) and HTTP method. You can workaround this by providing a
// custom strategy to pick a winner or merge the descriptions for the purposes of the Swagger docs
//
//c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
// Wrap the default SwaggerGenerator with additional behavior (e.g. caching) or provide an
// alternative implementation for ISwaggerProvider with the CustomProvider option.
//
//c.CustomProvider((defaultProvider) => new CachingSwaggerProvider(defaultProvider));
})
.EnableSwaggerUi(c =>
{
// Use the "InjectStylesheet" option to enrich the UI with one or more additional CSS stylesheets.
// The file must be included in your project as an "Embedded Resource", and then the resource's
// "Logical Name" is passed to the method as shown below.
//
//c.InjectStylesheet(containingAssembly, "Swashbuckle.Dummy.SwaggerExtensions.testStyles1.css");
// Use the "InjectJavaScript" option to invoke one or more custom JavaScripts after the swagger-ui
// has loaded. The file must be included in your project as an "Embedded Resource", and then the resource's
// "Logical Name" is passed to the method as shown above.
//
//c.InjectJavaScript(thisAssembly, "Swashbuckle.Dummy.SwaggerExtensions.testScript1.js");
// The swagger-ui renders boolean data types as a dropdown. By default, it provides "true" and "false"
// strings as the possible choices. You can use this option to change these to something else,
// for example 0 and 1.
//
//c.BooleanValues(new[] { "0", "1" });
// By default, swagger-ui will validate specs against swagger.io's online validator and display the result
// in a badge at the bottom of the page. Use these options to set a different validator URL or to disable the
// feature entirely.
//c.SetValidatorUrl("http://localhost/validator");
//c.DisableValidator();
// Use this option to control how the Operation listing is displayed.
// It can be set to "None" (default), "List" (shows operations for each resource),
// or "Full" (fully expanded: shows operations and their details).
//
//c.DocExpansion(DocExpansion.List);
// Specify which HTTP operations will have the 'Try it out!' option. An empty paramter list disables
// it for all operations.
//
//c.SupportedSubmitMethods("GET", "HEAD");
// Use the CustomAsset option to provide your own version of assets used in the swagger-ui.
// It's typically used to instruct Swashbuckle to return your version instead of the default
// when a request is made for "index.html". As with all custom content, the file must be included
// in your project as an "Embedded Resource", and then the resource's "Logical Name" is passed to
// the method as shown below.
//
//c.CustomAsset("index", containingAssembly, "YourWebApiProject.SwaggerExtensions.index.html");
// If your API has multiple versions and you've applied the MultipleApiVersions setting
// as described above, you can also enable a select box in the swagger-ui, that displays
// a discovery URL for each version. This provides a convenient way for users to browse documentation
// for different API versions.
//
//c.EnableDiscoveryUrlSelector();
// If your API supports the OAuth2 Implicit flow, and you've described it correctly, according to
// the Swagger 2.0 specification, you can enable UI support as shown below.
//
//c.EnableOAuth2Support(
// clientId: "test-client-id",
// clientSecret: null,
// realm: "test-realm",
// appName: "Swagger UI"
// //additionalQueryStringParams: new Dictionary<string, string>() { { "foo", "bar" } }
//);
// If your API supports ApiKey, you can override the default values.
// "apiKeyIn" can either be "query" or "header"
//
//c.EnableApiKeySupport("apiKey", "header");
});
}
}
}
4 items are still needed:
Uncomment the line in the SwaggerConfig Register() method that says c.IncludeXmlComments(GetXmlCommentsPath());
Add a private static string property to your SwaggerConfig class that specifies the path for the XML
private static string GetXmlCommentsPath()
{
var path = string.Format(#"{0}bin\StarterAPI.XML", System.AppDomain.CurrentDomain.BaseDirectory);
return path;
}
Generate XML by changing your build options as follows: in Project Properties, Build tab, Output section CHECK the XML documentation file and make it read: bin\StarterAPI.xml Notice that it is the same path as generated in the previous step...this links the build XML creation to the SwaggerConfig consumer.
Lastly, annotate your controller methods with /// tags , [HttpGet] annotations and [Route("")] annotations. The sample below looks mostly complete:
// GET api/Stuff/17205
/// <summary>
/// Returns Stuff record based on supplied Stuff_Id
/// </summary>
/// <param name="stuffId"></param>
/// <returns></returns>
[HttpGet]
[Route("api/Stuff/{stuffId}")]
public Stuff_Request Get(int stuffId) {
With Swashbuckle version 5.6.0 (actually you probably need only Swashbuckle.Core nuget package) you can configure it very easy.
Create SwaggerConfig class that looks like this:
public static class SwaggerConfig
{
public static void Register(HttpConfiguration configuration)
{
configuration.EnableSwagger(Configure).EnableSwaggerUi(ConfigureUi);
}
static void Configure(SwaggerDocsConfig config)
{
config.SingleApiVersion("V1", "MichalBialecki.com.Swagger.Example");
config.UseFullTypeNameInSchemaIds();
}
static void ConfigureUi(SwaggerUiConfig config)
{
config.DocExpansion(DocExpansion.None);
config.DisableValidator();
}
}
You also need to register it in Startup.cs:
SwaggerConfig.Register(config);
And that's it. It works for me.

Resources