reduxjs / redux-toolkit

The official, opinionated, batteries-included toolset for efficient Redux development
https://redux-toolkit.js.org
MIT License
10.64k stars 1.15k forks source link

Codegen OpenAPI translates anyOf number/integer to any type #3648

Open saschahofmann opened 1 year ago

saschahofmann commented 1 year ago

In my OpenAPI schema I have a schema type defined as

{
  "Coordinates": {
    "prefixItems": [
      {
        "anyOf": [
          {
            "type": "number"
          },
          {
            "type": "integer"
          }
        ],
        "title": "Lon"
      },
      {
        "anyOf": [
          {
            "type": "number"
          },
          {
            "type": "integer"
          }
        ],
        "title": "Lat"
      }
    ],
    "type": "array",
    "maxItems": 2,
    "minItems": 2
  }
}

which I would expect to be read as

type Coordinates = [number, number]

but instead I receive

type Coordinates = any;

More details

I am running an API using FastAPI, I generate the OpenAPI schema using the inbuilt method which produces a schema with "openapi": "3.1.0". I then use rtk-query-codegen-openapi to create my frontend client.

Reproducible example

Given this openapi.json

{
  "openapi": "3.1.0",
  "info": {
    "title": "Test",
    "description": "",
    "version": "1.0.0"
  },
  "paths": {
    "/test/": {
      "get": {
        "summary": "",
        "description": "",
        "operationId": "get_test",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Coordinates"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Coordinates": {
        "prefixItems": [
          {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "integer"
              }
            ],
            "title": "Lon"
          },
          {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "integer"
              }
            ],
            "title": "Lat"
          }
        ],
        "type": "array",
        "maxItems": 2,
        "minItems": 2
      },
      "HTTPValidationError": {
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "type": "array",
            "title": "Detail"
          }
        },
        "type": "object",
        "title": "HTTPValidationError"
      },
      "ValidationError": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          }
        },
        "type": "object",
        "required": ["loc", "msg", "type"],
        "title": "ValidationError"
      }
    }
  }
}

this empyApi.ts

import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";

// initialize an empty api service that we'll inject endpoints into later as needed
export const emptyApi = createApi({
  reducerPath: "api",
  baseQuery: fetchBaseQuery({
    baseUrl: "localhost",
  }),
  endpoints: () => ({}),
});

and the is openApiConfig.json

{
  "schemaFile": "openapi.json",
  "apiFile": "./emptyApi.ts",
  "apiImport": "emptyApi",
  "outputFile": "./apiClient.ts",
  "exportName": "apiClient",
  "hooks": true,
  "tag": true
}

I receive this apiClient.ts after running rtk-query-codegen-openapi openApiConfig.json

import { emptyApi as api } from "./emptyApi";
export const addTagTypes = [] as const;
const injectedRtkApi = api
  .enhanceEndpoints({
    addTagTypes,
  })
  .injectEndpoints({
    endpoints: (build) => ({
      getTest: build.query<GetTestApiResponse, GetTestApiArg>({
        query: () => ({ url: `/test/` }),
      }),
    }),
    overrideExisting: false,
  });
export { injectedRtkApi as apiClient };
export type GetTestApiResponse =
  /** status 200 Successful Response */ Coordinates;
export type GetTestApiArg = void;
export type Coordinates = any;
export type ValidationError = {
  loc: (string | number)[];
  msg: string;
  type: string;
};
export type HttpValidationError = {
  detail?: ValidationError[];
};
export const { useGetTestQuery } = injectedRtkApi;

As you can see in L19 Coordinates is typed as any.

saschahofmann commented 1 year ago

I had a little dig and found that the issue is caused by oazapfts. I opened an issue there https://github.com/oazapfts/oazapfts/issues/461 and will close this .

saschahofmann commented 1 year ago

Oazapfts now fixed this in https://github.com/oazapfts/oazapfts/pull/462 but this project still relies on oazapfts-patched.