Skip to content

Commit f48451d

Browse files
authored
feat: support date-time format (#100)
Added io-ts codec that supports date ignoring time
1 parent ca9d832 commit f48451d

File tree

7 files changed

+70
-12
lines changed

7 files changed

+70
-12
lines changed

src/language/typescript/2.0/serializers/swagger-object.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { clientFile } from '../../common/bundled/client';
1212
import { serializeParametersDefinitionsObject } from './parameters-definitions-object';
1313
import { combineReader } from '@devexperts/utils/dist/adt/reader.utils';
1414
import { serializeResponsesDefinitionsObject } from './responses-definitions-object';
15+
import { utilsFile } from '../../common/bundled/utils';
1516

1617
const definitionsRef = fromString('#/definitions');
1718
const parametersRef = fromString('#/parameters');
@@ -52,8 +53,8 @@ export const serializeSwaggerObject = combineReader(
5253
pathsRef,
5354
either.chain(from => serializePaths(from, swaggerObject.paths)),
5455
);
55-
return combineEither(additional, paths, clientFile, (additional, paths, clientFile) =>
56-
directory(name, [clientFile, ...additional, paths]),
56+
return combineEither(additional, paths, clientFile, utilsFile, (additional, paths, clientFile, utilsFile) =>
57+
directory(name, [clientFile, ...additional, utilsFile, paths]),
5758
);
5859
},
5960
);

src/language/typescript/3.0/serializers/document.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { OpenapiObject } from '../../../../schema/3.0/openapi-object';
1212
import { pathsRef } from '../../common/utils';
1313
import { clientFile } from '../../common/bundled/client';
1414
import { openapi3utilsFile } from '../bundled/openapi-3-utils';
15+
import { utilsFile } from '../../common/bundled/utils';
1516

1617
export const serializeDocument = combineReader(
1718
serializeComponentsObject,
@@ -36,9 +37,10 @@ export const serializeDocument = combineReader(
3637
paths,
3738
additional,
3839
clientFile,
40+
utilsFile,
3941
openapi3utilsFile,
40-
(paths, additional, clientFile, openapi3utilsFile) =>
41-
directory(name, [paths, ...additional, clientFile, openapi3utilsFile]),
42+
(paths, additional, clientFile, utilsFile, openapi3utilsFile) =>
43+
directory(name, [paths, ...additional, clientFile, utilsFile, openapi3utilsFile]),
4244
);
4345
},
4446
);

src/language/typescript/asyncapi-2.0.0/serializers/asyncapi-object.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { array, either, option } from 'fp-ts';
99
import { combineEither, sequenceEither } from '@devexperts/utils/dist/adt/either.utils';
1010
import { serializeChannelsObject } from './channels-object';
1111
import { clientFile } from '../../common/bundled/client';
12+
import { utilsFile } from '../../common/bundled/utils';
1213

1314
export const serializeAsyncAPIObject = combineReader(
1415
serializeComponentsObject,
@@ -29,8 +30,13 @@ export const serializeAsyncAPIObject = combineReader(
2930
either.chain(from => serializeChannelsObject(from, asyncAPIObject.channels)),
3031
either.map(content => directory('channels', [content])),
3132
);
32-
return combineEither(channels, additional, clientFile, (channels, additional, clientFile) =>
33-
directory(name, [channels, clientFile, ...additional]),
33+
return combineEither(
34+
channels,
35+
additional,
36+
clientFile,
37+
utilsFile,
38+
(channels, additional, clientFile, utilsFile) =>
39+
directory(name, [channels, clientFile, ...additional, utilsFile]),
3440
);
3541
},
3642
);

src/language/typescript/common/bundled/utils.ts

+32
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,38 @@ import { fromRef } from '../../../../utils/fs';
66
export const utilsRef = fromString('#/utils/utils');
77

88
const utils = `
9+
import { either } from 'fp-ts/lib/Either';
10+
import { Type, failure, success, string as tstring } from 'io-ts';
11+
12+
const getISOTimezoneOffsetString = (offsetMinutes: number): string => {
13+
if (offsetMinutes === 0) {
14+
return 'Z';
15+
}
16+
17+
const absoluteOffsetMinutes = Math.abs(offsetMinutes);
18+
const offsetHours = absoluteOffsetMinutes / 60;
19+
const offsetRestMinutes = absoluteOffsetMinutes % 60;
20+
21+
return \`\${offsetMinutes > 0 ? '-' : '+'}\${offsetHours
22+
.toString()
23+
.padStart(2, '0')}:\${offsetRestMinutes.toString().padStart(2, '0')}\`;
24+
};
25+
26+
export const DateFromISODateStringIO = new Type<Date, string, unknown>(
27+
'DateFromISODateString',
28+
(u): u is Date => u instanceof Date,
29+
(u, c) =>
30+
either.chain(tstring.validate(u, c), s => {
31+
const offset = new Date().getTimezoneOffset();
32+
const d = new Date(\`\${s}T00:00:00\${getISOTimezoneOffsetString(offset)}\`);
33+
return isNaN(d.getTime()) ? failure(u, c) : success(d);
34+
}),
35+
a =>
36+
\`\${a.getFullYear()}-\${(a.getMonth() + 1).toString().padStart(2, '0')}-\${a
37+
.getDate()
38+
.toString()
39+
.padStart(2, '0')}\`,
40+
);
941
`;
1042

1143
export const utilsFile = pipe(

src/language/typescript/common/data/serialized-type.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -73,20 +73,28 @@ export const SERIALIZED_BOOLEAN_TYPE = serializedType(
7373
);
7474
export const SERIALIZED_NUMBER_TYPE = serializedType('number', 'number', [serializedDependency('number', 'io-ts')], []);
7575
export const SERIALIZED_INTEGER_TYPE = serializedType('Int', 'Int', [serializedDependency('Int', 'io-ts')], []);
76-
export const SERIALIZED_DATE_TYPE = serializedType(
76+
export const SERIALIZED_DATETIME_TYPE = serializedType(
7777
'Date',
7878
'DateFromISOString',
7979
[serializedDependency('DateFromISOString', 'io-ts-types/lib/DateFromISOString')],
8080
[],
8181
);
82+
export const SERIALIZED_DATE_TYPE = serializedType(
83+
'Date',
84+
'DateFromISODateStringIO',
85+
[serializedDependency('DateFromISODateStringIO', '../utils/utils')],
86+
[],
87+
);
8288
export const SERIALIZED_STRING_TYPE = serializedType('string', 'string', [serializedDependency('string', 'io-ts')], []);
8389
export const getSerializedStringType = (format: Option<string>): SerializedType => {
8490
return pipe(
8591
format,
8692
option.chain(format => {
8793
// https://xml2rfc.tools.ietf.org/public/rfc/html/rfc3339.html#anchor14
8894
switch (format) {
89-
case 'date-time':
95+
case 'date-time': {
96+
return some(SERIALIZED_DATETIME_TYPE);
97+
}
9098
case 'date': {
9199
return some(SERIALIZED_DATE_TYPE);
92100
}

test/specs/2.0/json/common.json

+10-4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
"format": "int32"
2323
},
2424
"shipDate": {
25+
"type": "string",
26+
"format": "date"
27+
},
28+
"lastUpdate": {
2529
"type": "string",
2630
"format": "date-time"
2731
},
@@ -34,10 +38,12 @@
3438
"delivered"
3539
]
3640
},
37-
"state" : {
38-
"type" : "string",
39-
"description" : "order state",
40-
"enum" : [ "ACTIVE" ]
41+
"state": {
42+
"type": "string",
43+
"description": "order state",
44+
"enum": [
45+
"ACTIVE"
46+
]
4147
},
4248
"complete": {
4349
"type": "boolean",

test/specs/2.0/yaml/common.yml

+3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ definitions:
1818
type: integer
1919
format: int32
2020
shipDate:
21+
type: string
22+
format: date
23+
lastUpdate:
2124
type: string
2225
format: date-time
2326
status:

0 commit comments

Comments
 (0)