swagger-codegen icon indicating copy to clipboard operation
swagger-codegen copied to clipboard

fix typescript-node object deserialization for complex array types

Open poojyum opened this issue 4 years ago • 2 comments

PR checklist

  • [X] Read the contribution guidelines.
  • [X] Ran the shell script under ./bin/ to update Petstore sample so that CIs can verify the change. (For instance, only need to run ./bin/{LANG}-petstore.sh and ./bin/security/{LANG}-petstore.sh if updating the {LANG} (e.g. php, ruby, python, etc) code generator or {LANG} client's mustache templates). Windows batch files can be found in .\bin\windows\.
  • [X] Filed the PR against the correct branch: 3.0.0 branch for changes related to OpenAPI spec 3.0. Default: master.
  • [X] Copied the technical committee to review the pull request if your PR is targeting a particular programming language.

Description of the PR

This PR fixes the deserialization problem in typescript-node with complex array types. Fixes [typescript-node] Runtime error when using array definitions;

Sample specs to validate the fix:

swagger: "2.0"
info:
  title: Sample spec
  version: '1.0.0'
basePath: /v1
schemes:
- http
- https
consumes:
- application/json
produces:
- application/json
paths:
  /environments/:
    get:
      responses:
        200:
          $ref: '#/responses/EnvironmentList'
responses:
  EnvironmentList:
    description: "List of Environment Details"
    schema:
      $ref: '#/definitions/EnvironmentDetailsList'
definitions:
  EnvironmentDetailsList:
    description: "List of Environment Details"
    type: object
    properties:
      environments:
        type: array
        items:
          $ref: '#/definitions/Environment'
      links:
        $ref: '#/definitions/Links'
  Environment:
    type: object
    properties:
      name:
        type: string
  Link:
    type: object
    properties:
      href:
        type: string
      method:
        type: string
      rel:
        type: string
  Links:
    type: array
    items:
      $ref: '#/definitions/Link'

Deserialization test code:

import { ObjectSerializer, EnvironmentDetailsList } from './api';

const data = {
    "environments": [
        {
            "name": "test-environment",
        }
    ],
    "links": [
        {
            "href": "http://127.0.0.1:8080/v1/environments/?page=1&page_size=100&userid=owner",
            "method": "GET",
            "rel": "self"
        }
    ]
};

const output = ObjectSerializer.deserialize(data, "EnvironmentDetailsList");
console.log(output)

Before fix:

The generator produces Links model class and it is referenced in EnvironmentDetailsList. While proposed solution in the #10000 to add attributeTypeMap and getAttributeTypeMap to the array model classes would fix the error TypeError: typeMap[type].getAttributeTypeMap is not a function, it does not fix the deserialization. The reason is that the empty attributeTypeMap will skip deserializing that array object links as shown in the sample code above.

export class EnvironmentDetailsList {
    'environments'?: Array<Environment>;
    'links'?: Links;

    static discriminator: string | undefined = undefined;

    static attributeTypeMap: Array<{name: string, baseName: string, type: string}> = [
        {
            "name": "environments",
            "baseName": "environments",
            "type": "Array<Environment>"
        },
        {
            "name": "links",
            "baseName": "links",
            "type": "Links"
        }    ];

    static getAttributeTypeMap() {
        return EnvironmentDetailsList.attributeTypeMap;
    }
}

export class Links extends Array<Link> {
    static discriminator: string | undefined = undefined;
}

After fix:

It does not produce the intermediate model Links, instead any references of Links will be replaced with Array<Link>. The change will fix the runtime error and also the deserialization of those complex array types. This type mapping is very similar to how the environments is referenced as Array<Environment>

export class EnvironmentDetailsList {
    'environments'?: Array<Environment>;
    'links'?: Array<Link>;

    static discriminator: string | undefined = undefined;

    static attributeTypeMap: Array<{name: string, baseName: string, type: string}> = [
        {
            "name": "environments",
            "baseName": "environments",
            "type": "Array<Environment>"
        },
        {
            "name": "links",
            "baseName": "links",
            "type": "Array<Link>"
        }    ];

    static getAttributeTypeMap() {
        return EnvironmentDetailsList.attributeTypeMap;
    }
}

Tested the fix with the sample spec provided in the issue #10000 as well.

Before fix: It produces a model LocationGetResponse. It is also referenced in the operation response.

export class LocationGetResponse extends Array<LocationGetResponse> {
}

public locationGet (input: string, options: any = {}) : Promise<{ response: http.ClientResponse; body: LocationGetResponse;  }> {
....
   body = ObjectSerializer.deserialize(body, "LocationGetResponse");
}

After fix:

It does not produce the model LocationGetResponse. But it will replace any references of LocationGetResponse with Array<LocationGetResponseInner>.

public locationGet (input: string, options: any = {}) : Promise<{ response: http.ClientResponse; body: Array<LocationGetResponseInner>;  }> {
...
    body = ObjectSerializer.deserialize(body, "Array<LocationGetResponseInner>");
}

Test code:

import { ObjectSerializer, LocationGetResponseInner } from './api';

const data = [
    {
        "id": "1",
        "text": "text 1"
    },
    {
        "id": "2",
        "text": "text 2"
    }
];
const output = ObjectSerializer.deserialize(data, "Array<LocationGetResponseInner>");
console.log(output)

@CodeNinjai

poojyum avatar Jan 20 '21 01:01 poojyum

@HugoMario Could you please PR?

poojyum avatar Jan 20 '21 16:01 poojyum

bump @HugoMario @poojyum

Morriz avatar Jan 20 '22 20:01 Morriz