k kamalam
k kamalam

Reputation: 47

Override ref value in smallrye microprofile using custom OASFilter in quarkus application

I'm trying to override ref value in schema for microprofile health check API in quarkus application. I followed the below link Smallrye open api interceptor and created a custom filter which overrides OASFilter. But, the ref value is not picking the new ref value from the filter.

@Override
public APIResponse filterAPIResponse(APIResponse apiResponse) {
    if (apiResponse.getRef()=="#/components/schemas/HealthCheckResponse")
    {
        String ref = "#components/schemas/microProfile";
        apiResponse.setRef(ref);
    }

    return apiResponse;
}

Basically, I need to add description to each property inside a schema.

Existing schema:

HealthCheckResponse:
  type: object
  properties:
    data:
      type: object
      nullable: true
    name:
      type: string
    status:
      $ref: '#/components/schemas/HealthCheckStatus'
HealthCheckStatus:
  enum:
  - DOWN
  - UP
  type: string

Expected schema change:

microProfile:
  description: microprofile response
  type: object
  properties:
    data:
      description: "Information of the service. If the service is down, this holds\
        \ the information of why it is failed."
      type: object
    name:
      description: 'Service Name '
      type: string
    status:
      description: 'Service Status '
      type: string

I added the property mp.openapi.filter= custom filter name in application.properties file. Any help is greatly appreciated.

Upvotes: 0

Views: 1137

Answers (1)

jcompetence
jcompetence

Reputation: 8393

There are 2 ways that I am aware off:



You can programmatically create your own custom schema and reference it.

In this case, the Schema is created programmatically, and does not have to exist anywhere in your openapi.yaml file.

    void updateHealthStatusRefWithProgrammaticSchema(OpenAPI openAPI) {

        openAPI.getPaths().getPathItems().forEach((String pathName, PathItem pathItem) -> {
            if (pathName.equalsIgnoreCase("/health-check")) {

                Schema dynamicSchema = OASFactory.createSchema().title("Programmatic-MicroProfile").description("dynamic-schema-description").type(Schema.SchemaType.OBJECT).properties(Map.of("custom-field-data", OASFactory.createSchema().description("Information of the service. If the service is down, this holds the information of why it is failed.").type(Schema.SchemaType.OBJECT)));
                openAPI.getComponents().addSchema("Dynamic-MicroProfile", dynamicSchema);

                pathItem.getGET().getResponses().getAPIResponse("200").getContent().getMediaType("application/json").setSchema(dynamicSchema);
            }
        });
    }

enter image description here



You can have a defined Static Schema in your openapi.yaml which you can reference programmatically.

In this case, the schema must exist in your openapi.yaml file, as you can see we are searching for it by doing get()**

    void updateHealthStatusRefByUsingStaticSchema(OpenAPI openAPI) {

        openAPI.getPaths().getPathItems().forEach((String pathName, PathItem pathItem) -> {
            if (pathName.equalsIgnoreCase("/health-check")) {
                Schema staticMicroProfileSchema = openAPI.getComponents().getSchemas().get("Static-MicroProfile");
                pathItem.getGET().getResponses().getAPIResponse("200").getContent().getMediaType(MediaType.APPLICATION_JSON).setSchema(staticMicroProfileSchema);
            }
        });
    }

enter image description here



You update the openapi.yaml only if you want to have a Static Schema already defined.

openapi.yaml

components:
  schemas:
      HealthCheckResponse:
        type: object
        properties:
          data:
            type: object
            nullable: true
          name:
            type: string
          status:
            $ref: '#/components/schemas/HealthCheckStatus'
      HealthCheckStatus:
        enum:
          - DOWN
          - UP
        type: string
      Static-MicroProfile:
        description: microprofile response
        type: object
        properties:
          data:
            description: "Information of the service. If the service is down, this holds\
              \ the information of why it is failed."
            type: object
          name:
            description: 'Service Name '
            type: string
          status:
            description: 'Service Status '
            type: string

enter image description here


Filter:

package org.acme;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.eclipse.microprofile.openapi.OASFactory;
import org.eclipse.microprofile.openapi.OASFilter;
import org.eclipse.microprofile.openapi.models.Components;
import org.eclipse.microprofile.openapi.models.OpenAPI;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import org.eclipse.microprofile.openapi.models.PathItem;
import org.eclipse.microprofile.openapi.models.examples.Example;

import io.quarkus.logging.Log;
import org.eclipse.microprofile.openapi.models.media.Schema;

import javax.ws.rs.core.MediaType;

public class CustomOASFilter implements OASFilter {

    ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void filterOpenAPI(OpenAPI openAPI) {

        //openApi.getComponents() will result in NULL as we don't have any openapi.yaml file.
        Components defaultComponents = OASFactory.createComponents();
        if (openAPI.getComponents() == null) {
            openAPI.setComponents(defaultComponents);
        }

        generateExamples().forEach(openAPI.getComponents()::addExample);

        updateHealthStatusRefWithProgrammaticSchema(openAPI);
    }

    Map<String, Example> generateExamples() {

        Map<String, Example> examples = new LinkedHashMap<>();

        try {

            ClassLoader loader = Thread.currentThread().getContextClassLoader();

            String userJSON = new String(loader.getResourceAsStream("user.json").readAllBytes(), StandardCharsets.UTF_8);
            String customerJson = new String(loader.getResourceAsStream("customer.json").readAllBytes(), StandardCharsets.UTF_8);

            Example userExample = OASFactory.createExample().description("User JSON Example Description").value(objectMapper.readValue(userJSON, ObjectNode.class));

            Example customerExample = OASFactory.createExample().description("Customer JSON Example Description").value(objectMapper.readValue(customerJson, ObjectNode.class));

            examples.put("userExample", userExample);
            examples.put("customerExample", customerExample);

        } catch (IOException ioException) {
            Log.error(ioException);
        }
        return examples;
    }

        void updateHealthStatusRefWithProgrammaticSchema(OpenAPI openAPI) {

            openAPI.getPaths().getPathItems().forEach((String pathName, PathItem pathItem) -> {
                if (pathName.equalsIgnoreCase("/health-check")) {

                    Schema dynamicSchema = OASFactory.createSchema().title("Programmatic-MicroProfile").description("dynamic-schema-description").type(Schema.SchemaType.OBJECT).properties(Map.of("custom-field-data", OASFactory.createSchema().description("Information of the service. If the service is down, this holds the information of why it is failed.").type(Schema.SchemaType.OBJECT)));
                    openAPI.getComponents().addSchema("Dynamic-MicroProfile", dynamicSchema);

                    pathItem.getGET().getResponses().getAPIResponse("200").getContent().getMediaType("application/json").setSchema(dynamicSchema);
                }
            });
        }

        void updateHealthStatusRefByUsingStaticSchema(OpenAPI openAPI) {

            openAPI.getPaths().getPathItems().forEach((String pathName, PathItem pathItem) -> {
                if (pathName.equalsIgnoreCase("/health-check")) {
                    Schema staticMicroProfileSchema = openAPI.getComponents().getSchemas().get("Static-MicroProfile");
                    pathItem.getGET().getResponses().getAPIResponse("200").getContent().getMediaType(MediaType.APPLICATION_JSON).setSchema(staticMicroProfileSchema);
                }
            });
        }
}

application.properties:

mp.openapi.filter=org.acme.CustomOASFilter

Full-Example:

https://github.com/smustafa/quarkuks-openapi-exampleobject-loading-external-files

Upvotes: 1

Related Questions