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

[typescript-angular] Support custom types (serialization/deserialization/imports) (momentjs) #1875

Open
codemasterover9000 opened this issue Jan 10, 2019 · 10 comments

Comments

@codemasterover9000
Copy link

Hi,

I would like to be able to support custom types. This would mean:

  • Parameters in services are serialized from the custom type
  • Members of models are serialized/deserialized on receive/send.
  • Imports for the custom types are included where required.

My concrete case is that I want to use momentjs for date/time types in models and as parameters.

What I tried to do was:

But even then there seems to be no support for any kind pluggable serialization/deserialization. Maybe this could be achieved using custom providers? Maybe this is something to consider for #802.

I can imagine this is quite a common use case but could not find much about how to handle this.

My question is basically how would I achieve having working generated code where every date formatted string is replaced with a Moment type (Other than just writing my own templates).

Thanks

@auto-labeler
Copy link

auto-labeler bot commented Jan 10, 2019

👍 Thanks for opening this issue!
🏷 I have applied any labels matching special text in your issue.

The team will review the labels and make any necessary changes.

@batousik
Copy link

This sounds absolutely reasonable, take a look at #4170.
Typescript client must have dates and date-time as objects. Moment.js is one of the best libs out there.
The generator itself must be upgraded to support this and so ar the mustache templates.
openapi-generator/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java
Also take a look at #6602

@flaute
Copy link

flaute commented Jul 27, 2021

I would like to use UUIDs for the ids of my entities and luxon for any timestamps.

Using the default openapi generator for typescript-angular clients both of these fields are generated as string types in the models.

Example of current generated model:

export interface Event {
id: string;
startAt: string;
endAt: string;
}

Example of generated model that I would like to have:

import { v4 as UUID } from 'uuid'
import { DateTime } from 'luxon';
export interface Event {
id: UUID;
startAt: DateTime;
endAt: DateTime;
}

Are there any further ideas or solutions for serialization and deserialization of non-primitive or non-default-JS types like UUID or DateTime?

@sarumont
Copy link
Contributor

We have a fully-custom generator to handle this case (along with some other customizations). Do any of the other language generators support this use-case (to use as a reference point)? I'd love to upstream as much as I can to help others (and make it easier on myself to maintain).

@KillDozerX2
Copy link
Contributor

@sarumont Would you be able to share that?

@sarumont
Copy link
Contributor

@KillDozerX2 Unfortunately, not at this point in time. It's somewhat proprietary but mostly not ready for a larger audience than some other internal teams, and I don't have the bandwidth (or the priority) to make it better right now... :)

@KillDozerX2
Copy link
Contributor

@fultonm I got Datetime working by modifying the template, have you tried that yet?

@fultonm
Copy link
Contributor

fultonm commented Oct 7, 2021

Maybe you meant @flaute I am not a part of this haha.

@Nindouja
Copy link

@flaute I managed to achieve kindof what you want for Luxon support, but it works only with the typescript axios generator** (or any other axios one). You may not be able to use that; but I found this issue while googling so I guess it's a good place to share my solution. What I do is take advantage of the axios interceptors features.

First I edited the command I run to generate the code to change the mustache templates and also change Date types to luxon's type; here is my command:

openapi-generator-cli generate -g typescript-axios -i http://localhost:8420/api-docs -o src/lpr-api -t=templates --type-mappings=DateTime=DateTime

What you are interested in are the last two arguments:

  • -t out is the location of custom templates.
  • --type-mappings is what responsible for the change of date types in the generated dto. This make the dto interface have the correct type; but at run type we will still have strings because the generator have no idea how to convert them

Second I needed to tweak the generated code to make the Datetime type of luxon resolvable and intercepting response to convert string dates to luxon's Datetime, this is done by retrieving the original mustache template with the following command: openapi-generator-cli author template -g typescript-axios. Then I edited the api.mustache template to:

  1. Add an import for Luxon's Datetime type
  2. Registered an axios interceptor to parse my request results and convert them if needed.

I'll give you my current mustache template (with unchanged lines removed for clarity) but I'm almost certain there are bugs about timezones probably and also it certainly improvable A LOT.

/* tslint:disable */
/* eslint-disable */
{{>licenseInfo}}

{{^withSeparateModelsAndApi}}
import {DateTime} from "luxon";
...
import { BASE_PATH, COLLECTION_FORMATS, BaseAPI, RequiredError } from './base';

// Parse dates as Luxon dates
const regexDate = /^\d{4}-\d{2}-\d{2}$/;
const regexDatetime = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}[+-]\d{2}:\d{2}$/;
function parseDateInArray(data: Array<any>): Array<any> {
    return data.map((item) => genericParseDate(item));
  }
  function parseDateInObject(data: Object): Object {
    for (const [key, value] of Object.entries(data)) {
      data[key] = genericParseDate(data[key]);
    }
    return data;
  }
  function genericParseDate(data?: any): any {
    if (data && Array.isArray(data)) {
      data = parseDateInArray(data);
    } else if (data && typeof data === "object") {
      data = parseDateInObject(data);
    } else if (data && typeof data === "string") {
      if (data.match(regexDatetime)) {
        return DateTime.fromISO(data);
      } else if (data.match(regexDate)) {
        let s = data + "T00:00:00.000";
        return DateTime.fromISO(s);
      }
    }
    return data;
  }
  globalAxios.interceptors.response.use(
    function (response) {
      response.data = genericParseDate(response.data);
      return response;
    },
    function (error) {
      return Promise.reject(error);
    },
  );

{{#models}}
...
{{/withSeparateModelsAndApi}}

@lasselindqvist
Copy link

What @BancarelValentin has provided there should be quite easily doable for typescript-angular as well. You can use something like https://angular.io/guide/http-interceptor-use-cases#custom-json-parsing and https://stackoverflow.com/a/29971466 to convert the data to anything you like: Date, moment or Luxon objects.

This has the effect that any real text fields that contain date-looking data, will be parsed when they should not.
If you control the API definition, you might want to check also the field key when parsing, and enforce that dates end in "date" and date-times end in "dateTime" or similar rule.

To be implemented in the generator, the solution would need to track the objects and fields and actually "know" which fields are dates and date-times, but implementing that is much more work.

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

No branches or pull requests

8 participants