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

Support JsonExtensionData in TypeScript clients #1250

Open
Sharparam opened this issue Sep 14, 2020 · 2 comments
Open

Support JsonExtensionData in TypeScript clients #1250

Sharparam opened this issue Sep 14, 2020 · 2 comments

Comments

@Sharparam
Copy link
Contributor

See also: RicoSuter/NSwag#1541 (comment)

Currently, the generated C# clients support the JsonExtensionData attribute, but in the TypeScript clients it's simply ignored.

We discovered this when troubleshooting why data was missing in ProblemDetails in ASP.NET Core.

From a bit of debugging, it seems the following might be part of a solution:

Add the missing properties to ClassTemplateModel in NJsonSchema.CodeGeneration.TypeScript:

public bool HasAdditionalPropertiesType =>
    !_schema.IsDictionary &&
    !_schema.ActualTypeSchema.IsDictionary &&
    !_schema.IsArray &&
    !_schema.ActualTypeSchema.IsArray &&
    (_schema.ActualTypeSchema.AllowAdditionalProperties ||
    _schema.ActualTypeSchema.AdditionalPropertiesSchema != null);

public string AdditionalPropertiesType => HasAdditionalPropertiesType ? "any" : null;

Add code to the Liquid templates for property:

{% if HasAdditionalPropertiesType %}
    additionalProperties?: { [key: string]: {{ AdditionalPropertiesType }}; } | undefined;
{% endif %}

And code in the init method to populate it:

{% if HasAdditionalPropertiesType %}
            this.additionalProperties = {} as {{ AdditionalPropertiesType }};
            for (const [key, value] of Object.entries(_data)) {
                if (!this.hasOwnProperty(key))
                    this.additionalProperties![key] = value;
            }
{% endif %}

There's probably a bunch of things here to take into consideration that I have missed from my basic glance over the various code generators. In my limited testing at least it seems to produce the expected results.

Additionally, System.Text.Json's JsonExtensionData attribute would need additional support. I think that would be part of the separate issues about supporting System.Text.Json though?

@cbalisky
Copy link
Contributor

The TypeScript Liquid template has the logic already. It's currently only used when HasIndexerProperty is true. I've created a PR that adds AllowAdditionalProperties and AdditionalPropertiesSchema to the check #1412

@AlbertoSadoc
Copy link

Hello @Sharparam, did you manage to get this working? I am struggling to deserialize a ProblemDetails.

Using nswag v13.18.2
nswag openapi2tsclient /runtime:Net60 /input:swagger.json /output:src/api/client.generated.ts /template:axios /operationGenerationMode:MultipleClientsFromPathSegments /generateTypeCheckFunctions:true

and get the following:

export class ProblemDetails implements IProblemDetails {
  type?: string | undefined;
  title?: string | undefined;
  status?: number | undefined;
  detail?: string | undefined;
  instance?: string | undefined;
  readonly extensions?: { [key: string]: any };

  constructor(data?: IProblemDetails) {
    if (data) {
      for (var property in data) {
        if (data.hasOwnProperty(property)) (<any>this)[property] = (<any>data)[property];
      }
    }
  }

  init(_data?: any) {
    if (_data) {
      this.type = _data["Type"];
      this.title = _data["Title"];
      this.status = _data["Status"];
      this.detail = _data["Detail"];
      this.instance = _data["Instance"];
      if (_data["Extensions"]) {
        (<any>this).extensions = {} as any;
        for (let key in _data["Extensions"]) {
          if (_data["Extensions"].hasOwnProperty(key)) (<any>(<any>this).extensions)![key] = _data["Extensions"][key];
        }
      }
    }
  }

  static fromJS(data: any): ProblemDetails {
    data = typeof data === "object" ? data : {};
    let result = new ProblemDetails();
    result.init(data);
    return result;
  }

  toJSON(data?: any) {
    data = typeof data === "object" ? data : {};
    data["Type"] = this.type;
    data["Title"] = this.title;
    data["Status"] = this.status;
    data["Detail"] = this.detail;
    data["Instance"] = this.instance;
    if (this.extensions) {
      data["Extensions"] = {};
      for (let key in this.extensions) {
        if (this.extensions.hasOwnProperty(key)) (<any>data["Extensions"])[key] = (<any>this.extensions)[key];
      }
    }
    return data;
  }
}

export interface IProblemDetails {
  type?: string | undefined;
  title?: string | undefined;
  status?: number | undefined;
  detail?: string | undefined;
  instance?: string | undefined;
  extensions?: { [key: string]: any };
}

so the indexer isn't anywhere but the extensions property

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

No branches or pull requests

3 participants