Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalid zod schema type casting #1561

Closed
xdata-golubev opened this issue Feb 14, 2025 · 1 comment
Closed

Invalid zod schema type casting #1561

xdata-golubev opened this issue Feb 14, 2025 · 1 comment
Labels
bug Something isn't working @kubb/plugin-zod

Comments

@xdata-golubev
Copy link

xdata-golubev commented Feb 14, 2025

What version of kubb is running?

3.5.11

What kind of platform do you use?

MacOS

How does your kubb.config.ts config look like

import { defineConfig } from "@kubb/core";
import { pluginClient } from "@kubb/plugin-client";
import { Include, pluginOas } from "@kubb/plugin-oas";
import { pluginTs } from "@kubb/plugin-ts";
import { pluginZod } from "@kubb/plugin-zod";
import ts from "typescript";
import pkg from "typescript";

export const getApiGeneratedDirectoryPath = (apiName: string) =>
  `./src/shared/api/clients/${apiName}/generated`;

const { factory } = pkg;

const apiName = "apiName";

const includeOperations: Include[] = [
  {
    type: "path",
    pattern: "...",
  },
  {
    type: "path",
    pattern: "...",
  },
  {
    type: "path",
    pattern: "...",
  },
];

export default defineConfig(() => {
  return {
    name: apiName,
    root: ".",
    input: {
      path: "https://myapi",
    },
    output: {
      path: getApiGeneratedDirectoryPath(apiName),
      clean: true,
      barrelType: false,
      extension: { ".ts": "" },
    },
    hooks: {
      done: [
        `prettier --loglevel warn --write ${getApiGeneratedDirectoryPath(
          apiName
        )}`,
        `eslint --fix ${getApiGeneratedDirectoryPath(apiName)}`,
      ],
    },
    plugins: [
      pluginOas({
        generators: [],
        validate: true,
        serverIndex: 1,
      }),
      pluginTs({
        output: {
          path: "./models",
          barrelType: false,
        },
        group: {
          type: "tag",
          name: ({ group }) =>
            `${group
              .split(" ")
              .map(
                (part, index) =>
                  `${
                    index === 0
                      ? part.charAt(0).toLowerCase()
                      : part.charAt(0).toUpperCase()
                  }${part.slice(1)}`
              )
              .join("")}Controller`,
        },
        unknownType: "unknown",
        enumType: "literal",
        enumSuffix: "",
        include: includeOperations,
        mapper: {
          customerId: factory.createPropertySignature(
            undefined,
            factory.createIdentifier("customerId"),
            undefined,
            factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)
          ),
        },
      }),
      pluginZod({
        output: {
          path: `./validationSchemas/`,
          barrelType: false,
        },
        group: {
          type: "tag",
          name: ({ group }) =>
            `${group
              .split(" ")
              .map(
                (part, index) =>
                  `${
                    index === 0
                      ? part.charAt(0).toLowerCase()
                      : part.charAt(0).toUpperCase()
                  }${part.slice(1)}`
              )
              .join("")}Controller`,
        },
        unknownType: "unknown",
        dateType: false,
        typed: true,
        coercion: true,
        include: includeOperations,
      }),
      pluginClient({
        output: {
          path: "controllers",
          barrelType: false,
        },
        group: {
          type: "tag",
          name: ({ group }) =>
            `${group
              .split(" ")
              .map(
                (part, index) =>
                  `${
                    index === 0
                      ? part.charAt(0).toLowerCase()
                      : part.charAt(0).toUpperCase()
                  }${part.slice(1)}`
              )
              .join("")}Controller`,
        },
        dataReturnType: "data",
        pathParamsType: "inline",
        operations: true,
        paramsCasing: "camelcase",

        importPath: `~shared/api/clients/${apiName}/${apiName}AxiosClient`,
        include: includeOperations,
      }),
    ],
  };
});

Swagger/OpenAPI file?

Can't provide the full file, but this is the relevant endpoint:

      "/api/v1/business/{customerId}/notifications":{
         "get":{
            "tags":[
               "Business"
            ],
            "summary":"Get notifications",
            "description":"Get notifications",
            "operationId":"getNotifications",
            "parameters":[
               {
                  "name":"X-Auth-CustomerId",
                  "in":"header",
                  "schema":{
                     "type":"string",
                     "format":"long"
                  },
                  "example":12345678
               },
               {
                  "name":"customerId",
                  "in":"path",
                  "required":true,
                  "schema":{
                     "type":"string",
                     "format":"long"
                  },
                  "example":12345678
               }
            ],
            "responses":{
               "200":{
                  "description":"OK",
                  "content":{
                     "application/json":{
                        "schema":{
                           "type":"array",
                           "items":{
                              "type":"string",
                              "enum":[
                                 "TYPE1",
                                 "TYPE2",
                                 "TYPE3"
                              ]
                           }
                        }
                     }
                  }
               }
            }
         },

What version of external packages are you using(@tanstack-query, MSW, React, Vue, ...)

"react": "18.3.1",
"zod": "3.23.4",
"@tanstack/react-query": "5.56.2",

What steps can reproduce the bug?

  1. Generate the api files
  2. Check validation schema and model

Result:

export type GetNotifications200 = "TYPE1" | "TYPE2" | "TYPE3";

export type GetNotificationsQueryResponse = GetNotifications200;

export const getNotifications200Schema = z.array(
  z.enum(["TYPE1", "TYPE2", "TYPE3"])
) as unknown as ToZod<GetNotifications200>;

export const getNotificationsQueryResponseSchema = z.lazy(
  () => getNotifications200Schema
) as unknown as ToZod<GetNotificationsQueryResponse>;

The schemas are generated correctly, but then cast into a single element instead of an array

How often does this bug happen?

Every time

What is the expected behavior?

Schemas cast correctly to an array of GetNotifications200/GetNotificationsQueryResponse

Additional information

No response

@xdata-golubev xdata-golubev added the bug Something isn't working label Feb 14, 2025
@stijnvanhulle
Copy link
Collaborator

Setting enumSuffix to an empty string will result in some issues in creating enums, best to not set that to enum or Kubb will override other values. I will add an error when an empty string is being used.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working @kubb/plugin-zod
Projects
None yet
Development

No branches or pull requests

2 participants