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

Duplicate remote $ref paths not resolved correctly #1135

Open
SimonLudwigNeteleven opened this issue Oct 10, 2024 · 12 comments
Open

Duplicate remote $ref paths not resolved correctly #1135

SimonLudwigNeteleven opened this issue Oct 10, 2024 · 12 comments
Labels
bug 🔥 Something isn't working feature 🚀 New feature or request internal ⚙️ Internal development related

Comments

@SimonLudwigNeteleven
Copy link

SimonLudwigNeteleven commented Oct 10, 2024

Description

The command "yarn generate" should create all generated files including "types.gen.ts" without any issues.
The files are generated, but the response type of “B” is broken after the command finishes. This only happens, if both path references in the yaml file are the same for both paths. The first correct one wins, if both are the same.

This behavior breaks the type generation and I could only work around it, by making one path beginning with “http” instead of “https” in my project. This workaround is obviously more hack then a solution and works only for two paths maximum.
You can’t test the hack solution with “http” in stackblitz, because only “https” is allowed for the ref path, but you can reproduce the error.

Steps to reproduce the behavior

  1. Go to my stackblitz example
  2. open the terminal
  3. enter “yarn”
  4. enter “yarn generate”
  5. inspect the generated files (types.gen.ts) in packages/test-api/navigation-api/src-gen

Expected behavior

AResponse and BResponse should be “(unknown)”.

Reproducible example or configuration

https://stackblitz.com/edit/vitejs-vite-6tvzvt?file=packages%2Ftest-api%2Fopenapi-spec%2Fnavigation-api.yaml

OpenAPI specification (optional)

No response

System information (optional)

No response

@SimonLudwigNeteleven SimonLudwigNeteleven added the bug 🔥 Something isn't working label Oct 10, 2024
Copy link

stackblitz bot commented Oct 10, 2024

Fix this issue in StackBlitz Codeflow Start a new pull request in StackBlitz Codeflow.

@mrlubos mrlubos added the prioritized 🚚 This issue has been prioritized and will be worked on soon label Oct 10, 2024
@mrlubos
Copy link
Member

mrlubos commented Oct 10, 2024

Nice catch, thanks for reporting @SimonLudwigNeteleven!

@mrlubos
Copy link
Member

mrlubos commented Nov 10, 2024

@SimonLudwigNeteleven quick update, I found that this is most likely an issue with the underlying dependency https://github.com/APIDevTools/json-schema-ref-parser. Maybe there's a configuration for this, but a quick search didn't return anything.

For context, this is what happens internally. The first reference gets correctly bundled, for example:

{
  "type": "object",
  "properties": { "foo": { "enum": [], "type": "string" } }
}

while the second one produces this faulty reference.

{
  "$ref": "#/paths/~1a/get/responses/default/content/application~1json/schema"
}

I do plan to vertically integrate this tool eventually. Most people don't compose their schemas like this so it has not been reported before. Could you provide more context about your setup? How would you expect the remote reference to be resolved? Inline them or should they also create a reusable component and reference that?

@mrlubos mrlubos added feature 🚀 New feature or request internal ⚙️ Internal development related and removed prioritized 🚚 This issue has been prioritized and will be worked on soon labels Nov 10, 2024
@mrlubos mrlubos changed the title generation for response type breaks if same response schema ref is used twice for paths Duplicate remote $ref paths not resolved correctly Nov 10, 2024
@mrlubos
Copy link
Member

mrlubos commented Dec 1, 2024

@mmospanenko could you check the question above too?

@mmospanenko
Copy link

we have enough complicated flow, as source builds 300k lines yml file and has a lot of mistakes. I use as reference, giving ability to override some nodes ($ref) and as result we have template with tons of refs like your ("$ref": "#/paths/1a/get/responses/default/content/application1json/schema"). And these refs have own refs, and so on.

Now I use multi-file structure (from Redocly with their CLI) and it solves my case. So I have source file, it splits by files, then devs can add another copy of this file structure as 'overrides' folder, we prepare 'template' output file only with required paths, it merges and builds as result.

So TS in my case builds only from output, correct file, without complicated references. And this flow is better for my case now, not sure that TS generator should care about this, but it is definitely a bug from OAS perspective.

@larsrickert
Copy link

We are also facing this issue which unfortunately prevents us from generating the API client for our API.
Below is a minimal reproduction example. Whenever a $ref is used twice, the second occurrence is not generated correctly and the generated TypeScript code has errors.

openapi.yml:

openapi: "3.1.0"
info:
  version: 0.0.0
  title: Example API spec
paths:
  /example:
    get:
      summary: Some example path
      responses:
        "200":
          description: 200 OK
        "401":
          description: 401 UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: "./components.yml#/components/schemas/Error"
        "500":
          description: 500 INTERNAL SERVER ERROR
          content:
            application/json:
              schema:
                $ref: "./components.yml#/components/schemas/Error"

components.yml

components:
  schemas:
    Error:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
        message:
          type: string

openapi-ts.config.ts

import { defineConfig } from "@hey-api/openapi-ts";

export default defineConfig({
  client: "@hey-api/client-fetch",
  input: "./openapi.yml",
  // the issues also exists when not using the experimental parser
  experimentalParser: true,
  output: {
    path: "./src/api",
    format: "prettier",
  },
});

Resulting code with errors:

// This file is auto-generated by @hey-api/openapi-ts

export type GetExampleData = {
  body?: never;
  path?: never;
  query?: never;
  url: "/example";
};

export type GetExampleErrors = {
  /**
   * 401 UNAUTHORIZED
   */
  401: {
    code: string;
    message: string;
  };
  /**
   * 500 INTERNAL SERVER ERROR
   */
  500: Schema;
};

export type GetExampleError = GetExampleErrors[keyof GetExampleErrors];

export type GetExampleResponses = {
  /**
   * 200 OK
   */
  200: unknown;
};
image

@mrlubos
Copy link
Member

mrlubos commented Jan 9, 2025

Hey all, the good news is there's now https://github.com/hey-api/json-schema-ref-parser so the fix is a bit closer than before, I just need to find time to prioritise it

@larsrickert
Copy link

@mrlubos Thanks! And also cudos for hey API itself, I really like it :)

So is there something I can currently do in my project to fix this or do I have to wait until hey-api/json-schema-ref-parser is integrated by you?

@mrlubos
Copy link
Member

mrlubos commented Jan 9, 2025

Nothing you can do on your end to fix this I am afraid, this is a bug with how those $refs are resolved. I am not sure how much effort it will be to fix at this point.

A workaround on your end would be to bundle the schema yourself into a single file and pass that to openapi-ts, so you wouldn't have to rely on the ref resolver which has the bug

@pranav-growthx
Copy link

Hey @mrlubos was there any update on this?

By the way, really love the lib.

@pranav-growthx
Copy link

If anyone wants to NOT Bundle schemas and still have it work, add the same component in the yaml and then make it ref the actual external schema.

eg.

components:
  schemas:
     # hey-api's having a dependency breaking thats breaking generated types 
     # when the same external ref is repeated . 
     # Temporary fix is to create a schema that points to the external ref and then use it. 
    TeamSize:
      $ref: "./company.yaml#/components/schemas/TeamSize"

@danieltott
Copy link

I followed @mrlubos 's advice but it took me a few to try to figure it out, so I thought I'd share.

I was having a lot of trouble (honestly a lot of similar issues) using the bundler that comes with openapi-ts, so I ended up using redocly-cli to bundle. redocly-cli is really really great at dealing with openapi schemas and I think the maintainers should take a look at replacing json-schema-ref-parser - the difference is night and day.

Anyway, here's my bundle -> createClient step:

execSync(`redocly bundle ${inputUrl} --output ./.tmp/bundled.yml`);
await createClient({
  input: './.tmp/bundled.yml',
  //...
})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🔥 Something isn't working feature 🚀 New feature or request internal ⚙️ Internal development related
Projects
None yet
Development

No branches or pull requests

6 participants