devexperts / swagger-codegen-ts

Typesafe Swagger API generator for TypeScript
Mozilla Public License 2.0
80 stars 16 forks source link

Add possibility to not use TypeScript Date type but keep raw iso date strings instead #118

Closed bhoudu closed 4 years ago

bhoudu commented 4 years ago

It would be great to have an option to leave the raw value of iso strings in generated types.

For instance, iso dates can be defined with microtime or nanotime. Javascript dates can only keep milliseconds value at best, so we could just loose precision. In most cases it doesn't matter, but if we are dealing with timestamps and precision matters, it becomes a problem.

raveclassic commented 4 years ago

Hi, @bhoudu, thanks for the issue. When working with dates in JavaScript the only option we have is standard Date type which only supports milliseconds. There's no way to increase precision.

So, If you need a string-based date representation and you control the spec, you can just omit format attribute from the string type in the spec.

If you don't control the spec, I'm afraid there's no option at the moment. We don't want to add any configs because the issue it too specific and adding configuration for such cases here and there will quickly get out of control. Ideally we'd want to introduce some kind of plugin system to the codegen, however there are no estimations for that.

Anyway, thanks for the interest and the issue. If you need any help, please let me know.

bhoudu commented 4 years ago

Thanks for your quick reply. I guess I will have to build a workaround. Without control on the spec, I can just generate another spec from the original one and scrapping the format attribute, not beautiful but it will work.

raveclassic commented 4 years ago

Actually we could support arbitrary parsers in config so that instead of maintaining the whole infrastructure you could just hack into spec content like it's done for sketch zip-files: https://github.com/devexperts/swagger-codegen-ts/blob/7bae46deeb79684683cf49c10b983ada36fc499f/src/index.ts#L42-L44 I'll create an issue for that

raveclassic commented 4 years ago

Even more you could just override the decoder so that after parsing OpenAPI3 (or similar) another run could be done stripping format: date from the spec.

raveclassic commented 4 years ago

smth like this:

export const StringDatesCodec = OpenapiObjectCodec.pipe(
  new Type<OpenapiObject, OpenapiObject>(
    'StringDates',
    OpenapiObjectCodec.is,
    (i, c) => {
      return pipe(
        OpenapiObjectCodec.validate(i, c),
        either.map(openapi => ({
          ...openapi,
          components: pipe(
            openapi.components,
            option.map(components => ({
              ...components,
              schemas: pipe(
                components.schemas,
                option.map(
                  record.map(schema => {
                    if (
                      PrimitiveSchemaObjectCodec.is(schema) &&
                      schema.type === 'string' &&
                      isSome(schema.format) &&
                      schema.format.value === 'date-time'
                    ) {
                      return {
                        ...schema,
                        format: option.none,
                      };
                    }
                    // handle all cases!
                    return schema;
                  }),
                ),
              ),
            })),
          ),
        })),
      );
    },
    identity,
  ),
);
raveclassic commented 4 years ago

Alternatively you can use monocle-ts (or any other lenses library) to avoid messing with deep updates:

const isDate = (schema: PrimitiveSchemaObject) =>
    schema.type === 'string' && isSome(schema.format) && schema.format.value === 'date-time';
const removeDates = Optional.fromOptionProp<OpenapiObject>()('components')
    .composeOptional(Optional.fromOptionProp<ComponentsObject>()('schemas'))
    .composeTraversal(fromTraversable(record.record)())
    .filter(PrimitiveSchemaObjectCodec.is)
    .filter(isDate)
    .composeLens(Lens.fromProp<PrimitiveSchemaObject>()('format'))
    .set(option.option.zero<string>());