GithubHelp home page GithubHelp logo

guerricm / openapi-route-definition-locator Goto Github PK

View Code? Open in Web Editor NEW

This project forked from jbretsch/openapi-route-definition-locator

0.0 0.0 0.0 429 KB

A RouteDefinitionLocator for Spring Cloud Gateway which creates route definitions dynamically based on OpenAPI (aka Swagger) definitions served by backend (micro)services.

License: MIT License

Java 50.32% Groovy 39.47% Kotlin 10.21%

openapi-route-definition-locator's Introduction

OpenAPI Route Definition Locator for Spring Cloud Gateway

Maven Central License

The OpenAPI Route Definition Locator is a RouteDefinitionLocator for Spring Cloud Gateway which creates route definitions dynamically based on OpenAPI (aka Swagger) definitions served by backend (micro)services.

Why use it?

Let's say you run a number of microservices in a Kubernetes cluster. The microservices provide REST APIs. Some of those REST API resources should be publicly available via a Spring Cloud Gateway service, some should only be accessible internally by your microservices. What do you do?

You could manually create a route definition for each public API resource in a static configuration file for your Spring Cloud Gateway service. But maintaining these route definition gets tedious if you have many microservices. Even more so if those microservices are maintained by many teams. For instance, API Gateway releases (or at least configuration changes) must be synchronized with releases of other microservices. That's no pleasure in a large organization.

Or you can use the OpenAPI Route Definition Locator in your API Gateway to have all routes for your public API resources automatically configured during runtime. This works roughly as follows:

  1. Have your microservices provide an OpenAPI definition for all their public API resources via a (non-public) HTTP endpoint.
  2. Add the OpenAPI Route Definition Locator Spring Boot starter module to your API Gateway.
  3. Configure in the Spring properties of your API Gateway a list of microservices which the OpenAPI Route Definition Locator should monitor.
  4. The OpenAPI Route Definition Locator regularly retrieves the OpenAPI definitions of your microservices and configures a route for each of the operations in those OpenAPI definitions.

Overview

Versioning scheme and compatibility

Version Spring Cloud Spring Boot Minimum Java Version
x.y.z-sc-2022.0 2022.0.x 3.0.x, 3.1.x 17
x.y.z-sc-2021.0 2021.0.x 2.6.x, 2.7.x 8

Usage

Quickstart

Add the Spring Boot Starter module of the OpenAPI Route Definition Locator to your API Gateway service.

Maven

<dependencyManagement>
   <dependencies>
      <dependency>
         <groupId>net.bretti.openapi-route-definition-locator</groupId>
         <artifactId>openapi-route-definition-locator-bom</artifactId>
         <version>0.6.4-sc-2022.0</version>
         <type>pom</type>
         <scope>import</scope>
      </dependency>
   </dependencies>
</dependencyManagement>
<dependency>
  <groupId>net.bretti.openapi-route-definition-locator</groupId>
  <artifactId>openapi-route-definition-locator-spring-cloud-starter</artifactId>
</dependency>

Gradle Kotlin DSL

implementation(platform("net.bretti.openapi-route-definition-locator:openapi-route-definition-locator-bom:0.6.4-sc-2022.0"))
implementation("net.bretti.openapi-route-definition-locator:openapi-route-definition-locator-spring-cloud-starter")

Add the list of services which the OpenAPI Route Definition Locator should monitor to the application.yml of your Spring Cloud API Gateway.

openapi-route-definition-locator:
  services:
    - id: service-users
      uri: http://service-users:8080
    - id: service-orders
      uri: http://service-orders:8080

Have the services (configured above) return an OpenAPI (Version 3) definition under the HTTP URL path /internal/openapi-definition.

Say, the service-users returns the following OpenAPI definition.

openapi: 3.0.3
info:
  title: Users API
  version: 0.1.0
paths:
  /users:
    get:
      responses:
        200:
          description: ''
          content:
            text/plain:
              schema:
                type: string

Then the OpenAPI Route Definition Locator creates a route definition that would look like this if you configured it manually in the application.yml.

spring:
  cloud:
    gateway:
      routes:
         - id: 36e0f904-0b89-446e-9aee-5cd0285cb54f
           uri: http://service-users:8080
           predicates:
             - Method=GET 
             - Path=/users

         # More routes for http://service-orders:8080. 

You can find a fully working example at sample-apps. See the sample-apps/README.md.

Advanced Configuration

URI to OpenAPI definition

Per default the OpenAPI definition of a service is retrieved via the URL path /internal/openapi-definition relative to the base URL of the respective service. If your service serves its OpenAPI definition from a different path, you can configure the OpenAPI Route Definition Locator accordingly. In fact, the OpenAPI definition can be retrieved from any HTTP(S) URL or from local locations referenced via the URL schemas file: or classpath: that are supported by Spring's ResourceLoader. The OpenAPI definition URI can be set globally or per service. Of course, you can set it also globally and per service. The latter overrides the former.

Setting the OpenAPI definition URL globally:

openapi-route-definition-locator:
  # Default: /internal/openapi-definition
  openapi-definition-uri: /global-custom-path-to/openapi-definition

Setting the OpenAPI definition URL per service:

openapi-route-definition-locator:
  services:
    - id: service1
      uri: http://service1:8080
      # OpenAPI definition is retrieved from <http://service1:8080/internal/openapi-definition>.

    - id: service2
      uri: http://service2:8080
      openapi-definition-uri: /custom-path-to/openapi-definition
      # OpenAPI definition is retrieved from <http://service2:8080/custom-path-to/openapi-definition>.

    - id: service3
      uri: http://service3:8080
      openapi-definition-uri: http://openapi-repository/service3/openapi-definition
      # OpenAPI definition is retrieved from <http://openapi-repository/service3/openapi-definition>.

    - id: service4
      uri: http://service4:8080
      openapi-definition-uri: classpath:service4/openapi.public.yaml
      # OpenAPI definition is retrieved from given classpath location.

    - id: service5
      uri: http://service5:8080
      openapi-definition-uri: file:/etc/api-gateway/openapi-definitions/service5/openapi.public.yaml
      # OpenAPI definition is retrieved from given file location.

Default Filters

As the OpenAPI Route Definition Locator is just another RouteDefinitionLocator, all Default Filters you have defined in your application.yml also apply to the RouteDefinitions created by the OpenAPI Route Definition Locator.

For example:

spring:
  cloud:
    gateway:
      default-filters:
        - AddResponseHeader=X-Response-FromGlobalConfig, global-sample-value

Additional RouteDefinition attributes in OpenAPI definitions

Spring Cloud Gateway route definitions can have more attributes. You may want to use

with the routes created from your OpenAPI definitions.

You can define those attributes within your OpenAPI definitions:

  • globally for all operations within one OpenAPI definition and
  • for each operation in an OpenAPI definition.

You do this by adding the configuration properties you would have otherwise added to the application.yml to your OpenAPI definition within the object x-gateway-route-settings at the top level (for global settings) or at the operation level (for operation specific settings).

Let's say, the service-users provides two HTTP endpoints

  • GET /api/users and
  • GET /api/users/{userId}

which should be publicly available as

  • GET /users and
  • GET /users/{userId}.

And you want the GET /users/{userId} endpoint to be available only after 2022-01-20T17:42:47.789+01:00[Europe/Berlin].

Spring Cloud Gateway offers the PrefixPath filter and the After predicate for those tasks.

You can use them in your OpenAPI definition as follows.

openapi: 3.0.3
info:
  title: Users API
  version: 0.1.
x-gateway-route-settings:
  filters:
    - PrefixPath=/api
paths:
  /users:
    get:
      responses:
        200:
          description: ''
          content:
            text/plain:
              schema:
                type: string
  /users/{userId}:
    get:
      parameters:
        - name: userId
          in: path
          schema:
            type: string
          required: true
      responses:
        200:
          description: ''
          content:
            text/plain:
              schema:
                type: string
      x-gateway-route-settings:
        predicates:
          - After=2022-01-20T17:42:47.789+01:00[Europe/Berlin]

Then the OpenAPI Route Definition Locator creates route definitions that would look like this if you configured them manually in the application.yml.

spring:
  cloud:
    gateway:
      routes:
         - id: 36e0f904-0b89-446e-9aee-5cd0285cb54f
           uri: http://service-users:8080
           predicates:
             - Method=GET 
             - Path=/users
           filters:
              - PrefixPath=/api
         - id: 4340cb37-882a-4b8f-bc48-d035060d9ac2
           uri: http://service-users:8080
           predicates:
             - Method=GET 
             - Path=/users/{userId}
             - After=2022-01-20T17:42:47.789+01:00[Europe/Berlin]
           filters:
              - PrefixPath=/api

The global x-gateway-route-settings object and the operation specific x-gateway-route-settings objects are merged according to JSON Merge Patch (RFC7386) with one exception: Merging two lists is done by concatenating them.

So merging

x-gateway-route-settings:
  filters:
    - PrefixPath=/api
    - name: SetStatus
      args:
        status: 418
  order: 1
  metadata:
    optionName: "OptionValue"
    compositeObject:
      name: "value"
    aList:
      - foo
      - bar
    iAmNumber: 1

and

x-gateway-route-settings:
   filters:
      - AddResponseHeader=X-Response-FromOpenApiDefinition, sample-value
  metadata:
    compositeObject:
      otherName: 2
    aList:
      - quuz

yields

x-gateway-route-settings:
  filters:
    - PrefixPath=/api
    - name: SetStatus
      args:
        status: 418
    - AddResponseHeader=X-Response-FromOpenApiDefinition, sample-value
  order: 1
  metadata:
    optionName: "OptionValue"
    compositeObject:
      name: "value"
      otherName: 2
    aList:
      - foo
      - bar
      - quuz
    iAmNumber: 1

Customize RouteDefinitions dynamically

For cases in which you need more control over the RouteDefinitions which are created based on your OpenAPI definitions, the OpenAPI Route Definition Locator provides a hook you can use to dynamically alter those RouteDefinitions.

For this you have to implement one or more Spring beans which implement the OpenApiRouteDefinitionCustomizer interface. Each of those customizer beans is called for each created RouteDefinition. In your customizer method you have access to

  • the RouteDefinition,
  • the configuration of the service the RouteDefinition belongs to,
  • the global OpenAPI extensions of the service's OpenAPI definition, and
  • the OpenAPI extensions of the OpenAPI operation the RouteDefinition is based on.

Example customizer:

package net.bretti.sample.apigateway.customizer;

import net.bretti.openapi.route.definition.locator.core.config.OpenApiRouteDefinitionLocatorProperties;
import net.bretti.openapi.route.definition.locator.core.customizer.OpenApiRouteDefinitionCustomizer;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component
public class SampleOpenApiRouteDefinitionCustomizer implements OpenApiRouteDefinitionCustomizer {
    @Override
    public void customize(
            RouteDefinition routeDefinition,
            OpenApiRouteDefinitionLocatorProperties.Service service,
            Map<String, Object> openApiGlobalExtensions,
            Map<String, Object> openApiOperationExtensions
    ) {
        Object xSampleKeyValue = openApiOperationExtensions.get("x-sample-key");
        if (!(xSampleKeyValue instanceof String)) {
            return;
        }

        FilterDefinition filter = new FilterDefinition("AddResponseHeader=X-Sample-Key-Was, " + xSampleKeyValue);
        routeDefinition.getFilters().add(filter);
    }
}

Also see the SampleOpenApiRouteDefinitionCustomizer.java and the openapi.public.yaml in the sample apps.

Configure OpenAPI retrieval properties

Retrieval interval

The OpenAPI Route Definition Locator regularly retrieves the OpenAPI definitions from the configured services. By default, each retrieval run starts 5 minutes after the last run completed. You can configure a different delay with the following Spring property.

openapi-route-definition-locator:
  update-scheduler:
    fixed-delay: 30s

See Converting Durations for possible duration values.

Grace period for removal of route definitions

When the OpenAPI Route Definition Locator encounters a problem while retrieving the OpenAPI definition from a service, it does not immediately remove its routes definitions. The rationale is: Your service may still be able to serve normal API requests although there was a problem with retrieving its OpenAPI definition.

However, after some grace period, the route definitions are removed. The default is 15 minutes. You can configure a different grace period with the following Spring property.

openapi-route-definition-locator:
  update-scheduler:
    remove-routes-on-update-failures-after: 120s

See Converting Durations for possible duration values.

Disabling the OpenAPI Route Definition Locator

You can disable the OpenAPI Route Definition Locator by setting the Spring property

openapi-route-definition-locator:
  enabled: false

If this property is not set or set to true, the OpenAPI Route Definition Locator is enabled.

Metrics

The OpenAPI Route Definition Locator provides metrics via Micrometer.

If you have enabled the Prometheus endpoint you can expect output like this:

# HELP openapi_route_definition_locator_routes_count Number of routes managed by the OpenAPI Route Definition Locator
# TYPE openapi_route_definition_locator_routes_count gauge
openapi_route_definition_locator_routes_count{upstream_service="service-users",} 2.0
openapi_route_definition_locator_routes_count{upstream_service="service-orders",} 1.0

# HELP openapi_route_definition_locator_openapi_definition_updates_seconds_max Time and count of attempts to update the route definitions for registered services based on their OpenAPI definitions.
# TYPE openapi_route_definition_locator_openapi_definition_updates_seconds_max gauge
openapi_route_definition_locator_openapi_definition_updates_seconds{update_result="success",update_result_detailed="success_with_route_changes",upstream_service="service-users",quantile="0.5",} 0.243269632
openapi_route_definition_locator_openapi_definition_updates_seconds{update_result="success",update_result_detailed="success_with_route_changes",upstream_service="service-users",quantile="0.8",} 0.243269632
openapi_route_definition_locator_openapi_definition_updates_seconds{update_result="success",update_result_detailed="success_with_route_changes",upstream_service="service-users",quantile="0.95",} 0.243269632
openapi_route_definition_locator_openapi_definition_updates_seconds{update_result="success",update_result_detailed="success_with_route_changes",upstream_service="service-users",quantile="0.98",} 0.243269632
openapi_route_definition_locator_openapi_definition_updates_seconds_count{update_result="success",update_result_detailed="success_with_route_changes",upstream_service="service-users",} 1.0
openapi_route_definition_locator_openapi_definition_updates_seconds_sum{update_result="success",update_result_detailed="success_with_route_changes",upstream_service="service-users",} 0.248666992

openapi_route_definition_locator_openapi_definition_updates_seconds{update_result="success",update_result_detailed="success_without_route_changes",upstream_service="service-users",quantile="0.5",} 0.0
openapi_route_definition_locator_openapi_definition_updates_seconds{update_result="success",update_result_detailed="success_without_route_changes",upstream_service="service-users",quantile="0.8",} 0.0
openapi_route_definition_locator_openapi_definition_updates_seconds{update_result="success",update_result_detailed="success_without_route_changes",upstream_service="service-users",quantile="0.95",} 0.0
openapi_route_definition_locator_openapi_definition_updates_seconds{update_result="success",update_result_detailed="success_without_route_changes",upstream_service="service-users",quantile="0.98",} 0.0
openapi_route_definition_locator_openapi_definition_updates_seconds_count{update_result="success",update_result_detailed="success_without_route_changes",upstream_service="service-users",} 0.0
openapi_route_definition_locator_openapi_definition_updates_seconds_sum{update_result="success",update_result_detailed="success_without_route_changes",upstream_service="service-users",} 0.0

openapi_route_definition_locator_openapi_definition_updates_seconds{update_result="failure",update_result_detailed="failure_retrieval",upstream_service="service-users",quantile="0.5",} 0.0
openapi_route_definition_locator_openapi_definition_updates_seconds{update_result="failure",update_result_detailed="failure_retrieval",upstream_service="service-users",quantile="0.8",} 0.0
openapi_route_definition_locator_openapi_definition_updates_seconds{update_result="failure",update_result_detailed="failure_retrieval",upstream_service="service-users",quantile="0.95",} 0.0
openapi_route_definition_locator_openapi_definition_updates_seconds{update_result="failure",update_result_detailed="failure_retrieval",upstream_service="service-users",quantile="0.98",} 0.0
openapi_route_definition_locator_openapi_definition_updates_seconds_count{update_result="failure",update_result_detailed="failure_retrieval",upstream_service="service-users",} 0.0
openapi_route_definition_locator_openapi_definition_updates_seconds_sum{update_result="failure",update_result_detailed="failure_retrieval",upstream_service="service-users",} 0.0

openapi_route_definition_locator_openapi_definition_updates_seconds{update_result="failure",update_result_detailed="failure_publication",upstream_service="service-users",quantile="0.5",} 0.0
openapi_route_definition_locator_openapi_definition_updates_seconds{update_result="failure",update_result_detailed="failure_publication",upstream_service="service-users",quantile="0.8",} 0.0
openapi_route_definition_locator_openapi_definition_updates_seconds{update_result="failure",update_result_detailed="failure_publication",upstream_service="service-users",quantile="0.95",} 0.0
openapi_route_definition_locator_openapi_definition_updates_seconds{update_result="failure",update_result_detailed="failure_publication",upstream_service="service-users",quantile="0.98",} 0.0
openapi_route_definition_locator_openapi_definition_updates_seconds_count{update_result="failure",update_result_detailed="failure_publication",upstream_service="service-users",} 0.0
openapi_route_definition_locator_openapi_definition_updates_seconds_sum{update_result="failure",update_result_detailed="failure_publication",upstream_service="service-users",} 0.0

# HELP openapi_route_definition_locator_openapi_definition_updates_seconds_max Time and count of attempts to update the route definitions for registered services based on their OpenAPI definitions.
# TYPE openapi_route_definition_locator_openapi_definition_updates_seconds_max gauge
openapi_route_definition_locator_openapi_definition_updates_seconds_max{update_result="success",update_result_detailed="success_with_route_changes",upstream_service="service-users",} 0.0
openapi_route_definition_locator_openapi_definition_updates_seconds_max{update_result="success",update_result_detailed="success_without_route_changes",upstream_service="service-users",} 0.542245334
openapi_route_definition_locator_openapi_definition_updates_seconds_max{update_result="failure",update_result_detailed="failure_retrieval",upstream_service="service-users",} 0.0
openapi_route_definition_locator_openapi_definition_updates_seconds_max{update_result="failure",update_result_detailed="failure_publication",upstream_service="service-users",} 0.0

openapi-route-definition-locator's People

Contributors

jbretsch avatar

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.