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

Pull all schemas instead of just those linked to paths #11

Open
dradetsky opened this issue Oct 6, 2020 · 3 comments
Open

Pull all schemas instead of just those linked to paths #11

dradetsky opened this issue Oct 6, 2020 · 3 comments
Labels
enhancement New feature or request

Comments

@dradetsky
Copy link

This is a feature request.

The way spec2ts (and most comparable tools) work to generate types from schemas is:

  1. for each path in paths
  2. for each operation in path
  3. for the request & all responses, find the associated schema & emit a type

This is probably what the tool should do by default.

However, from my perspective, there are times when I'd rather just say: for every schema in components.schemas, emit a type. I can explain more about the cases where this is useful if desired.

For example, you could add a flag --all-schemas which would say to just emit a type for every schema in the target file, instead of trying to follow the paths.

If you were to do this, it would also be potentially useful to add the ability to not validate that the input file is valid openapi, although this is something that users could work around in the short term. The reason is that I would want to write a fragment with just my schemas and have the main file refer to it with jsonschema refs. But if I want to pass the file with just the schemas into spec2ts, i would have to write

openapi: 3.0.3

info:
  title: chicken
  version: 0.0.0

paths:

components:
  schemas:
    ThingKey:
      type: object
      properties:
        userId:
          type: integer
        thingId:
          type: integer
      required:
        - userId
        - thingId

    ThingData:
      type: object
      properties:
        lastSeen:
          type: string
          format: date-time
        value:
          type: integer
          
    Thing:
      type: object
      allOf:
        - $ref: '#/components/schemas/ThingKey'
        - $ref: '#/components/schemas/ThingData'

the main problem with this is that I'm repeating info.version (and I guess the openapi version) multiple times, so if I bump my api's version, I have to change it in multiple places. This is merely kind of ugly though, and not a major problem.

Another possible improvement would be for the user to specify the prefix under which all entries would be assumed to be schemas, which by default would be components.schemas. That way, the user could have a fragment with just a schema and no prefix (i think people do this a lot), and set the prefix to empty. This is also not very important. Personally, I always make my fragments start with the components.schemas prefix like the above example because it makes it obvious what these things are, but not everybody does this.


So, to summarize: I would find it very handy if there was a flag --all-schemas so I could run

oapi2ts api/file-with-just-schemas-like-above-example.yaml --all-schemas -o path/for/generated/stuff

and have it just emit whatever was in components.schemas in that file. There are also some ways you could improve this & make it more flexible.

@dradetsky
Copy link
Author

Also, I should note that I managed to hack something together for this, but I'd prefer it if a less shitty version could be added to spec2ts. I'm trying to get more people in my organization to use openapi, and having a good tool for tasks like this would help me do that.

The core of what I did was just this

  const context = await createContext(spec, options)
  const outs = []

  Object.entries(spec.components.schemas).forEach(([name, schema]) => {
    const x = makeSchemaThing(name, schema, context)
    outs.push(x)
  })

and

function makeSchemaThing(name, schema, ctx) {
  const st = getTypeFromSchema(schema, ctx)
  const tsdecl = core.createTypeOrInterfaceDeclaration({
    modifiers: [core.modifier.export],
    undefined,
    name,
    type: st
  })
  return tsdecl
}

and then just some printer.printNodes. It seems to work, but I'm not 100% sure.

@SomaticIT SomaticIT added the enhancement New feature or request label Oct 7, 2020
@dradetsky
Copy link
Author

Here's some real-world use-cases for this:

  1. You're trying to emit types from a schema you haven't actually finished yet.

  2. You start a project to build a replacement for a legacy service. You believe the output type for some endpoint E has some schema S. So you define that schema in the openapi yaml, emit a typescript type T for S, and begin writing domain code using T in various places. Then you discover that actually, the output for E has to, temporarily, have some schema SS to be compatible with another legacy service. Long term, you're going to replace the other legacy service, and go back to returning an S response from endpoint E, but in the short run, you have to return SS from E. But if you change E to return an SS, you break all the domain code that relies on T generated from S. Note that in this case we cannot define SS as an extension of S; S and SS are disjoint, not subtypes.

What you'd like to do is just keep both S and SS around, and even though no endpoints point to S anymore, you still generate T from it and continue to use that in your domain code. Eventually you get rid of SS and nothing really changes.

You might say that the correct answer here is "don't use types generated from openapi yaml in your domain code." And this is not necessarily wrong, but it doesn't really help me at the moment. Also, the slightly longer term plan for the project involves replacing that same response schema S with another schema R, and in that case changing the generated type across my codebase is exactly what I want.

@SomaticIT
Copy link
Contributor

This enhancement could make sense.
It should not be the default behavior but instead be available using a specific option.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants