GithubHelp home page GithubHelp logo

burgyn / mmlib.swaggerforocelot Goto Github PK

View Code? Open in Web Editor NEW
338.0 11.0 93.0 844 KB

This repo contains swagger extension for ocelot.

License: MIT License

C# 100.00%
swagger gateway ocelot api-gateway dotnet

mmlib.swaggerforocelot's Introduction

logo

Publish package

SwaggerForOcelot combines two amazing projects Swashbuckle.AspNetCore and Ocelot. Allows you to view and use swagger documentation for downstream services directly through the Ocelot project.

Direct via http://ocelotprojecturl:port/swagger provides documentation for downstream services configured in ocelot.json. Additionally, the addresses are modified to match the UpstreamPathTemplate from the configuration.

SwaggerForOcelot


Did this project help you? You can now buy me a coffee ☕️.

Buy Me A Coffee

Get Started

  1. Configure SwaggerGen in your downstream services.

    Follow the SwashbuckleAspNetCore documentation.

  2. Install Nuget package into yout ASP.NET Core Ocelot project.

    dotnet add package MMLib.SwaggerForOcelot

  3. Configure SwaggerForOcelot in ocelot.json.
 {
  "Routes": [
    {
      "DownstreamPathTemplate": "/api/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5100
        }
      ],
      "UpstreamPathTemplate": "/api/contacts/{everything}",
      "UpstreamHttpMethod": [ "Get" ],
      "SwaggerKey": "contacts"
    },
    {
      "DownstreamPathTemplate": "/api/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5200
        }
      ],
      "UpstreamPathTemplate": "/api/orders/{everything}",
      "UpstreamHttpMethod": [ "Get" ],
      "SwaggerKey": "orders"
    }
  ],
  "SwaggerEndPoints": [
    {
      "Key": "contacts",
      "Config": [
        {
          "Name": "Contacts API",
          "Version": "v1",
          "Url": "http://localhost:5100/swagger/v1/swagger.json"
        }
      ]
    },
    {
      "Key": "orders",
      "Config": [
        {
          "Name": "Orders API",
          "Version": "v0.9",
          "Url": "http://localhost:5200/swagger/v0.9/swagger.json"
        },
        {
          "Name": "Orders API",
          "Version": "v1",
          "Url": "http://localhost:5200/swagger/v1/swagger.json"
        },
        {
          "Name": "Orders API",
          "Version": "v2",
          "Url": "http://localhost:5200/swagger/v2/swagger.json"
        },
        {
          "Name": "Orders API",
          "Version": "v3",
          "Url": "http://localhost:5200/swagger/v3/swagger.json"
        }
      ]
    }
  ],
  "GlobalConfiguration": {
    "BaseUrl": "http://localhost"
  }
}

SwaggerEndPoint is configuration for downstream service swagger generator endpoint. Property Key is used to pair with the Route configuration. Name is displayed in the combobox. Url is downstream service swagger generator endpoint.

  1. In the ConfigureServices method of Startup.cs, register the SwaggerForOcelot generator.
services.AddSwaggerForOcelot(Configuration);
  1. In Configure method, insert the SwaggerForOcelot middleware to expose interactive documentation.
app.UseSwaggerForOcelotUI(opt => {
  opt.PathToSwaggerGenerator = "/swagger/docs";
})

You can optionally include headers that your Ocelot Gateway will send when requesting a swagger endpoint. This can be especially useful if your downstream microservices require contents from a header to authenticate.

app.UseSwaggerForOcelotUI(opt => {
  opt.DownstreamSwaggerHeaders = new[]
  {
      new KeyValuePair<string, string>("Auth-Key", "AuthValue"),
  };
})

After swagger for ocelot transforms the downstream swagger to the upstream swagger, you have the ability to alter the upstream swagger if you need to by setting the ReConfigureUpstreamSwaggerJson option or ReConfigureUpstreamSwaggerJsonAsync option for async methods.

public string AlterUpstreamSwaggerJson(HttpContext context, string swaggerJson)
{
  var swagger = JObject.Parse(swaggerJson);
  // ... alter upstream json
  return swagger.ToString(Formatting.Indented);
}

app.UseSwaggerForOcelotUI(opt => {
  opt.ReConfigureUpstreamSwaggerJson = AlterUpstreamSwaggerJson;
})

You can optionally customize the swagger server prior to calling the endpoints of the microservices as follows:

app.UseSwaggerForOcelotUI(opt => {
    opt.ReConfigureUpstreamSwaggerJson = AlterUpstreamSwaggerJson;
    opt.ServerOcelot = "/siteName/apigateway" ;
})

You can optionally customize SwaggerUI:

app.UseSwaggerForOcelotUI(opt => {
    // swaggerForOcelot options
}, uiOpt => {
    //swaggerUI options
    uiOpt.DocumentTitle = "Gateway documentation";
})
  1. Show your microservices interactive documentation.

    http://ocelotserviceurl/swagger

Open API Servers

If you have multiple servers defined in the downstream service Open API documentation, or you use server templating and you want to use it on the gateway side as well, then you must explicitly enable it on the Swagger endpoint definition by setting property TakeServersFromDownstreamService to true.

"SwaggerEndPoints": [
    {
      "Key": "users",
      "TakeServersFromDownstreamService": true,
      "Config": [
        {
          "Name": "Users API",
          "Version": "v1",
          "Service": {
            "Name": "users",
            "Path": "/swagger/v1/swagger.json"
          }
        }
      ]
    }
]

⚠ If you set TakeServersFromDownstreamService to true, then the server path is not used to transform the paths of individual endpoints.

Virtual directory

If you have a downstream service hosted in the virtual directory, you probably have a DownstreamPathTemplate starting with the name of this virtual directory /virtualdirectory/api/{everything}. In order to properly replace the paths, it is necessary to set the property route "Virtualdirectory":"/virtualdirectory".

Example:

 {
  "DownstreamPathTemplate": "/project/api/{everything}",
  "DownstreamScheme": "http",
  "DownstreamHostAndPorts": [
      {
      "Host": "localhost",
      "Port": 5100
      }
  ],
  "UpstreamPathTemplate": "/api/project/{everything}",
  "UpstreamHttpMethod": [ "Get" ],
  "SwaggerKey": "project",
  "VirtualDirectory":"/project"
}

Service discovery

If you use Ocelot Service Discovery Provider to find the host and port for the downstream service, then you can use the same service name for swagger configuration.

"Routes": [
  {
    "DownstreamPathTemplate": "/api/{everything}",
    "ServiceName": "projects",
    "UpstreamPathTemplate": "/api/project/{everything}",
    "SwaggerKey": "projects",
  }
],
 "SwaggerEndPoints": [
    {
      "Key": "projects",
      "Config": [
        {
          "Name": "Projects API",
          "Version": "v1",
          "Service": {
            "Name": "projects",
            "Path": "/swagger/v1/swagger.json"
          }
        }
      ]
    }
  ],

  "GlobalConfiguration": {
    "ServiceDiscoveryProvider": {
      "Type": "AppConfiguration",
      "PollingInterval": 1000
    }
  }

The Gateway documentation itself

There are several real scenarios when you need to have a controller directly in your gateway. For example: specific aggregation of results from multiple services / legacy part of your system / ...

If you need to, you can also add documentation.

  1. Allow GenerateDocsForGatewayItSelf option in configuration section.
services.AddSwaggerForOcelot(Configuration,
  (o) =>
  {
      o.GenerateDocsForGatewayItSelf = true;
  });

or you can provide more options for gateway itself documentation

services.AddSwaggerForOcelot(Configuration,
  (o) =>
  {
      o.GenerateDocsDocsForGatewayItSelf(opt =>
      {
          opt.FilePathsForXmlComments = { "MyAPI.xml" };
          opt.GatewayDocsTitle = "My Gateway";
          opt.GatewayDocsOpenApiInfo = new()
          {
             Title = "My Gateway",
             Version = "v1",
          };
          opt.DocumentFilter<MyDocumentFilter>();
          opt.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
          {
              Description = @"JWT Authorization header using the Bearer scheme. Enter 'Bearer' [space] and then your token in the text input below. Example: 'Bearer 12345abcdef'",
              Name = "Authorization",
              In = ParameterLocation.Header,
              Type = SecuritySchemeType.ApiKey,
              Scheme = "Bearer"
          });
          opt.AddSecurityRequirement(new OpenApiSecurityRequirement()
          {
              {
                  new OpenApiSecurityScheme
                  {
                      Reference = new OpenApiReference
                      {
                          Type = ReferenceType.SecurityScheme,
                          Id = "Bearer"
                      },
                      Scheme = "oauth2",
                      Name = "Bearer",
                      In = ParameterLocation.Header,
                  },
                  new List<string>()
              }
          });
      });
  });
  1. Use Swagger generator in Configure section.
app.UseSwagger();

ocelot docs

Documentation of Ocelot Aggregates

You are probably familiar with Ocelot great feature Request Aggregation. Request Aggregation allows you to easily add a new endpoint to the gateway that will aggregate the result from other existing endpoints. If you use these aggregations, you would probably want to have these endpoints in the api documentation as well.

📢 From version 3.0.0 you can use this package for generating documentation for Ocelot aggregates.

In ConfigureServices allow GenerateDocsForAggregates option.

services.AddSwaggerForOcelot(Configuration,
  (o) =>
  {
      o.GenerateDocsForAggregates = true;
  });

Documentations of your aggregates will be available on custom page Aggregates. aggregates docs

The current implementation may not cover all scenarios (I hope most of them), but there are several ways you can change the final documentation.

Custom description

By default, this package generate description from downstream documentation. If you want add custom description for your aggregate route, you can add description to ocelot.json.

"Aggregates": [ 
  {
    "RouteKeys": [
      "user",
      "basket"
    ],
    "Description": "Custom description for this aggregate route.",
    "Aggregator": "BasketAggregator",
    "UpstreamPathTemplate": "/gateway/api/basketwithuser/{id}"
  }
]

Different parameter names

It is likely that you will have different parameter names in the downstream services that you are aggregating. For example, in the User service you will have the {Id} parameter, but in the Basket service the same parameter will be called {BuyerId}. In order for Ocelot aggregations to work, you must have parameters named the same in Ocelot configurations, but this will make it impossible to find the correct documentation.

Therefore, you can help the configuration by setting parameter name map.

{
  "DownstreamPathTemplate": "/api/basket/{id}",
  "UpstreamPathTemplate": "/gateway/api/basket/{id}",
  "ParametersMap": {
    "id": "buyerId"
  },
  "ServiceName": "basket",
  "SwaggerKey": "basket",
  "Key": "basket"
}

Property ParametersMap is map, where key (first parameter) is the name of parameter in Ocelot configuration and value (second parameter) is the name of parameter in downstream service.

Custom aggregator

The response documentation is generated according to the rules that Ocelot uses to compose the response from the aggregate. If you use your custom IDefinedAggregator, your result may be different. In this case you can use AggregateResponseAttibute.

[AggregateResponse("Basket with buyer and busket items.", typeof(CustomResponse))]
public class BasketAggregator : IDefinedAggregator
{
    public async Task<DownstreamResponse> Aggregate(List<HttpContext> responses)
    {
        ...
    }
}

Modifying the generated documentation

If you do not like the final documentation, you can modify it by defining your custom postprocessor.

services.AddSwaggerForOcelot(Configuration,
  (o) =>
  {
      o.GenerateDocsForAggregates = true;
      o.AggregateDocsGeneratorPostProcess = (aggregateRoute, routesDocs, pathItemDoc, documentation) =>
      {
          if (aggregateRoute.UpstreamPathTemplate == "/gateway/api/basketwithuser/{id}")
          {
              pathItemDoc.Operations[OperationType.Get].Parameters.Add(new OpenApiParameter()
              {
                  Name = "customParameter",
                  Schema = new OpenApiSchema() { Type = "string"},
                  In = ParameterLocation.Header
              });
          }
      };
  });

If none of this is enough

🙏 Feel free to provide a PR with implementation of your scenario. You will probably help many others.

Merging configuration files

Optionally you can use the Ocelot feature Merging configuration files to load the apigateway configuration from multiple configuration files named as follows: ocelot.exampleName.json. To activate this feature you need to use the following extension:

WebHost.CreateDefaultBuilder(args)
  .ConfigureAppConfiguration((hostingContext, config) =>
  {
      config.AddOcelotWithSwaggerSupport();
  })
  .UseStartup<Startup>();

Using this extension the swagger path settings must be in a file called: ocelot.SwaggerEndPoints.json. If instead you want to use another name for this file you could set the name as follows (without the .json extension):

WebHost.CreateDefaultBuilder(args)
  .ConfigureAppConfiguration((hostingContext, config) =>
  {
     config.AddOcelotWithSwaggerSupport((o) => {
       o.FileOfSwaggerEndPoints = "ocelot.swagger";
     })
  })
  .UseStartup<Startup>();

Optionally you can put the configuration files in a folder, and for that you have to set the extension as follows:

WebHost.CreateDefaultBuilder(args)
  .ConfigureAppConfiguration((hostingContext, config) =>
  {
    config.AddOcelotWithSwaggerSupport((o) => {
      o.Folder = "Configuration";
    });
  })
  .UseStartup<Startup>();

Optionally you can also add configuration files with the format ocelot.exampleName.json per environment, to use this functionality you must configure the extension as follows:

WebHost.CreateDefaultBuilder(args)
  .ConfigureAppConfiguration((hostingContext, config) =>
  {
    config.AddOcelotWithSwaggerSupport((o) => {
      o.Folder = "Configuration";
      o.Environment = hostingContext.HostingEnvironment;
    });
  })
  .UseStartup<Startup>();

To save the primary Ocelot config file under a name other than `ocelot.json then use the following:

WebHost.CreateDefaultBuilder(args)
  .ConfigureAppConfiguration((hostingContext, config) =>
  {
    config.AddOcelotWithSwaggerSupport((o) => {
      o.PrimaryOcelotConfigFile = "myOcelot.json";
    });
  })
  .UseStartup<Startup>();

Control downstream to swagger api

With the ISwaggerDownstreamInterceptor interface you are able to inject your own logic to control the downstream.

  1. In the ConfigureServices method of Startup.cs, register your downstream interceptor along with your other dependencies.
services.AddSingleton<ISwaggerDownstreamInterceptor, PublishedDownstreamInterceptor>();
services.AddSingleton<ISwaggerEndpointConfigurationRepository, DummySwaggerEndpointRepository>();
  1. In your downstream interceptor add your custom logic to control if the downstream should be done.
public class PublishedDownstreamInterceptor : ISwaggerDownstreamInterceptor
{
    private readonly ISwaggerEndpointConfigurationRepository _endpointConfigurationRepository;

    public PublishedDownstreamInterceptor(ISwaggerEndpointConfigurationRepository endpointConfigurationRepository)
    {
        _endpointConfigurationRepository = endpointConfigurationRepository;
    }

    public bool DoDownstreamSwaggerEndpoint(HttpContext httpContext, string version, SwaggerEndPointOptions endPoint)
    {
        var myEndpointConfiguration = _endpointConfigurationRepository.GetSwaggerEndpoint(endPoint, version);

        if (!myEndpointConfiguration.IsPublished)
        {
            httpContext.Response.StatusCode = 404;
            httpContext.Response.WriteAsync("This enpoint is under development, please come back later.");
        }

        return myEndpointConfiguration.IsPublished;
    }
}

Note, the service is still visible in the swagger ui the response is only visible in the request to the downstream url. If you want to control the visibility of the endpoints as well you have to implement a custom swagger ui.

Security definition generation

It is possible to generate security definitions for the enpoints based on Ocelot configuration

  1. Add AuthenticationOptions to your route definition
"Routes": [
  {
    "DownstreamPathTemplate": "/api/{everything}",
    "ServiceName": "projects",
    "UpstreamPathTemplate": "/api/project/{everything}",
    "SwaggerKey": "projects",
    "AuthenticationOptions": {
      "AuthenticationProviderKey": "Bearer",
      "AllowedScopes": [ "scope" ]
    },
  }
]
  1. Provide a mapping in Startup between AuthenticationProviderKey and it's corresponding securityDefintion
services.AddSwaggerForOcelot(Configuration,
  (o) =>
  {
    o.AddAuthenticationProviderKeyMapping("Bearer", "appAuth");
  });
  1. Now you should have security definitions on your swagger documents
{
  "paths": {
    "/api/project": {
      "get": {
        ...
        "security": [
          {
            "appAuth": [ "scope" ]
          }
        ]
      }
    }
  }
}

Note, this does not affect nor checks the swagger document's securityDefinitions property.

Downstream Documentation Caching

If your downstream documentation is too large, the response time may be slow. To address this issue, you can enable caching of transformed documentation by setting the DownstreamDocsCacheExpire parameter. If this parameter is not provided, the documentation won't be cached. If there is any change in the downstream documentation, the cache will be refreshed.

services.AddSwaggerForOcelot(Configuration,
            setup =>
            {
                setup.DownstreamDocsCacheExpire = TimeSpan.FromMinutes(10);
            });

Limitation

  • Now, this library support only {everything} as a wildcard in routing definition. #68
  • This package unfortunately does not support parameter translating between upstream and downstream path template. #59
  • If your downstream documentation is too large (usually more than 10 MB), the response may be too slow. You can turn off the removal of an unused schema component from downstream documentation by using RemoveUnusedComponentsFromScheme: false.
 "SwaggerEndPoints": [
    {
      "Key": "projects",
      "RemoveUnusedComponentsFromScheme": false,
      "Config": [
        {
          "Name": "Projects API",
          "Version": "v1",
          "Service": {
            "Name": "projects",
            "Path": "/swagger/v1/swagger.json"
          }
        }
      ]
    }
  ]

Version 6.0.0

⚠️ Breaking change #240 - new way to modify swagger UI configuration.

Version 2.0.0

This version is breaking change. Because support Ocelot 16.0.0, which rename ReRoutes to Routes. See Ocelot v16.0.0.


If you have read this readme to the end, please let me know by clicking this link.

mmlib.swaggerforocelot's People

Contributors

amir-chehri avatar arcalise08 avatar arsslensoft avatar bodnarkevin avatar burgyn avatar catalingoran avatar cguyonnet avatar dadjh85 avatar danadesrosiers avatar dependabot[bot] avatar duncan-g avatar eymentopcuoglu avatar felpasl avatar gametrojaner avatar hantsch avatar jhines-sf avatar jkosters avatar jondmcelroy avatar materalcmx avatar mikado3780 avatar raga70 avatar rasmus715 avatar rklfss avatar satano avatar simbadltd avatar tcsidevtools avatar teomane avatar tomer-cohen avatar ussamoo avatar vedadiyan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mmlib.swaggerforocelot's Issues

Fetch Error

Describe the bug
Fetch error: undefined /swagger/docs/v1/documents

Expected behavior
A clear and concise description of what you expected to happen.

To Reproduce
If it is a possible attache:

  1. Original downstream swagger.json.
{
  "openapi": "3.0.1",
  "info": {
    "title": "Dum.QueryService API",
    "version": "v1"
  },
  "paths": {
    "/api/CategoriaEvento/GetCategoriaEventoProperties": {
      "get": {
        "tags": [
          "CategoriaEvento"
        ],
        "parameters": [
          {
            "name": "categoriaEventoSelected",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/VOC_EventoObject"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VOC_EventoObject"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/VOC_EventoObject"
                }
              }
            }
          }
        }
      }
    },
    "/api/CategoriaEvento/GetFormaDocumentoProperties": {
      "get": {
        "tags": [
          "CategoriaEvento"
        ],
        "parameters": [
          {
            "name": "formaDocumentoSelected",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/VOC_EventoObject"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VOC_EventoObject"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/VOC_EventoObject"
                }
              }
            }
          }
        }
      }
    },
    "/api/CategoriaEvento/GetTipologicaList": {
      "get": {
        "tags": [
          "CategoriaEvento"
        ],
        "parameters": [
          {
            "name": "tableName",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          },
          {
            "name": "tipologica",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/CategoriaEvento/GetFormaDocumentoList": {
      "get": {
        "tags": [
          "CategoriaEvento"
        ],
        "parameters": [
          {
            "name": "categoriaEvento",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/Documento/Description": {
      "get": {
        "tags": [
          "Documento"
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "application/json": {
                "schema": {
                  "type": "string"
                }
              },
              "text/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/Documento/GetDocumento": {
      "get": {
        "tags": [
          "Documento"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentoObject"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentoObject"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentoObject"
                }
              }
            }
          }
        }
      }
    },
    "/api/Documento/GetAllDocumento": {
      "get": {
        "tags": [
          "Documento"
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DocumentoObject"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DocumentoObject"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DocumentoObject"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/Documento/GetDocumentSearchList": {
      "get": {
        "tags": [
          "Documento"
        ],
        "parameters": [
          {
            "name": "id_utente",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          },
          {
            "name": "marcatura",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": -1
            }
          },
          {
            "name": "data_marcatura",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "",
              "nullable": true
            }
          },
          {
            "name": "data_marcatura_al",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "",
              "nullable": true
            }
          },
          {
            "name": "ente",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "",
              "nullable": true
            }
          },
          {
            "name": "forma",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "",
              "nullable": true
            }
          },
          {
            "name": "numero_documento",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "",
              "nullable": true
            }
          },
          {
            "name": "data_documento",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "",
              "nullable": true
            }
          },
          {
            "name": "data_documento_al",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "",
              "nullable": true
            }
          },
          {
            "name": "uor_mittente",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "",
              "nullable": true
            }
          },
          {
            "name": "numero_protocollo",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "",
              "nullable": true
            }
          },
          {
            "name": "data_protocollo",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "",
              "nullable": true
            }
          },
          {
            "name": "data_protocollo_al",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "",
              "nullable": true
            }
          },
          {
            "name": "aoo_mittente",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "",
              "nullable": true
            }
          },
          {
            "name": "matricola",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DocumentiView_ResultObject"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DocumentiView_ResultObject"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DocumentiView_ResultObject"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/Documento/GetLastDocumentoById": {
      "get": {
        "tags": [
          "Documento"
        ],
        "parameters": [
          {
            "name": "id_utente",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "$ref": "#/components/schemas/DocumentoObject"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "$ref": "#/components/schemas/DocumentoObject"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "$ref": "#/components/schemas/DocumentoObject"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/Documento/GetDaMarcareNonAssegnateAMe": {
      "get": {
        "tags": [
          "Documento"
        ],
        "parameters": [
          {
            "name": "id_utente",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "application/json": {
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "text/json": {
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            }
          }
        }
      }
    },
    "/api/Documento/GetDaMarcareAssegnateAMe": {
      "get": {
        "tags": [
          "Documento"
        ],
        "parameters": [
          {
            "name": "id_utente",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "application/json": {
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "text/json": {
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            }
          }
        }
      }
    },
    "/api/Documento/GetSenzaVariazioni": {
      "get": {
        "tags": [
          "Documento"
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "application/json": {
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "text/json": {
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            }
          }
        }
      }
    },
    "/api/Documento/GetUORById": {
      "get": {
        "tags": [
          "Documento"
        ],
        "parameters": [
          {
            "name": "id_utente",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "application/json": {
                "schema": {
                  "type": "string"
                }
              },
              "text/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/Documento/GetAooById": {
      "get": {
        "tags": [
          "Documento"
        ],
        "parameters": [
          {
            "name": "id_utente",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "application/json": {
                "schema": {
                  "type": "string"
                }
              },
              "text/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/Matricola/GetLastMatricolaById": {
      "get": {
        "tags": [
          "Matricola"
        ],
        "parameters": [
          {
            "name": "id_utente",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "$ref": "#/components/schemas/MatricolaObject"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "$ref": "#/components/schemas/MatricolaObject"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "$ref": "#/components/schemas/MatricolaObject"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/Matricola/GetMatricolaSearchList": {
      "get": {
        "tags": [
          "Matricola"
        ],
        "parameters": [
          {
            "name": "id_utente",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          },
          {
            "name": "matricola",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "",
              "nullable": true
            }
          },
          {
            "name": "cognome",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "",
              "nullable": true
            }
          },
          {
            "name": "nome",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "",
              "nullable": true
            }
          },
          {
            "name": "codice_fiscale",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "",
              "nullable": true
            }
          },
          {
            "name": "data_nascita",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "",
              "nullable": true
            }
          },
          {
            "name": "grado",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "",
              "nullable": true
            }
          },
          {
            "name": "posizione",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": -1
            }
          },
          {
            "name": "categoria",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "",
              "nullable": true
            }
          },
          {
            "name": "ufficio",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MatricolaViewObject"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MatricolaViewObject"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MatricolaViewObject"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/Matricola/GetMatricola": {
      "get": {
        "tags": [
          "Matricola"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/MatricolaObject"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MatricolaObject"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/MatricolaObject"
                }
              }
            }
          }
        }
      }
    },
    "/api/Matricola/GetMatricolaListByUser": {
      "get": {
        "tags": [
          "Matricola"
        ],
        "parameters": [
          {
            "name": "id_utente",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MatricolaObject"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MatricolaObject"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MatricolaObject"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/Profilo/GetIdUtente": {
      "get": {
        "tags": [
          "Profilo"
        ],
        "parameters": [
          {
            "name": "nome_utente",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "application/json": {
                "schema": {
                  "type": "string"
                }
              },
              "text/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/Profilo/GetUfficiobyId": {
      "get": {
        "tags": [
          "Profilo"
        ],
        "parameters": [
          {
            "name": "id_utente",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "application/json": {
                "schema": {
                  "type": "string"
                }
              },
              "text/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/Profilo/GetRuoloById": {
      "get": {
        "tags": [
          "Profilo"
        ],
        "parameters": [
          {
            "name": "id_utente",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "application/json": {
                "schema": {
                  "type": "string"
                }
              },
              "text/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/Profilo/GetArticolazioneUtenteById": {
      "get": {
        "tags": [
          "Profilo"
        ],
        "parameters": [
          {
            "name": "id_utente",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "application/json": {
                "schema": {
                  "type": "string"
                }
              },
              "text/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/Profilo/GetComandoUtenteById": {
      "get": {
        "tags": [
          "Profilo"
        ],
        "parameters": [
          {
            "name": "id_utente",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "application/json": {
                "schema": {
                  "type": "string"
                }
              },
              "text/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/Profilo/GetVOC_ArticolazioneUtenteById": {
      "get": {
        "tags": [
          "Profilo"
        ],
        "parameters": [
          {
            "name": "id_utente",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "application/json": {
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "text/json": {
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            }
          }
        }
      }
    },
    "/api/Profilo/GetVOC_ComandoUtenteById": {
      "get": {
        "tags": [
          "Profilo"
        ],
        "parameters": [
          {
            "name": "id_utente",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "application/json": {
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "text/json": {
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            }
          }
        }
      }
    },
    "/api/Variazione/GetContingenteListByIdMatricola": {
      "get": {
        "tags": [
          "Variazione"
        ],
        "parameters": [
          {
            "name": "id_matricola",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ContingenteObject"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ContingenteObject"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ContingenteObject"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "VOC_EventoObject": {
        "type": "object",
        "properties": {
          "annotazioni": {
            "type": "string",
            "nullable": true
          },
          "autoritaDefault": {
            "type": "string",
            "nullable": true
          },
          "bloccato": {
            "type": "boolean",
            "nullable": true
          },
          "descrizione": {
            "type": "string",
            "nullable": true
          },
          "enteDefault": {
            "type": "string",
            "nullable": true
          },
          "formaDefault": {
            "type": "string",
            "nullable": true
          },
          "haAutorita": {
            "type": "boolean",
            "nullable": true
          },
          "haData": {
            "type": "boolean",
            "nullable": true
          },
          "haDataEvento": {
            "type": "boolean",
            "nullable": true
          },
          "haDescrizioneEnte": {
            "type": "boolean",
            "nullable": true
          },
          "haEnte": {
            "type": "boolean",
            "nullable": true
          },
          "haLegislazione": {
            "type": "boolean",
            "nullable": true
          },
          "haNumero": {
            "type": "boolean",
            "nullable": true
          },
          "haOrganodiControllo": {
            "type": "boolean",
            "nullable": true
          },
          "haReparto": {
            "type": "boolean",
            "nullable": true
          },
          "iD_Evento": {
            "type": "integer",
            "format": "int32"
          },
          "iD_Livello": {
            "type": "integer",
            "format": "int32"
          },
          "iD_Tipologia": {
            "type": "integer",
            "format": "int32"
          },
          "organodiControlloDefault": {
            "type": "string",
            "nullable": true
          },
          "periodo_Fine": {
            "type": "string",
            "format": "date-time"
          },
          "periodo_Inizio": {
            "type": "string",
            "format": "date-time"
          },
          "repartoDefault": {
            "type": "string",
            "nullable": true
          },
          "tipoRaccoltaDefault": {
            "type": "string",
            "nullable": true
          },
          "voC_AutoritaDefault": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_CategoriaEvento": {
            "type": "integer",
            "format": "int32"
          },
          "voC_EnteDefault": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_FormaDefault": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_FormaDocumento": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_OrganodiControlloDefault": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_RepartoDefault": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_TipoRaccoltaDefault": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "DocumentoObject": {
        "required": [
          "categoriaEvento",
          "dataEvento",
          "formaDocumento"
        ],
        "type": "object",
        "properties": {
          "aoO_destinazione": {
            "type": "string",
            "nullable": true
          },
          "aoO_mittente": {
            "type": "string",
            "nullable": true
          },
          "articolazioneUtente": {
            "type": "string",
            "nullable": true
          },
          "autorita": {
            "type": "string",
            "nullable": true
          },
          "categoriaEvento": {
            "type": "string"
          },
          "comandoUtente": {
            "type": "string",
            "nullable": true
          },
          "data_Documento": {
            "type": "string",
            "format": "date"
          },
          "data_Marcatura": {
            "type": "string",
            "format": "date"
          },
          "data_Protocollo": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "data_Protocollo_Mittente": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "dataEvento": {
            "type": "string",
            "format": "date"
          },
          "dataRegistrazione": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "descrizione_Ente": {
            "type": "string",
            "nullable": true
          },
          "ente": {
            "type": "string",
            "nullable": true
          },
          "formaDocumento": {
            "type": "string"
          },
          "iD_Documento": {
            "type": "integer",
            "format": "int32"
          },
          "iD_DocumentoRiferimento": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "iD_Utente": {
            "type": "string",
            "nullable": true
          },
          "legge": {
            "type": "string",
            "nullable": true
          },
          "link_Documento": {
            "type": "string",
            "nullable": true
          },
          "num_Documento": {
            "type": "string",
            "nullable": true
          },
          "num_Protocollo": {
            "type": "string",
            "nullable": true
          },
          "num_Protocollo_Mittente": {
            "type": "string",
            "nullable": true
          },
          "numeroFoglio": {
            "type": "string",
            "nullable": true
          },
          "numeroRegistro": {
            "type": "string",
            "nullable": true
          },
          "oggetto": {
            "type": "string",
            "nullable": true
          },
          "organodiControllo": {
            "type": "string",
            "nullable": true
          },
          "origine": {
            "type": "boolean"
          },
          "progTipoDiRaccolta": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "protAnnulato": {
            "type": "boolean",
            "nullable": true
          },
          "reparto": {
            "type": "string",
            "nullable": true
          },
          "tipoDiRaccolta": {
            "type": "string",
            "nullable": true
          },
          "uoR_destinazione": {
            "type": "string",
            "nullable": true
          },
          "uoR_mittente": {
            "type": "string",
            "nullable": true
          },
          "utente": {
            "type": "string",
            "nullable": true
          },
          "voC_ArticolazioneUtente": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_Autorita": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_CategoriaEvento": {
            "type": "integer",
            "format": "int32"
          },
          "voC_ComandoUtente": {
            "type": "integer",
            "format": "int32"
          },
          "voC_Ente": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_FormaDocumento": {
            "type": "integer",
            "format": "int32"
          },
          "voC_Legge": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_OrganoDiControllo": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_Reparto": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_TipoDiRaccolta": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "DocumentiView_ResultObject": {
        "type": "object",
        "properties": {
          "aoO_destinazione": {
            "type": "string",
            "nullable": true
          },
          "aoO_mittente": {
            "type": "string",
            "nullable": true
          },
          "articolazioneUtente": {
            "type": "string",
            "nullable": true
          },
          "autorita": {
            "type": "string",
            "nullable": true
          },
          "categoriaEvento": {
            "type": "string",
            "nullable": true
          },
          "comandoUtente": {
            "type": "string",
            "nullable": true
          },
          "data_Documento": {
            "type": "string",
            "format": "date-time"
          },
          "data_Marcatura": {
            "type": "string",
            "format": "date-time"
          },
          "data_Protocollo": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "data_Protocollo_Mittente": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "dataEvento": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "dataRegistrazione": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "descrizione_Ente": {
            "type": "string",
            "nullable": true
          },
          "ente": {
            "type": "string",
            "nullable": true
          },
          "formaDocumento": {
            "type": "string",
            "nullable": true
          },
          "iD_Documento": {
            "type": "integer",
            "format": "int32"
          },
          "iD_DocumentoRiferimento": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "iD_Utente": {
            "type": "string",
            "nullable": true
          },
          "legge": {
            "type": "string",
            "nullable": true
          },
          "link_Documento": {
            "type": "string",
            "nullable": true
          },
          "matricolaMeccanografica": {
            "type": "string",
            "nullable": true
          },
          "num_Documento": {
            "type": "string",
            "nullable": true
          },
          "num_Protocollo": {
            "type": "string",
            "nullable": true
          },
          "num_Protocollo_Mittente": {
            "type": "string",
            "nullable": true
          },
          "numeroFoglio": {
            "type": "string",
            "nullable": true
          },
          "numeroRegistro": {
            "type": "string",
            "nullable": true
          },
          "oggetto": {
            "type": "string",
            "nullable": true
          },
          "organodiControllo": {
            "type": "string",
            "nullable": true
          },
          "origine": {
            "type": "boolean"
          },
          "progTipoDiRaccolta": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "protAnnulato": {
            "type": "boolean",
            "nullable": true
          },
          "reparto": {
            "type": "string",
            "nullable": true
          },
          "tipoDiRaccolta": {
            "type": "string",
            "nullable": true
          },
          "uoR_destinazione": {
            "type": "string",
            "nullable": true
          },
          "uoR_mittente": {
            "type": "string",
            "nullable": true
          },
          "utente": {
            "type": "string",
            "nullable": true
          },
          "voC_ArticolazioneUtente": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_Autorita": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_CategoriaEvento": {
            "type": "integer",
            "format": "int32"
          },
          "voC_ComandoUtente": {
            "type": "integer",
            "format": "int32"
          },
          "voC_Ente": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_FormaDocumento": {
            "type": "integer",
            "format": "int32"
          },
          "voC_Legge": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_OrganoDiControllo": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_Reparto": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_TipoDiRaccolta": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "MatricolaObject": {
        "type": "object",
        "properties": {
          "articolazione": {
            "type": "string",
            "nullable": true
          },
          "bloccato": {
            "type": "boolean",
            "nullable": true
          },
          "categoria": {
            "type": "string",
            "nullable": true
          },
          "categoria_nello_Stato": {
            "type": "string",
            "nullable": true
          },
          "cessazione_dal_Servizio": {
            "type": "string",
            "nullable": true
          },
          "codice_Fiscale": {
            "type": "string",
            "nullable": true
          },
          "cognome": {
            "type": "string",
            "nullable": true
          },
          "comune_Nascita": {
            "type": "string",
            "nullable": true
          },
          "contingente": {
            "type": "string",
            "nullable": true
          },
          "data_Arruolamento": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "data_Cessazione_dal_Servizio": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "data_Nascita": {
            "type": "string",
            "format": "date-time"
          },
          "grado": {
            "type": "string",
            "nullable": true
          },
          "iD_Matricola": {
            "type": "integer",
            "format": "int32"
          },
          "iD_Tipologia": {
            "type": "integer",
            "format": "int32"
          },
          "localita_Nascita": {
            "type": "string",
            "nullable": true
          },
          "matricola_CEMM": {
            "type": "string",
            "nullable": true
          },
          "matricolaMeccanografica": {
            "type": "string",
            "nullable": true
          },
          "nome": {
            "type": "string",
            "nullable": true
          },
          "posizione_Servizio": {
            "type": "string",
            "nullable": true
          },
          "posizione_Stato": {
            "type": "string",
            "nullable": true
          },
          "provincia_Nascita": {
            "type": "string",
            "nullable": true
          },
          "reparto": {
            "type": "string",
            "nullable": true
          },
          "ruolo": {
            "type": "string",
            "nullable": true
          },
          "sesso": {
            "type": "string",
            "nullable": true
          },
          "stato_Estero_Nascita": {
            "type": "string",
            "nullable": true
          },
          "titolo_Studio": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "MatricolaViewObject": {
        "type": "object",
        "properties": {
          "articolazione": {
            "type": "string",
            "nullable": true
          },
          "bloccato": {
            "type": "boolean",
            "nullable": true
          },
          "categoria": {
            "type": "string",
            "nullable": true
          },
          "categoria_nello_Stato": {
            "type": "string",
            "nullable": true
          },
          "cessazione_dal_Servizio": {
            "type": "string",
            "nullable": true
          },
          "codice_Fiscale": {
            "type": "string",
            "nullable": true
          },
          "cognome": {
            "type": "string",
            "nullable": true
          },
          "comune_Nascita": {
            "type": "string",
            "nullable": true
          },
          "contingente": {
            "type": "string",
            "nullable": true
          },
          "data_Arruolamento": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "data_Cessazione_dal_Servizio": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "data_Nascita": {
            "type": "string",
            "format": "date-time"
          },
          "grado": {
            "type": "string",
            "nullable": true
          },
          "iD_Matricola": {
            "type": "integer",
            "format": "int32"
          },
          "iD_Tipologia": {
            "type": "integer",
            "format": "int32"
          },
          "iD_Utente": {
            "type": "string",
            "nullable": true
          },
          "iD_UtenteContesto": {
            "type": "integer",
            "format": "int32"
          },
          "localita_Nascita": {
            "type": "string",
            "nullable": true
          },
          "matricola_CEMM": {
            "type": "string",
            "nullable": true
          },
          "matricolaMeccanografica": {
            "type": "string",
            "nullable": true
          },
          "nome": {
            "type": "string",
            "nullable": true
          },
          "posizione_Servizio": {
            "type": "string",
            "nullable": true
          },
          "posizione_Stato": {
            "type": "string",
            "nullable": true
          },
          "provincia_Nascita": {
            "type": "string",
            "nullable": true
          },
          "reparto": {
            "type": "string",
            "nullable": true
          },
          "ruolo": {
            "type": "string",
            "nullable": true
          },
          "sesso": {
            "type": "string",
            "nullable": true
          },
          "stato_Estero_Nascita": {
            "type": "string",
            "nullable": true
          },
          "titolo_Studio": {
            "type": "string",
            "nullable": true
          },
          "ufficio": {
            "type": "string",
            "nullable": true
          },
          "ufficio_Descr": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "ContingenteObject": {
        "type": "object",
        "properties": {
          "iD_Matricola": {
            "type": "integer",
            "format": "int32"
          },
          "autorita": {
            "type": "string",
            "nullable": true
          },
          "comando": {
            "type": "string",
            "nullable": true
          },
          "contingente": {
            "type": "string",
            "nullable": true
          },
          "data_Provvedimento": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "dataDecorrenza": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "ente": {
            "type": "string",
            "nullable": true
          },
          "iD_Variazione": {
            "type": "integer",
            "format": "int32"
          },
          "note": {
            "type": "string",
            "nullable": true
          },
          "num_Provvedimento": {
            "type": "string",
            "nullable": true
          },
          "organodiControllo": {
            "type": "string",
            "nullable": true
          },
          "registrazioneDataRegistro": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "registrazioneNumFoglio": {
            "type": "string",
            "nullable": true
          },
          "registrazioneNumRegistro": {
            "type": "string",
            "nullable": true
          },
          "tipoProvvedimento": {
            "type": "string",
            "nullable": true
          },
          "voC_Autorita": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_Comando": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_Contingente": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_Ente": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_OrganoDiControllo": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voC_TipoProvvedimento": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        },
        "additionalProperties": false
      }
    }
  }
}
  1. Ocelot ReRoutes configuration.
{
  "ReRoutes": [
    //documento api config
    {
      "DownstreamPathTemplate": "/api/documento/{everything}",
      "DownstreamScheme": "https",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 44360
        }
      ],
      "UpstreamPathTemplate": "/api/documento/{everything}",
      "UpstreamHttpMethod": [
        "GET",
        "POST",
        "PUT",
        "DELETE"
      ],
      "SwaggerKey": "documents"
      //"AuthenticationOptions": {
      //  "AuthenticationProviderKey": "TestKey",
      //  "AllowedScopes": []
      //},
      //"RateLimitOptions": {
      //  "ClientWhitelist": [],
      //  "EnableRateLimiting": true,
      //  "Period": "1s",
      //  "PeriodTimespan": 1,
      //  "Limit": 1
      //},
      //"FileCacheOptions": {
      //  "TtlSeconds": 15,
      //  "Region": "productcaching"
      //}
    },
    {
      "DownstreamPathTemplate": "/api/categoriaevento/{everything}",
      "DownstreamScheme": "https",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 44360
        }
      ],
      "UpstreamPathTemplate": "/api/categoriaevento/{everything}",
      "UpstreamHttpMethod": [
        "GET",
        "POST",
        "PUT",
        "DELETE"
      ],
      "SwaggerKey": "documents"
    },
    {
      "DownstreamPathTemplate": "/api/matricola/{everything}",
      "DownstreamScheme": "https",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 44360
        }
      ],
      "UpstreamPathTemplate": "/api/matricola/{everything}",
      "UpstreamHttpMethod": [
        "GET",
        "POST",
        "PUT",
        "DELETE"
      ],
      "SwaggerKey": "documents"
    },
    {
      "DownstreamPathTemplate": "/api/profilo/{everything}",
      "DownstreamScheme": "https",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 44360
        }
      ],
      "UpstreamPathTemplate": "/api/profilo/{everything}",
      "UpstreamHttpMethod": [
        "GET",
        "POST",
        "PUT",
        "DELETE"
      ],
      "SwaggerKey": "documents"
    },
    {
      "DownstreamPathTemplate": "/api/variazione/{everything}",
      "DownstreamScheme": "https",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 44360
        }
      ],
      "UpstreamPathTemplate": "/api/variazione/{everything}",
      "UpstreamHttpMethod": [
        "GET",
        "POST",
        "PUT",
        "DELETE"
      ],
      "SwaggerKey": "documents"
    },
    {
      "DownstreamPathTemplate": "/api/documentocommand/{everything}",
      "DownstreamScheme": "https",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 44396
        }
      ],
      "UpstreamPathTemplate": "/api/documentocommand/{everything}",
      "UpstreamHttpMethod": [
        "GET",
        "POST",
        "PUT",
        "DELETE"
      ],
      "SwaggerKey": "documentscommand"
    },
    {
      "DownstreamPathTemplate": "/api/logcommand/{everything}",
      "DownstreamScheme": "https",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 44396
        }
      ],
      "UpstreamPathTemplate": "/api/logcommand/{everything}",
      "UpstreamHttpMethod": [
        "GET",
        "POST",
        "PUT",
        "DELETE"
      ],
      "SwaggerKey": "documentscommand"
    },
    {
      "DownstreamPathTemplate": "/api/variazionecommand/{everything}",
      "DownstreamScheme": "https",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 44396
        }
      ],
      "UpstreamPathTemplate": "/api/variazionecommand/{everything}",
      "UpstreamHttpMethod": [
        "GET",
        "POST",
        "PUT",
        "DELETE"
      ],
      "SwaggerKey": "documentscommand"
    }


    //payment api config
    //{
    //  "DownstreamPathTemplate": "/api/payment/{everything}",
    //  "DownstreamScheme": "https",
    //  "DownstreamHostAndPorts": [
    //    {
    //      "Host": "localhost",
    //      "Port": 44302
    //    }
    //  ],
    //  "UpstreamPathTemplate": "/api/payment/{everything}",
    //  "UpstreamHttpMethod": [
    //    "GET",
    //    "POST",
    //    "PUT",
    //    "DELETE"
    //  ],
    //  "AuthenticationOptions": {
    //    "AuthenticationProviderKey": "TestKey",
    //    "AllowedScopes": []
    //  },
    //  "RateLimitOptions": {
    //    "ClientWhitelist": [],
    //    "EnableRateLimiting": true,
    //    "Period": "1s",
    //    "PeriodTimespan": 1,
    //    "Limit": 1
    //  },
    //  "FileCacheOptions": {
    //    "TtlSeconds": 15,
    //    "Region": "paymentcaching"
    //  }
    //}
  ],
  "SwaggerEndPoints": [
    {
      "Key": "documents",
      "Config": [
        {
          "Name": "Dum.QueryService API",
          "Version": "v1",
          "Url": "http://localhost:44360/swagger/v1/swagger.json"
        }
      ]
    },
    {
      "Key": "documentscommand",
      "Config": [
        {
          "Name": "Document.CommandService API",
          "Version": "v1",
          "Url": "http://localhost:44396/swagger/v1/swagger.json"
        }

      ]
    }
  ],
  "GlobalConfiguration": {
    "BaseUrl": "http://localhost:44387"
  }
}

hi i'm new to Ocelot and .net core

hi i am new to Ocelot and .net core , when i browse for some example i found your github project , can some one help me on how to run this project and explore more, whether each project has to be hosted prior accessing it or can i run in visual studio as multiple project run at time ? which one is the start up project here to begin and Test

How to log request json body in case of error in ocelot

I am serilog to log error from ocelot in case of downstream giving any error.
Using serilog i am sending the exception details to Application Insight.

Serilog configuration:-

"Serilog": {
"Using": [ "Serilog.Sinks.ApplicationInsights" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning"
}
},
"WriteTo": [
{
"Name": "ApplicationInsights",
"Args": {
"instrumentationKey": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"telemetryConverter": "Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters.TraceTelemetryConverter, Serilog.Sinks.ApplicationInsights"
}
}
],
"Enrich": [ "FromLogContext" ],
"Properties": {
"Application": "API GATEWAY"
}
}

Now i want to log request json body in case of error only , How will i do it ?

Middleware didn't work as it didn't catch any ocelot exception, so its out of question.

Configuration for bearer token.

Hi @Burgyn. I would like to know is there any specific config for adding bearer token to swagger for ocelot DownstreamSwaggerHeaders? I mean how to provide "Auth-Key", "AuthValue"?

consolidate API help on ocelot gateway doesn't show help for nested model

Individual API is showing swagger help for nested model, however nested model is not shown in ocelot API gateway swagger help. our model definitions is like below

public class SavingActuals
{
public double Comfort { get; set; }
public double Energy { get; set; }
public double RealEstate { get; set; }
}

public class DetailedChartSavingData
{
    public string Group { get; set; }
    public double Projection { get; set; }
    public double Target { get; set; }
    public SavingActuals Actual { get; set; }
}

SavingActuals is not rendered on swagger page present on ocelot gateway. however it is shown when I am looking individual service swagger.

All paths and definitions are still removed from swagger.json (without double parameters)

Describe the bug
As with the previous similar issue, I get the full swagger.json file accessing it directly from the sub service, but through the gateway all the paths and definitions are empty. I do not have any double parameters in the URL as in the previous issue. ("http://svc-api:{port}" is local address on same server with Ocelot)

Expected behavior
Swagger.json should have all properties just like it was directly request via url.

To Reproduce
Ocelot .json

  "ReRoutes": [  
    {  
      "DownstreamPathTemplate": "/api/{url}",  
      "DownstreamScheme": "http",  
      "DownstreamHostAndPorts": [  
        {
          "Host": "svc-api",
          "Port": 47104
        }
      ],
      "UpstreamPathTemplate": "/nextripv2/{url}",
      "UpstreamHttpMethod": [ "Get" ],
      "SwaggerKey": "nextrip"
    }
  ],
  "SwaggerEndPoints": [
    {
      "Key": "nextrip",
      "Config": [
        {
          "Name": "NexTrip API",
          "Version": "v2",
          "Url": "http://svc-api:47104/swagger/NextripApiSpecification/swagger.json"
        }
      ]
    }
  ],
  "GlobalConfiguration":
  {
    "BaseUrl": "http://svc.{testserver}.org"
  }
}

swagger.json from gateway

{
  "swagger": "2.0",
  "info": {
    "version": "2",
    "title": "NexTrip API",
    "description": "API for creating real-time departure information display"
  },
  "paths": {},
  "definitions": {},
  "host": "svc.{testserver}.org"
}

swagger.json directly from service

{
  "swagger": "2.0",
  "info": {
    "version": "2",
    "title": "NexTrip API",
    "description": "API for creating real-time departure information display"
  },
  "paths": {
    "/api/routes": {
      "get": {
        "tags": [ "NexTrip" ],
        "summary": "Get a list of active Routes for the current service day",
        "operationId": "GetRoutes",
        "consumes": [],
        "produces": [ "application/json" ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Success",
            "schema": {
              "uniqueItems": false,
              "type": "array",
              "items": { "$ref": "#/definitions/Route" }
            }
          },
          "406": {
            "description": "Not Acceptable",
            "schema": { "$ref": "#/definitions/ProblemDetails" }
          },
          "500": { "description": "Server Error" }
        }
      }
    },
    "/api/providers": {
      "get": {
        "tags": [ "NexTrip" ],
        "summary": "Get the full list of regional transit providers",
        "operationId": "GetProviders",
        "consumes": [],
        "produces": [ "application/json" ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Success",
            "schema": {
              "uniqueItems": false,
              "type": "array",
              "items": { "$ref": "#/definitions/Provider" }
            }
          },
          "406": {
            "description": "Not Acceptable",
            "schema": { "$ref": "#/definitions/ProblemDetails" }
          },
          "500": { "description": "Server Error" }
        }
      }
    },
    "/api/directions/{routeId}": {
      "get": {
        "tags": [ "NexTrip" ],
        "summary": "Get two Directions for the given Route, NB/SB or EB/WB",
        "operationId": "GetDirections",
        "consumes": [],
        "produces": [ "application/json" ],
        "parameters": [
          {
            "name": "routeId",
            "in": "path",
            "description": "",
            "required": true,
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "schema": {
              "uniqueItems": false,
              "type": "array",
              "items": { "$ref": "#/definitions/Direction" }
            }
          },
          "406": {
            "description": "Not Acceptable",
            "schema": { "$ref": "#/definitions/ProblemDetails" }
          },
          "500": { "description": "Server Error" },
          "404": {
            "description": "Not Found",
            "schema": { "$ref": "#/definitions/ProblemDetails" }
          },
          "400": {
            "description": "Bad Request",
            "schema": { "$ref": "#/definitions/ProblemDetails" }
          }
        }
      }
    },
    "/api/stops/{routeId}/{directionId}": {
      "get": {
        "tags": [ "NexTrip" ],
        "summary": "Get the Timepoint Stops for the requested Route/Direction",
        "description": "Returns a list of PlaceCodes with descriptions",
        "operationId": "GetPlaces",
        "consumes": [],
        "produces": [ "application/json" ],
        "parameters": [
          {
            "name": "routeId",
            "in": "path",
            "description": "Id for the requested route",
            "required": true,
            "type": "string"
          },
          {
            "name": "directionId",
            "in": "path",
            "description": "Id for the requested direction",
            "required": true,
            "type": "integer",
            "format": "int32"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "schema": {
              "uniqueItems": false,
              "type": "array",
              "items": { "$ref": "#/definitions/Place" }
            }
          },
          "406": {
            "description": "Not Acceptable",
            "schema": { "$ref": "#/definitions/ProblemDetails" }
          },
          "500": { "description": "Server Error" },
          "404": {
            "description": "Not Found",
            "schema": { "$ref": "#/definitions/ProblemDetails" }
          },
          "400": {
            "description": "Bad Request",
            "schema": { "$ref": "#/definitions/ProblemDetails" }
          }
        }
      }
    },
    "/api/{stopId}": {
      "get": {
        "tags": [ "NexTrip" ],
        "summary": "Get a result with stop information and real-time departures",
        "operationId": "GetDeparturesById",
        "consumes": [],
        "produces": [ "application/json" ],
        "parameters": [
          {
            "name": "stopId",
            "in": "path",
            "description": "Bus stop id",
            "required": true,
            "type": "integer",
            "format": "int32"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "schema": { "$ref": "#/definitions/NexTripResult" }
          },
          "406": {
            "description": "Not Acceptable",
            "schema": { "$ref": "#/definitions/ProblemDetails" }
          },
          "500": { "description": "Server Error" },
          "404": {
            "description": "Not Found",
            "schema": { "$ref": "#/definitions/ProblemDetails" }
          },
          "400": {
            "description": "Bad Request",
            "schema": { "$ref": "#/definitions/ProblemDetails" }
          }
        }
      }
    },
    "/api/{routeId}/{directionId}/{placeCode}": {
      "get": {
        "tags": [ "NexTrip" ],
        "summary": "Get a result with stop information and real-time departures",
        "operationId": "GetDepartures",
        "consumes": [],
        "produces": [ "application/json" ],
        "parameters": [
          {
            "name": "routeId",
            "in": "path",
            "description": "Id for the Route",
            "required": true,
            "type": "string"
          },
          {
            "name": "directionId",
            "in": "path",
            "description": "Id for the Direction",
            "required": true,
            "type": "integer",
            "format": "int32"
          },
          {
            "name": "placeCode",
            "in": "path",
            "description": "PlaceCode for the Timepoint stop place name",
            "required": true,
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "schema": { "$ref": "#/definitions/NexTripResult" }
          },
          "406": {
            "description": "Not Acceptable",
            "schema": { "$ref": "#/definitions/ProblemDetails" }
          },
          "500": { "description": "Server Error" },
          "404": {
            "description": "Not Found",
            "schema": { "$ref": "#/definitions/ProblemDetails" }
          },
          "400": {
            "description": "Bad Request",
            "schema": { "$ref": "#/definitions/ProblemDetails" }
          }
        }
      }
    },
    "/api/vehicles/{routeId}": {
      "get": {
        "tags": [ "NexTrip" ],
        "summary": "Get an array of Vehicles in service for a Route",
        "description": "Enter 0 for all Vehicles",
        "operationId": "GetVehicles",
        "consumes": [],
        "produces": [ "application/json" ],
        "parameters": [
          {
            "name": "routeId",
            "in": "path",
            "description": "",
            "required": true,
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "schema": {
              "uniqueItems": false,
              "type": "array",
              "items": { "$ref": "#/definitions/Vehicle" }
            }
          },
          "406": {
            "description": "Not Acceptable",
            "schema": { "$ref": "#/definitions/ProblemDetails" }
          },
          "500": { "description": "Server Error" },
          "404": {
            "description": "Not Found",
            "schema": { "$ref": "#/definitions/ProblemDetails" }
          },
          "400": {
            "description": "Bad Request",
            "schema": { "$ref": "#/definitions/ProblemDetails" }
          }
        }
      }
    }
  },
  "definitions": {
    "Route": {
      "type": "object",
      "properties": {
        "RouteId": {
          "type": "string",
          "readOnly": true
        },
        "ProviderId": {
          "format": "int32",
          "type": "integer",
          "readOnly": true
        },
        "Description": {
          "type": "string",
          "readOnly": true
        },
        "RouteAbbr": {
          "type": "string",
          "readOnly": true
        }
      }
    },
    "ProblemDetails": {
      "type": "object",
      "properties": {
        "type": { "type": "string" },
        "title": { "type": "string" },
        "status": {
          "format": "int32",
          "type": "integer"
        },
        "detail": { "type": "string" },
        "instance": { "type": "string" }
      },
      "additionalProperties": { "type": "object" }
    },
    "Provider": {
      "type": "object",
      "properties": {
        "ProviderId": {
          "format": "int32",
          "type": "integer",
          "readOnly": true
        },
        "ProviderName": {
          "type": "string",
          "readOnly": true
        }
      }
    },
    "Direction": {
      "type": "object",
      "properties": {
        "DirectionId": {
          "format": "int32",
          "type": "integer",
          "readOnly": true
        },
        "DirectionName": {
          "type": "string",
          "readOnly": true
        }
      }
    },
    "Place": {
      "type": "object",
      "properties": {
        "PlaceCode": {
          "type": "string",
          "readOnly": true
        },
        "Description": {
          "type": "string",
          "readOnly": true
        }
      }
    },
    "NexTripResult": {
      "type": "object",
      "properties": {
        "Stop": {
          "$ref": "#/definitions/Stop",
          "readOnly": true
        },
        "Departures": {
          "uniqueItems": false,
          "type": "array",
          "items": { "$ref": "#/definitions/Departure" },
          "readOnly": true
        }
      }
    },
    "Stop": {
      "type": "object",
      "properties": {
        "StopId": {
          "format": "int32",
          "type": "integer",
          "readOnly": true
        },
        "Latitude": {
          "format": "double",
          "type": "number",
          "readOnly": true
        },
        "Longitude": {
          "format": "double",
          "type": "number",
          "readOnly": true
        },
        "Description": {
          "type": "string",
          "readOnly": true
        }
      }
    },
    "Departure": {
      "type": "object",
      "properties": {
        "Actual": {
          "type": "boolean",
          "readOnly": true
        },
        "BlockNumber": {
          "format": "int32",
          "type": "integer",
          "readOnly": true
        },
        "DepartureText": {
          "type": "string",
          "readOnly": true
        },
        "DepartureTime": {
          "format": "date-time",
          "type": "string",
          "readOnly": true
        },
        "Description": {
          "type": "string",
          "readOnly": true
        },
        "Gate": {
          "type": "string",
          "readOnly": true
        },
        "RouteId": {
          "type": "string",
          "readOnly": true
        },
        "DirectionId": {
          "format": "int32",
          "type": "integer",
          "readOnly": true
        },
        "DirectionText": {
          "type": "string",
          "readOnly": true
        },
        "Terminal": {
          "type": "string",
          "readOnly": true
        },
        "Latitude": {
          "format": "double",
          "type": "number",
          "readOnly": true
        },
        "Longitude": {
          "format": "double",
          "type": "number",
          "readOnly": true
        }
      }
    },
    "Vehicle": {
      "type": "object",
      "properties": {
        "BlockNumber": {
          "format": "int32",
          "type": "integer",
          "readOnly": true
        },
        "DirectionId": {
          "format": "int32",
          "type": "integer",
          "readOnly": true
        },
        "LocationTime": {
          "format": "date-time",
          "type": "string",
          "readOnly": true
        },
        "RouteId": {
          "type": "string",
          "readOnly": true
        },
        "Terminal": {
          "type": "string",
          "readOnly": true
        },
        "Latitude": {
          "format": "double",
          "type": "number",
          "readOnly": true
        },
        "Longitude": {
          "format": "double",
          "type": "number",
          "readOnly": true
        },
        "Bearing": {
          "format": "int32",
          "type": "integer",
          "readOnly": true
        },
        "Odometer": {
          "format": "int32",
          "type": "integer",
          "readOnly": true
        },
        "Speed": {
          "format": "int32",
          "type": "integer",
          "readOnly": true
        }
      }
    }
  }
}

Schema Invalid : http port not valid

everything has been rendered in view, when trying to execute API through Swagger, getting invalid schema. find the configuration and screen shot below, please help me out, if there I missed anything from my end.

ocelot.json:
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/api/{everything}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 5001
}
],
"UpstreamPathTemplate": "/auth-service/{everything}",
"UpstreamHttpMethod": [ "Get", "Post" ],
"SwaggerKey": "authswagger",
"VirtualDirectory": "/authswagger"
}
],
"SwaggerEndPoints": [
{
"Key": "gateway",
"TransformByOcelotConfig": false,
"Config": [
{
"Name": "Gateway",
"Version": "v1",
"Url": "https://localhost:5003/swagger/v1/swagger.json"
}
]
},
{
"Key": "authswagger",
"Config": [
{
"Name": "Authentication API",
"Version": "v1",
"Url": "https://localhost:5001/api/swagger/v1/swagger.json"
}
]
}
],
"GlobalConfiguration": {
"BaseUrl": "https://localhost"
}
}
Screenshot 2020-05-03 at 23 12 28

BasePath Does not Appear to Work correctly

Describe the bug
I am using a downstream swagger service that has a basePath in it. When I set it up through ocelot, i have to add the basePath into the Upstream/Downstream Path Templates in order to get the routing working. When I do that though, it messes up the swagger from SwaggerForOcelot as the route cannot be detected.

Example Original Swagger:

{
    "swagger": "2.0",
    ...
    "host": "localhost:8001",
    "basePath": "/api",
    "paths": {
        "/address/check": {
            "post": {
                ...
            }
        }
    }
}

To Call this it would be localhost:8001/api/address/check.

After adding ocelot:

Ocelot.json

{
 "ReRoutes": [
    {
      "DownstreamPathTemplate": "/api/{everything}",
      "UpstreamPathTemplate": "/api/OM/{everything}",
      ...
    }
 ]
}

Now I can hit the API at localhost:8999/api/OM/address/check

But the problem is that the swagger generated from SwaggerForOcelot does not find those routes and is not generated.

If I remove the basePath. and change the Ocelot.json to:

{
 "ReRoutes": [
    {
      "DownstreamPathTemplate": "/{everything}",
      "UpstreamPathTemplate": "/OM/{everything}",
      ...
    }
 ]
}

then the swagger is correctly generated from SwaggerForOcelot.
But the Ocelot reRouting fails, it thinks that I need to hit this endpoint:
localhost:8999/OM/api/address/check

It appears that the base path needs to be considered when working in the SwaggerJsonTransformer when finding the reRoutes.

The tests also seem to hide this issue. For instance: CreateNewJsonWhenConfigurationContainsOnlyOneController has a base path of v2. It appears that if ocelot actually used that reRoute though, that it would not work properly as the v2 would not be matched by ocelot.

Update configuration via Ocelot.Administration

Is your feature request related to a problem? Please describe.

We are moving to dynamic configuration of Ocelot via a fairly hacky method that (edit: used to) involves POSTing the new configuration to the Ocelot administration API. However, SwaggerForOcelot:

  1. Throws because there are no Swagger documents in the startup config, and
  2. (I presume) does not react to Ocelot configuration updates.

Describe the solution you'd like

It would be good if SwaggerForOcelot:

  1. Did not crash the program if there were no configured Swaggers, and
  2. Updated itself when the Ocelot configuration changes.

This might require changes to Ocelot to make it issue an event or trigger a change token of some kind. I'll investigate this myself when I get the time, probably this or next week. Please let me know if you have any concerns.

Chris

Swagger documentation become duplicated for each down services.

Hi.
In swagger for ocelot, I observe a strange behavior.
When I use app.UseSwaggerForOcelotUI with opts.SwaggerEndpoint, it duplicates the swagger documentation for each down services. I can't figure it out, why this is happening? Am i doing something wrong?

Here is one more thing I would like to ask. I am able to integrate down services swagger documentation directly in my ocelot gateway just by using app.UseSwaggerUI with SwaggerEndpoint. Here I don't need any configuration and no need of app.UseSwaggerForOcelotUI.
Then why we are using this separate NuGet package?
1
2

BuilderExtensions - System.NullReferenceException

General

I am experiencing System.NullReferenceException while registering Ocelot Swagger middleware with extension method UseSwaggerForOcelotUI().

Steps to reproduce

  1. Missing or empty section SwaggerEndPoints in configuration file (e.g. Appsettings.json):
"SwaggerEndPoints": [],
  1. Register Ocelot Swagger
app.UseSwaggerForOcelotUI(Configuration);

Expectation

I would expect better exception message, or support for empty section.

New UI layout

Creat new UI where services will be in tabs not in combobox.

Swagger not working with self signed certificate.

Describe the bug
I have a service with swagger with self signed certificate. There is no possibility to configure request to use self signed certificates.

Expected behavior
There should be an option to set self signed certificates as valid.

To Reproduce
The service with selfsigned certificate is required.

VirtualDirectory not reflects on swagger ui

Hi,

in my configuration, I set the virtual directory.
when I check my rest methods on swagger ui, virtual directory is missing and when i try to execute the methods, I get 404 error.

but when i manually add virtual host to request url. it works.
are there any workaround to handle this issue?

Thanks,

Support complex ocelot scenario

  • Add tests for Petstore swagger Example.
  • Group more reroutes with same SwaggerKey.
  • Filter by UpstreamHttpMethod.
  • Splited endpoints

SwaggerEndPoints configuration section is missing or empty exception

I am using SwaggerForOcelot with .net core 3.1 for api gateway.

Our ocelot json is divided into modules like below:-

ocelot.call-log.json
ocelot.column-chooser.json
ocelot.contact-list.json
ocelot.documentation.json
ocelot.global.json
ocelot.inventory-list.json
ocelot.json <= this is generated automatically.
ocelot.swagger.json <=define here swagger related configuration , pls find below

{
"ReRoutes": [
{
"DownstreamPathTemplate": "/api/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "xx.xx.xx.xx",
"Port": 6004
}
],
"UpstreamPathTemplate": "{everything}",
"UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE" ],
"SwaggerKey": "all"
}
],
"SwaggerEndPoints": [
{
"Key": "all",
"Config": [
{
"Name": "Test API V1",
"Version": "v1",
"Url": "http://xx.xx.xx.xx:6004/swagger/v1/swagger.json"
}
]
}
]
}

On running it giving exception on below line:-

app.UseSwaggerForOcelotUI(Configuration, opt =>
{
opt.PathToSwaggerGenerator = "/swagger/docs";
});

Exception details:-
at Microsoft.AspNetCore.Builder.BuilderExtensions.GetConfiguration(IConfiguration configuration)
at Microsoft.AspNetCore.Builder.BuilderExtensions.<>c__DisplayClass1_0.b__0(SwaggerUIOptions c)
at Microsoft.AspNetCore.Builder.SwaggerUIBuilderExtensions.UseSwaggerUI(IApplicationBuilder app, Action1 setupAction) at Microsoft.AspNetCore.Builder.BuilderExtensions.UseSwaggerForOcelotUI(IApplicationBuilder app, IConfiguration configuration, Action1 setupAction)
at SMARTS2.ApiGateway.Startup.Configure(IApplicationBuilder app, IWebHostEnvironment env) in C:\Users\rajesh.agrawal\source\repos\SMARTS 2.0 API\SMARTS2.0\SMARTS2.ApiGateway\Startup.cs:line 63
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
at Microsoft.AspNetCore.Hosting.ConfigureBuilder.<>c__DisplayClass4_0.b__0(IApplicationBuilder builder)
at Microsoft.AspNetCore.Hosting.ConventionBasedStartup.Configure(IApplicationBuilder app)

Let me know why this error coming ?

Confusion in the interpretation of the routes

This is my configuration file:
{ "ReRoutes": [ // AUTHENTICATION { "DownstreamPathTemplate": "/api/authentication", "DownstreamScheme": "https", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 4001 } ], "UpstreamPathTemplate": "/authentication", "UpstreamHttpMethod": [ "Post" // Login ], // Login Service is not under Auth in order to be accessible "SwaggerKey": "Authentication" }, { "DownstreamPathTemplate": "/api/authentication", "DownstreamScheme": "https", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 4001 } ], "UpstreamPathTemplate": "/authentication", "UpstreamHttpMethod": [ "Get" // IsValidRequest ], "AuthenticationOptions": { "AuthenticationProviderKey": "AxylogKey", "AllowedScopes": [] }, "SwaggerKey": "Authentication" }, // AUTHENTICATION // ADMIN.ADMINUSER { "DownstreamPathTemplate": "/api/adminuser/{id}", "DownstreamScheme": "https", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 5001 } ], "UpstreamPathTemplate": "/adminuser/{id}", "UpstreamHttpMethod": [ "Get" ], "SwaggerKey": "Admin" }, // ADMIN.ADMINUSER // LOCALS.COMPANIES { "DownstreamPathTemplate": "/api/companies", "DownstreamScheme": "https", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 6001 } ], "UpstreamPathTemplate": "/companies", "UpstreamHttpMethod": [ "Get" ], "AuthenticationOptions": { "AuthenticationProviderKey": "AxylogKey", "AllowedScopes": [] }, "SwaggerKey": "Locals" }, // LOCALS.COMPANIES // LOCALS.MASTERDATA { "DownstreamPathTemplate": "/api/masterdata", "DownstreamScheme": "https", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 6001 } ], "UpstreamPathTemplate": "/masterdata", "UpstreamHttpMethod": [ "Get", "Post", "Put" ], "AuthenticationOptions": { "AuthenticationProviderKey": "AxylogKey", "AllowedScopes": [] }, "SwaggerKey": "Locals" }, { "DownstreamPathTemplate": "/api/masterdata/{everything}", "DownstreamScheme": "https", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 6001 } ], "UpstreamPathTemplate": "/masterdata/{everything}", "UpstreamHttpMethod": [ "Get", "Delete" ], "AuthenticationOptions": { "AuthenticationProviderKey": "AxylogKey", "AllowedScopes": [] }, "SwaggerKey": "Locals" }, // LOCALS.MASTERDATA // LOCALS.MASTERDATATYPE { "DownstreamPathTemplate": "/api/masterdatatype", "DownstreamScheme": "https", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 6001 } ], "UpstreamPathTemplate": "/masterdatatype", "UpstreamHttpMethod": [ "Get", "Post", "Put" ], "AuthenticationOptions": { "AuthenticationProviderKey": "AxylogKey", "AllowedScopes": [] }, "SwaggerKey": "Locals" }, { "DownstreamPathTemplate": "/api/masterdatatype/{everything}", "DownstreamScheme": "https", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 6001 } ], "UpstreamPathTemplate": "/masterdatatype/{everything}", "UpstreamHttpMethod": [ "Get", "Delete" ], "AuthenticationOptions": { "AuthenticationProviderKey": "AxylogKey", "AllowedScopes": [] }, "SwaggerKey": "Locals" }, // LOCALS.MASTERDATATYPE // MIT SERVICE { "DownstreamPathTemplate": "/api/Vehicle", "DownstreamScheme": "https", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 3001 } ], "UpstreamPathTemplate": "/vehicle", "UpstreamHttpMethod": [ "Post" ], "AuthenticationOptions": { "AuthenticationProviderKey": "AxylogKey", "AllowedScopes": [] }, "SwaggerKey": "MITService" } // MIT SERVICE ], "SwaggerEndPoints": [ { "Key": "Authentication", "Config": [ { "Name": "Authentication API", "Version": "v1", "Url": "https://localhost:4001/swagger/v1/swagger.json" } ] }, { "Key": "Admin", "Config": [ { "Name": "Admin API", "Version": "v1", "Url": "https://localhost:5001/swagger/v1/swagger.json" } ] }, { "Key": "Locals", "Config": [ { "Name": "Locals API", "Version": "v1", "Url": "https://localhost:6001/swagger/v1/swagger.json" } ] }, { "Key": "MITService", "Config": [ { "Name": "MIT Service API", "Version": "v1", "Url": "https://localhost:3001/swagger/v1/swagger.json" } ] } ], "GlobalConfiguration": { "RequestIdKey": "OcRequestId", "BaseUrl": "https://localhost:7000" } }

The route "/authentication" have to be separeted since the POST does not require authentication while the GET does.
When it generates the documentation it only reads the first one and for the authenticationController find only the POST service, the GET is ignored.
image

Also for the "/masterdata" and "/masterdatatype" routes there are some problems. It seems like that when it looks for the masterdataController it also finds the masterdatatypeController and his services, then the routes for the "masterdatatype" are ignored like for the authenticationController.
image
Then if I exchange the order of the "/masterdata" route with the "/masterdatatype" route it works in the right way and all the route are shown correctly.
image

I hope I've been clear enough, if needed I'll try to provide more explanation.

Headers from securityDefinitions are not kept

Describe the bug
Headers from securityDefinitions are not kept in DownStream when trying with swagger UI

Expected behavior
Headers from securityDefinitions should be kept in DownStream

To Reproduce
Having following definitions in swagger.json :
"securityDefinitions": { "apiKey": { "type": "apiKey", "description": "API Key Authentication", "name": "myheader", "in": "header" } }
And following acelot.json
{ "DownstreamPathTemplate": "/api/account/connexion", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "serviceclientapi.powimo.fr", "Port": 80 } ], "UpstreamPathTemplate": "/api/account/connexion", "UpstreamHttpMethod": [ "Post" ], "SwaggerKey": "orders" } ... { "Key": "orders", "Config": [ { "Name": "Orders API", "Version": "1", "Url": "http://serviceclientapi.powimo.fr/swagger/docs/v1" } ] } ...

When trying on Swagger UI, "myheader" is not is not send to the backend

Thanks

All paths and definitions are removed from swagger.json

Describe the bug
Library removes all paths and definitions from microservice swagger.json. SwaggerUI shows No operations defined in spec! error.
This happens because of double {} variables in PathTemplate. If {version} is removed everything works as expected.

Expected behavior
Swagger.json should have all properties just like it was directly request via url.

To Reproduce

{
    "ReRoutes": [
        {
            "DownstreamPathTemplate": "/{version}/identity/{everything}",
            "DownstreamScheme": "http",
            "DownstreamHostAndPorts": [
                {
                    "Host": "identity",
                    "Port": 80
                }
            ],
            "UpstreamPathTemplate": "/api/{version}/identity/{everything}",
            "UpstreamHttpMethod": [ "Post" ],
            "SwaggerKey": "identity"
        }
    ],
    "SwaggerEndPoints": [
        {
            "Key": "identity",
            "Config": [
                {
                    "Name": "Identity API",
                    "Version": "v1",
                    "Url": "http://identity:80/api-docs/v1/swagger.json"
                }
            ]
        }
    ],
    "GlobalConfiguration": {
        "BaseUrl": "api.test.local",
        "RequestIdKey": "OcRequestId"
    }
}

Swagger.json

{
  "swagger": "2.0",
  "info": {
    "version": "v1",
    "title": "Api"
  },
  "paths": {
    "/api/v1/path/action": {
      "post": {
        "tags": [
          "Identity"
        ],
        "operationId": "Opeartion",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "ViewModel",
            "in": "body",
            "required": false,
            "schema": {
              "$ref": "#/definitions/ViewModel"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "schema": {
              "$ref": "#/definitions/ViewModel"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/ViewModel"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "$ref": "#/definitions/ViewModel"
            }
          }
        }
      }
    },
    "/api/v1/path/action": {
      "post": {
        "tags": [
          "Identity"
        ],
        "operationId": "Operation",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "RequestParameters",
            "in": "body",
            "required": false,
            "schema": {
              "$ref": "#/definitions/RequestParameters"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "schema": {
              "$ref": "#/definitions/ViewModel"
            }
          },
          "400": {
            "description": "Bad Request",
            "schema": {
              "$ref": "#/definitions/ViewModel"
            }
          },
          "409": {
            "description": "Conflict",
            "schema": {
              "$ref": "#/definitions/ViewModel"
            }
          }
        }
      }
    }
  },
  "definitions": {
    "ViewModel": {
      "required": [
        "something",
        "something"
      ],
      "type": "object",
      "properties": {
        "something": {
          "type": "string"
        },
        "something": {
          "type": "string"
        },
        "something": {
          "type": "string"
        },
        "something": {
          "type": "string"
        },
        "something": {
          "type": "string"
        }
      }
    },
    "ViewModel": {
      "type": "object",
      "properties": {
        ...
      }
    },
    "ModelStateViewModel": {
      "type": "object",
      "properties": {
        "errors": {
          "type": "object",
          "additionalProperties": {
            "uniqueItems": false,
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      }
    },
    "ErrorViewModel": {
      "type": "object",
      "properties": {
        "message": {
          "type": "string"
        },
        ....
      }
    },
    "ViewModel": {
      "required": [
        "test"
      ],
      "type": "object",
      "properties": {
        "test": {
          "type": "string"
        }
      }
    }
  }
}

Customizable swagger base URL.

HI @Burgyn.

I have a question. Does Swagger for Ocelot support changing default swagger path? I need to use RoutePrefix from UseSwaggerUI method:
`
app.UseSwaggerUI(c =>
{

c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");

c.RoutePrefix = "docs";

`});``

Could not resolve reference

Describe the bug
Hi,
Through Ocelot some references could not be resolved.
In the following sample TypeColors or TypeImages are not retrieved in (POST)/api/admin/settings

They does existe in original JSON but not in ocelot json

ocelot.txt
swagger.txt

Thanks

Support for openapi v3

This package actualy doesn't support new version of openapi (version 3.0)

One of the breaking change is that definitions section is moved under components.

It would be great if this package would support a new version of openapi

Unable to integrate swagger with Gateway

Describe the bug
I have ocelot with consul service discovery, whenever swagger is fetching from api gateway it returns empty path and contents and no services found but
without gateway swagger is displaying the services
Expected behavior
with localhost url its working but with service discovery its not working

To Reproduce
If it is a possible attache:

  1. Original downstream swagger.json.
  2. Ocelot ReRoutes configuration.

After updating swashbuckle to version 5.0.0, some models have trouble serializing

Description
Using .net core 3.0 project, after updating swashbuckle library to version 5.0.0 some models have trouble serializing and swagger renders an error message upon clicking some routes.
In my case the error had problems with Enum and some classes.

Expected behavior
Models should be serialized.

To Reproduce

  1. Open swagger
  2. Click on route

Swagger configure dynamic url for microservices

Hello, today I have a structure containing 3 microservices and I am using this library to expose the endpoints of these services via swagger, however, using docker to allow scalability, which makes the need for the ocelot.json configuration file not to fix the ports and url services, is there any functionality or configuration that meets my need?

SwaggerEndPoints configuration section is missing or empty for merged ocelot jsons

Hi,

When I'm using per service ocelot.json configuration and file merging, I get System.InvalidOperationException: "SwaggerEndPoints configuration section is missing or empty."
Approach is described here: https://ocelot.readthedocs.io/en/latest/features/configuration.html#merging-configuration-files

When I put whole configuration in standalone ocelot.json, everything works fine.

I would like to use both configurations merging and swagger on api gateway.

My configuration below:

Startup.cs

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddOcelot();
            services.AddSwaggerForOcelot(Configuration);
            ...
        }
        public async void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
          ...
            app.UseStaticFiles();

            await app.UseSwaggerForOcelotUI(Configuration, opt =>
            {
                opt.PathToSwaggerGenerator = "/swagger/docs";

            }).UseOcelot();
           ...
        }

Program.cs

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.ConfigureAppConfiguration((hostingContext, config) =>
                    {
                        config
                            .AddOcelot("./Ocelot", hostingContext.HostingEnvironment)
                            .AddEnvironmentVariables();
                    });
                    webBuilder.UseStartup<Startup>();
                });

ocelot.service1.json

{
  "ReRoutes": [
    {
      "SwaggerKey": "service1",
      "UpstreamPathTemplate": "/service1/{everything}",
      "UpstreamHttpMethod": [ "GET", "PUT" ],
      "DownstreamPathTemplate": "/service1/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5010
        }
      ]
    }
  ]
}

ocelot.global.json

{
  "SwaggerEndPoints": [
    {
      "Key": "service1",
      "Config": [
        {
          "Name": "Service1 API V1",
          "Version": "v1",
          "Url": "http://localhost:5010/swagger/v1/swagger.json"
        }
      ]
    }
  ],
  "GlobalConfiguration": {
    "BaseUrl": "http://localhost:5014"
  }
}

Documentation for Ocelot aggregates

It would be great if there were any way how to create Swagger documentation for Ocelot aggregates.
Please, think it over and if any idea comes up to your mind, realise it.
Thanks a lot :)

File Merge Support

I have the same problem as #69 and built my own fix by making my own version of the file merge code.

@Burgyn How would you like me to proceed to get you the code to review to decide if you want to take my changes?

Found SwaggerJsonTransformer of low performance

Describe the bug
SwaggerJsonTransformer has performance problems.

Expected behavior
The size of swagger.json in api project more than 1MB, and request took more than 15 seconds.

To Reproduce
Request:
001
dotTrace:
002

Resolver error at ... Could not resolve reference: Could not resolve pointer: ...

Hi,
I'm having two different behaviour in the swagger of my end point and in SwaggerForOcelot of my gateway.
I have defined an object that is also a property of another object that I defined.
In the swagger of the end point everything works fine, I can navigate all the properties.
image
While when I try to navigate this objects and properties in the page of the gateway, I get this error:
image
I'm working with the 1.10.1 version.

I'll be happy the give any further information if needed.

Any help is welcome,
Thank you in advance!

Value cannot be null

Describe the bug

I get the following error when i try to load in my swagger json file.

      An unhandled exception has occurred while executing the request.
System.ArgumentNullException: Value cannot be null.
Parameter name: source
   at System.Linq.Enumerable.Cast[TResult](IEnumerable source)
   at MMLib.SwaggerForOcelot.Transformation.SwaggerJsonTransformer.RemoveItems[T](JToken token, JToken paths, Func`2[] searchPaths)
   at MMLib.SwaggerForOcelot.Transformation.SwaggerJsonTransformer.Transform(String swaggerJson, IEnumerable`1 reRoutes, String hostOverride)
   at MMLib.SwaggerForOcelot.Middleware.SwaggerForOcelotMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.MiddlewareAnalysis.AnalysisMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Builder.Extensions.MapMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.MiddlewareAnalysis.AnalysisMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.MiddlewareAnalysis.AnalysisMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.MiddlewareAnalysis.AnalysisMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.MiddlewareAnalysis.AnalysisMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.MiddlewareAnalysis.AnalysisMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.MiddlewareAnalysis.AnalysisMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.ResponseCompression.ResponseCompressionMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.MiddlewareAnalysis.AnalysisMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware:Error: An unhandled exception has occurred while executing the request.

Expected behavior
A viewable swagger ui.

To Reproduce
The swagger.json file

{
  "openapi": "3.0.1",
  "info": {
    "title": "Building API",
    "version": "v1"
  },
  "paths": {
    "/api/buildings/{buildingId}": {
      "get": {
        "tags": [
          "Building"
        ],
        "parameters": [
          {
            "name": "buildingId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Building"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Building"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Building"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Building"
        ],
        "parameters": [
          {
            "name": "buildingId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "projectName",
            "in": "query",
            "schema": {
              "type": "string",
              "default": ""
            }
          },
          {
            "name": "email",
            "in": "query",
            "schema": {
              "type": "string",
              "default": ""
            }
          },
          {
            "name": "phoneNumber",
            "in": "query",
            "schema": {
              "type": "string",
              "default": ""
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Building"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Building"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Building"
                }
              }
            }
          }
        }
      }
    },
    "/api/buildings": {
      "get": {
        "tags": [
          "Building"
        ],
        "parameters": [
          {
            "name": "buildingIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "integer",
                "format": "int64"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Building"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Building"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Building"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Building"
        ],
        "requestBody": {
          "content": {
            "application/json-patch+json": {
              "schema": {
                "$ref": "#/components/schemas/Building"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Building"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/Building"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/Building"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Building"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Building"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Building"
                }
              }
            }
          }
        }
      }
    },
    "/api/buildings/{buildingId}/features": {
      "get": {
        "tags": [
          "Building"
        ],
        "parameters": [
          {
            "name": "buildingId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Feature"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Feature"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Feature"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Building"
        ],
        "parameters": [
          {
            "name": "buildingId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          }
        }
      }
    },
    "/api/buildings/{buildingId}/temperature/offset": {
      "get": {
        "tags": [
          "Building"
        ],
        "parameters": [
          {
            "name": "buildingId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DataPoint"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DataPoint"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DataPoint"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Building"
        ],
        "parameters": [
          {
            "name": "buildingId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "Value",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          }
        }
      }
    },
    "/api/buildings/{buildingId}/temperature/inside": {
      "get": {
        "tags": [
          "Building"
        ],
        "parameters": [
          {
            "name": "buildingId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DataPoint"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DataPoint"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DataPoint"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/buildings/{buildingId}/zones": {
      "post": {
        "tags": [
          "Building"
        ],
        "parameters": [
          {
            "name": "buildingId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "zoneId",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "zoneName",
            "in": "query",
            "schema": {
              "type": "string",
              "default": ""
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Building"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Building"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Building"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Zone": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "name": {
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "Building": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "projectName": {
            "type": "string"
          },
          "companyName": {
            "type": "string"
          },
          "street": {
            "type": "string"
          },
          "number": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "zip": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "phoneNumber": {
            "type": "string"
          },
          "zones": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Zone"
            }
          }
        },
        "additionalProperties": false
      },
      "Feature": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "name": {
            "type": "string"
          },
          "state": {
            "enum": [
              "On",
              "Off",
              "Error"
            ],
            "type": "string"
          },
          "type": {
            "enum": [
              "Zone",
              "Building"
            ],
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "DataPoint": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "unit": {
            "type": "string"
          },
          "value": {
            "type": "number",
            "format": "double"
          },
          "zoneId": {
            "type": "integer",
            "format": "int64"
          },
          "time": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      }
    }
  }
}```
The ocelot file
``` js{
    "ReRoutes": [
        {
            "DownstreamPathTemplate": "/api/buildings/{everything}",
            "DownstreamScheme": "http",
            "DownstreamHostAndPorts": [
                {
                    "Host": "localhost",
                    "Port": 5100
                }
            ],
            "UpstreamPathTemplate": "/api/buildings/{everything}",
            "SwaggerKey": "building"
        }
    ],
    "SwaggerEndPoints": [
        {
            "Key": "building",
            "Config": [
                {
                    "Name": "Building API",
                    "Version": "v1",
                    "Url": "http://localhost:5100/swagger/v1/swagger.json"
                }
            ]
        }
    ],
    "GlobalConfiguration": {
        "BaseUrl": "http://localhost:3000"
    }
}```

Getting issue in .Net core 3.1

Describe the bug
Getting this error with simple micro service structure that uses Ocelot. see attached snap.

An invalid request URI was provided. The request URI must either be an absolute URI or BaseAddress must be set.

image

To Reproduce
Here I have attached my Ocelot configuration file.

image

My start up class.
image

I am trying to resolve this issue from last 2 days, but it's just not working so adding this here to get some help. Thanks.

Base path property incorrect value

Describe the bug
If gateway has no base path and service has base path swagger UI creates invalid urls for queries to gateway (with service base path)

Support for local swagger

In certain situations where a developer is slowly transitioning from a monolithic system they may have a hybrid gateway. So in some cases the gateway may have some local endpoints its serving as well as acting as a gateway for other services with ocelot. Would be nice if there was a way to be able to serve up the local swagger doc as well as the swagger docs of the downstream services.

Incorrect URL for microservice swagger

Describe the bug
Swagger launched from the gateway is looking for the microservice swagger json at the wrong path

Expected behavior
Swagger should populate microservice json data

To Reproduce
Gateway API: localhost:44393
Users Microservice: localhost:44340

I am able to access the Users microservice json file directly at: https://localhost:44340/swagger/v1/swagger.json

But from the gateway page I am getting the error:
Fetch error: Internal Server Error /swagger/docs/v1/users

I'm unable to figure out why that path is being used rather than the one specified in SwaggerEndPoints:Config:Url

In the Gateway API
ocelot.json file:

{
  "ReRoutes": [
    {
      "UpstreamPathTemplate": "/users/{everything}",
      "DownstreamPathTemplate": "/{everything}",
      "SwaggerKey": "users",
      "DownstreamScheme": "https",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 44340
        }
      ]
    }
  ],
  "SwaggerEndPoints": [
    {
      "Key": "users",
      "Config": [
        {
          "Name": "Users API",
          "Version": "v1",
          "Url": "https://localhost:44340/swagger/v1/swagger.json"
        }
      ]
    }
  ],
  "GlobalConfiguration": {
    "BaseUrl": "https://localhost:44393"
  },
  "AuthenticationOptions": {
    "authPersistSingleRequest": "True"
  }
}

gateway startup file:

using System.Collections.Generic;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;

namespace endpointApi.gateway
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddOcelot();
            services.AddSwaggerForOcelot(Configuration);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseStaticFiles();

            app.UseAuthorization();

            app.UseSwaggerForOcelotUI(Configuration, opt =>
            {
                opt.DownstreamSwaggerHeaders = new[]
                {
                        new KeyValuePair<string, string>("Key", "Value"),
                        new KeyValuePair<string, string>("Key2", "Value2"),
                    };
            })
                .UseOcelot()
                .Wait();
        }
    }
}

** Users microservice Startup**

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;

namespace endpointApi.sites.users
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            services.AddMvc();

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "Users API", Version = "v1" });
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseSwagger()
                .UseSwaggerUI(c =>
                {
                    c.SwaggerEndpoint("/swagger/v1/swagger.json", "Users API V1");
                });

            app.UseRouting();

            app.UseAuthorization();


            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

Route wildcard naming

First off; Thank you for developing this fine project! :)

However, we had quite some trouble getting the library to work for us.

Thing was that in Ocelot the route wildcards can be named whatever. In our case they were named {path} and Ocelot-wise that was all working fine. However after some debugging we found that SwaggerForOcelot does not allow this, and will only accept wildcards named {everything}.

My suggestion would be to either implement a fix that allows any wildcard naming, like Ocelot do, or to expressively state in the documentation that only the {everything} wildcard tag is supported.

It might help others 👍

Double http protocol when executing action (v1.9.0)

For the ocelot.json content below. I get a working Swagger page for the microservice. However, when trying to execute an action, a wrong URL is generated as indicated in the screenshot. Particularly, the URL starts with "http://http://".

{
   "ReRoutes":[
      {
         "DownstreamPathTemplate":"/api/{everything}",
         "DownstreamScheme":"http",
         "DownstreamHostAndPorts":[
            {
               "Host":"localhost",
               "Port":5000
            }
         ],
         "UpstreamPathTemplate":"/api/catalog/{everything}",
         "UpstreamHttpMethod":[

         ],
         "SwaggerKey":"ProductCatalog"
      }
   ],
   "SwaggerEndPoints":[
      {
         "Key":"ProductCatalog",
         "Config":[
            {
               "Name":"Product Catalog API",
               "Version":"v2",
               "Url":"http://localhost:5000/swagger/2.0/swagger.json"
            }
         ]
      }
   ],
   "GlobalConfiguration":{
      "BaseUrl":"http://localhost:64294"
   }
}

image

UpstreamHttpMethod ignored

Describe the bug
When UpstreamHttpMethod is used in the ocelot configuration.json the generated swagger.json will still have all endpoints present even when the HttpMethod was not present in the UpstreamHttpMethod

Expected behavior
Method for which the HttpMethod is not present in the configuration.json should not be in the generated swagger.

To Reproduce
create an api with a simple endpoint with a GET and POST method
create a ocelot configuration.json for these endpoints with UpstreamHttpMethod: ["GET"]
I have checked it with version 1.1.0 and 1.2.0. Both have the problem.

Refactoring this repository. 🔨 🔨 🔨

Refactor

General issue to improve this repository.

Tests

  • Make new way of unittesting. (One unittest method with theory) (Implemented in PR #63)
  • Create new unit tests sources. (based on the old issues)

Project structure, conventions, ...

  • Add .editorconfig.
  • Remove warnings.
  • Comments public members.
  • Check project structure.

Performance

  • Make banchmark tests for swagger transforming.
  • Performance improvement.

Demo

  • Make a demo closer to reality.

Documentation

  • Refresh quick start part.
  • Describe advanced features.
  • Gif

Contribution guide

  • Add contribution guide page.
  • Add info about conventions.
  • Add info about tests

Build & automations

Error when used with Consul as service discovery provider

With Consul up and running (available through port 8500), I'm not able to use this library to access an individual service's swagger documentation.

The Example service is configured as follows:

public void ConfigureServices(IServiceCollection services)
{
	services.AddSwaggerGen(c =>
	{
		c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo { Title = "Example Service", Version = "v1" });
	});
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
	app.UseSwagger();
}

And my ocelot.json:

{
  "ReRoutes": [
    {
      "DownstreamPathTemplate": "/api/{everything}",
      "ServiceName": "example",
      "UpstreamPathTemplate": "/api/example/{everything}",
      "SwaggerKey": "example"
    }
  ],
  "SwaggerEndPoints": [
    {
      "Key": "example",
      "Config": [
        {
          "Name": "Example",
          "Version": "v1",
          "Service": {
            "Name": "example",
            "Path": "/swagger/v1/swagger.json"
          }
        }
      ]
    }
  ],
  "GlobalConfiguration": {
    "ServiceDiscoveryProvider": {
      "Host": "consul",
      "Port": 8500,
      "Type": "Consul"
    }
  }
}

The services are accessible directly (by creating a simple API controller with a HttpGet method) but don't seem to be through swagger. I'm getting the following error in the logs:

System.InvalidOperationException: An invalid request URI was provided. The request URI must either be an absolute URI or BaseAddress must be set.
   at System.Net.Http.HttpClient.PrepareRequestMessage(HttpRequestMessage request)
   at System.Net.Http.HttpClient.SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.GetStringAsync(String requestUri)
   at MMLib.SwaggerForOcelot.Middleware.SwaggerForOcelotMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.MiddlewareAnalysis.AnalysisMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Builder.Extensions.MapMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.MiddlewareAnalysis.AnalysisMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.MiddlewareAnalysis.AnalysisMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.MiddlewareAnalysis.AnalysisMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.MiddlewareAnalysis.AnalysisMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.MiddlewareAnalysis.AnalysisMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.