Manweill/swagger-axios-codegen

Error when generating parameters by reference

danielgelling opened this issue · 26 comments

When specifying a global parameter in documentation.parameters and referencing to this parameter in the request ('$ref': '#/parameters/someParam') specification an error occurs when generating the client.

(node:93171) UnhandledPromiseRejectionWarning: Error: Request failed with status code 500
    at createError (/path/to/project/node_modules/axios/lib/core/createError.js:16:15)
    at settle (/path/to/project/node_modules/axios/lib/core/settle.js:17:12)
    at IncomingMessage.handleStreamEnd (/path/to/project/node_modules/axios/lib/adapters/http.js:237:11)
    at IncomingMessage.emit (events.js:215:7)
    at endReadableNT (_stream_readable.js:1183:12)
    at processTicksAndRejections (internal/process/task_queues.js:80:21)
(node:93171) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:93171) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Insufficient number of arguments or no entry found.
Alternatively, run 'webpack(-cli) --help' for usage info.

Hash: 7532bd6d1996f82068cf
Version: webpack 4.35.0
Time: 31ms
Built at: 10/16/2019 11:54:09 AM

ERROR in Entry module not found: Error: Can't resolve './our-project.js' in '/path/to/project/bin/build'

@danielgelling
Can you describe it in detail?

When we add a parameter to our spec, and reference this to this parameter in the path specification the generator gives the error listed above.

Part of our spec.json

{
    "paths": {
        "/users": {
            "get": {
                "summary": "Returns a list of users",
                "operationId": "someOperation",
"// ... and the other values usually in the spec": "",
                "parameters": [
                    {
                        "$ref": "#/parameters/someParam"
                    }
                ]
            }
        }
    },
    "parameters": {
        "someParam": {
            "name": "paramNameAsUsedByClient",
            "in": "query",
            "required": false,
            "description": "A description of this parameter",
            "type": "string" 
        }
    }
}

Note: I only added the part of the spec that is relevant to this issue.

If you need any more information, or access to our spec, just let me know :-)

The spec.json was not generated through the webapi?
Wirte on SwaggerEditor?

The spec.json file is generated and is Swagger 2.0 compliant.

The spec.json file is generated and is Swagger 2.0 compliant.

@haroldiedema
Emmm, typically, interface parameters are directly within the requested parameters, and references are rarely used.
For example, C#, Java, Nodejs, these languages generate spec.json like this:

"/products-test": {
      "get": {
        "tags": [
          "products-test"
        ],
        "summary": "Product Types",
        "description": "The Products endpoint returns information about the *Uber* products\noffered at a given location. The response includes the display name\nand other details about each product, and lists the products in the\nproper display order.\n",
        "parameters": [
          {
            "name": "latitude",
            "in": "query",
            "description": "Latitude component of location.",
            "required": true,
            "type": "number",
            "format": "double"
          },
          {
            "name": "longitude",
            "in": "query",
            "description": "Longitude component of location.",
            "required": true,
            "type": "number",
            "format": "double"
          }
        ],

This is only when we globally define the parameter in the spec, and then reference to it in the route. This is so you don't have to copy/paste the parameters for each route that use that same parameter.

Other parameters that are route specific are defined in the parameters in the route itself, like in your example. Which obviously does work (:

To put this use-case into context:

We have a specific parameter: q, which alot of our end-points use. We want to define this parameter once and re-use its definition in our operations.

The swagger-axios-codegen tool doesn't recognize the reference to another definition.

Currently, you can define the parameter in definition.
And then, welcome PR or wait for me of next version .

When we add a parameter to our spec, and reference this to this parameter in the path specification the generator gives the error listed above.

Part of our spec.json

{
    "paths": {
        "/users": {
            "get": {
                "summary": "Returns a list of users",
                "operationId": "someOperation",
"// ... and the other values usually in the spec": "",
                "parameters": [
                    {
                        "$ref": "#/parameters/someParam"
                    }
                ]
            }
        }
    },
    "parameters": {
        "someParam": {
            "name": "paramNameAsUsedByClient",
            "in": "query",
            "required": false,
            "description": "A description of this parameter",
            "type": "string" 
        }
    }
}

Note: I only added the part of the spec that is relevant to this issue.

If you need any more information, or access to our spec, just let me know :-)

@danielgelling
Hei, I think this spec.json not good.
The request parameters should be defined as a whole

@Manweill I just generated a new spec.json (only with two of our endpoints, one of which uses a parameter by reference, the other has its parameters specified in the route itself). You can find it here: https://gist.github.com/danielgelling/d816b09e76bf662723776ab07de1dd90

Using this generator it gives the error listed above, however when using the same spec with the official swagger-codegen-cli it just works as expected.

Indeed, using parameters as a reference is quite common in swagger generated docs. I was going to use your library for our new E2E framework, but i'm glad that i found this issue first :) Would love to see this feature in the future :)

@Manweill any ETA when this will be fixed? I want to use this code-gen in my project but i am blocked due to this error. Almost all of our swaggers has params as "$ref".

@usman087
How did you get your spec.json, through the backend service?

@usman087
How did you get your spec.json, through the backend service?
The swagger specs are generated as part of CI/CD pipeline during build and are published on our internal npm feed.

@usman087
I don‘t understand.
@fairking @arkraft can you help him?

@Manweill You support Models (definitions) being referenced via $ref in the schema. Parameters should behave the same way.

For example, alot of your end-points use the same parameter. It is useful for these parameters to be defined once in your application, so you can simply reference them instead of copy-pasting them repeatedly. The official swagger-codegen-cli supports this just fine.

I'm sorry, but I'm not able to explain this issue any more clearer.

@haroldiedema
So, Your spec.json is written manually, not automatically generated through backend services (such as c#, java, etc.)

@Manweill No our spec.json is not written manually, that'd be insanely stupid. It is being generated by the nelmio/NelmioApiDocBundle.

@danielgelling
I don't have time to make any changes to this issue at now, welcome PR.

There is a similar issue with using $ref for request bodies
...
"requestBody": {
"$ref": "#/components/requestBodies/UserRole"
},

(node:19348) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'multipart/form-data' of undefined
at Object.getContentType (C:\Repos\AddoCloudPlatform\Site\node_modules\swagger-axios-codegen\dist\requestCodegen\getContentType.js:10:144)

Ref is not resolved and the code tries to look for _a.content['multipart/form-data'] which of course fails.

@mikaelnilsson77
What is your version of openapi and backend service?

"openapi": "3.0.2",

We were able to work around this issue using https://github.com/APIDevTools/json-schema-ref-parser and passing the dereferenced swagger definition to codegen.

Note: We published a more recent version of json-schema-ref-parser that ships the feature to exclude certain paths from being dereferenced. No clue why they do not publish it upstream.

Example:

import { codegen } from "swagger-axios-codegen";
import * as $RefParser from "@nesto-software/json-schema-ref-parser";
import * as fs from "fs";
import * as path from "path";

// eslint-disable-next-line
$RefParser.dereference(require("../resources/swagger.json"), {
        dereference: {
            circular: "ignore",

            // note: we must not dereference responses, otherwise no dtos are created
            //       this is the most hilarious workaround ever
            //       now we basically inlining parameters etc. but not response-related paths
            excludedPathMatcher: path => path.includes("/get/responses/") || path.includes("/post/responses/") || path.includes("/properties/value/"),
        },
    },
    (err, fixedSchema) => {
        if (err) {
            console.error(err);
            return;
        } else {
            // for debugging purposes: save the dereferenced schema
            fs.writeFileSync(path.join(__dirname, "../resources/swagger-dereferenced.json"), JSON.stringify(fixedSchema, null, 2));

            codegen({
                methodNameMode: "operationId",
                source: fixedSchema,
                outputDir: "./lib/api/",
                fileName: "services.ts",
                useStaticMethod: false,
                useHeaderParameters: false,
                modelMode: "class",
                useClassTransformer: true
            });
        }
    },
);

@MartinLoeper Emmm.... and then?

Uhm, let me elaborate:

I was working on a customer's OData API that provided a swagger documentation (OpenAPI 3.0.0) and I wanted to auto-generate a typescript client using this package. Unfortunately, it produced incorrect parameters as the swagger spec used 'ref' for the common OData parameters such as: "$ref": "#/components/parameters/expandParam" (similar to thread opener's example above).

To make it work, I decided that it is easier to change the input for swagger-axios-codegen than the package's implementation.
I posted the workaround above for anyone that runs into a similar problem and just wants to make this package work for specs that use parameter references.

I guess, in the long run we could also integrate json-schema-ref-parser into this package to support the kind of references that the thread opener described.