diff --git a/Directory.Build.props b/Directory.Build.props index e66b93c..ba3e246 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 2.7.0 + 2.8.0 11.0 enable enable @@ -33,7 +33,7 @@ - 2.10.1 + 2.11.0 diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 8f1f898..93fb9ec 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -2,6 +2,10 @@ These are the NuGet package releases. See also [npm Release Notes](ReleaseNotesNpm.md). +## 2.8.0 + +* Support `datetime`. + ## 2.7.0 * Support `extern` data and enum types. diff --git a/conformance/ConformanceApi.fsd b/conformance/ConformanceApi.fsd index e51d494..ff35857 100644 --- a/conformance/ConformanceApi.fsd +++ b/conformance/ConformanceApi.fsd @@ -149,14 +149,13 @@ service ConformanceApi int32: int32; int64: int64; decimal: decimal; - - [validate] - enum: Answer; + [validate] enum: Answer; + datetime: datetime; }: { } - [http(method: GET, path: "/checkPath/{string}/{boolean}/{double}/{int32}/{int64}/{decimal}/{enum}")] + [http(method: GET, path: "/checkPath/{string}/{boolean}/{double}/{int32}/{int64}/{decimal}/{enum}/{datetime}")] method checkPath { string: string; @@ -165,9 +164,8 @@ service ConformanceApi int32: int32; int64: int64; decimal: decimal; - - [validate] - enum: Answer; + [validate] enum: Answer; + datetime: datetime; }: { } @@ -182,6 +180,7 @@ service ConformanceApi [http(from: header)] int64: int64; [http(from: header)] decimal: decimal; [http(from: header)] enum: Answer; + [http(from: header)] datetime: datetime; }: { [http(from: header)] string: string; @@ -191,6 +190,7 @@ service ConformanceApi [http(from: header)] int64: int64; [http(from: header)] decimal: decimal; [http(from: header)] enum: Answer; + [http(from: header)] datetime: datetime; } [http(path: "/mixed/{path}")] @@ -261,6 +261,7 @@ service ConformanceApi int32: int32; int64: int64; decimal: decimal; + datetime: datetime; bytes: bytes; object: object; error: error; @@ -280,6 +281,7 @@ service ConformanceApi int32: int32[]; int64: int64[]; decimal: decimal[]; + datetime: datetime[]; bytes: bytes[]; object: object[]; error: error[]; @@ -299,6 +301,7 @@ service ConformanceApi int32: map; int64: map; decimal: map; + datetime: map; bytes: map; object: map; error: map; @@ -318,6 +321,7 @@ service ConformanceApi int32: result; int64: result; decimal: result; + datetime: result; bytes: result; object: result; error: result; @@ -337,6 +341,7 @@ service ConformanceApi int32: nullable; int64: nullable; decimal: nullable; + datetime: nullable; bytes: nullable; object: nullable; error: nullable; diff --git a/conformance/ConformanceTests.json b/conformance/ConformanceTests.json index cb036f7..405be38 100644 --- a/conformance/ConformanceTests.json +++ b/conformance/ConformanceTests.json @@ -10,7 +10,8 @@ "int32": 42, "int64": 12345678910, "decimal": 1.5, - "enum": "yes" + "enum": "yes", + "datetime": "2001-02-03T04:05:06Z" }, "response": {} }, @@ -24,7 +25,8 @@ "int32": 42, "int64": 12345678910, "decimal": 1.5, - "enum": "yes" + "enum": "yes", + "datetime": "2001-02-03T04:05:06Z" }, "response": {} }, @@ -205,6 +207,7 @@ "int32": [42, 24], "int64": [12345678910, -987654321], "decimal": [1.5, 6.875], + "datetime": ["2001-02-03T04:05:06Z", "2023-08-10T19:41:21Z"], "bytes": ["aGk=", ""], "object": [{ "": null, " ": [null] }, {}], "error": [{ "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } }], @@ -227,6 +230,7 @@ "int32": [42, 24], "int64": [12345678910, -987654321], "decimal": [1.5, 6.875], + "datetime": ["2001-02-03T04:05:06Z", "2023-08-10T19:41:21Z"], "bytes": ["aGk=", ""], "object": [{ "": null, " ": [null] }, {}], "error": [{ "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } }], @@ -252,6 +256,7 @@ "int32": 42, "int64": 12345678910, "decimal": 1.5, + "datetime": "2001-02-03T04:05:06Z", "bytes": "aGk=", "object": { "": null, " ": [null] }, "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } }, @@ -267,6 +272,7 @@ "int32": 42, "int64": 12345678910, "decimal": 1.5, + "datetime": "2001-02-03T04:05:06Z", "bytes": "aGk=", "object": { "": null, " ": [null] }, "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } }, @@ -285,7 +291,8 @@ "int32": 42, "int64": 12345678910, "decimal": 1.5, - "enum": "yes" + "enum": "yes", + "datetime": "2001-02-03T04:05:06Z" }, "response": { "string": "string", @@ -294,7 +301,8 @@ "int32": 42, "int64": 12345678910, "decimal": 1.5, - "enum": "yes" + "enum": "yes", + "datetime": "2001-02-03T04:05:06Z" } }, { @@ -315,6 +323,7 @@ "int32": { "": 42, " ": 24 }, "int64": { "": 12345678910, " ": -987654321 }, "decimal": { "": 1.5, " ": 6.875 }, + "datetime": { "": "2001-02-03T04:05:06Z", " ": "2023-08-10T19:41:21Z" }, "bytes": { "": "aGk=", " ": "" }, "object": { "": { "": null, " ": [null] }, " ": {} }, "error": { "": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } }, " ": {} }, @@ -336,6 +345,7 @@ "int32": { "": 42, " ": 24 }, "int64": { "": 12345678910, " ": -987654321 }, "decimal": { "": 1.5, " ": 6.875 }, + "datetime": { "": "2001-02-03T04:05:06Z", " ": "2023-08-10T19:41:21Z" }, "bytes": { "": "aGk=", " ": "" }, "object": { "": { "": null, " ": [null] }, " ": {} }, "error": { "": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } }, " ": {} }, @@ -361,6 +371,7 @@ "int32": 42, "int64": 12345678910, "decimal": 1.5, + "datetime": "2001-02-03T04:05:06Z", "bytes": "aGk=", "object": { "": null, " ": [null] }, "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } }, @@ -381,6 +392,7 @@ "int32": 42, "int64": 12345678910, "decimal": 1.5, + "datetime": "2001-02-03T04:05:06Z", "bytes": "aGk=", "object": { "": null, " ": [null] }, "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } }, @@ -421,6 +433,7 @@ "int32": null, "int64": null, "decimal": null, + "datetime": null, "bytes": null, "object": null, "error": null, @@ -441,6 +454,7 @@ "int32": null, "int64": null, "decimal": null, + "datetime": null, "bytes": null, "object": null, "error": null, @@ -465,6 +479,7 @@ "int32": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, "int64": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, "decimal": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "datetime": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, "bytes": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, "object": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, "error": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, @@ -485,6 +500,7 @@ "int32": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, "int64": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, "decimal": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "datetime": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, "bytes": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, "object": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, "error": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, @@ -509,6 +525,7 @@ "int32": { "value": 42 }, "int64": { "value": 12345678910 }, "decimal": { "value": 1.5 }, + "datetime": { "value": "2001-02-03T04:05:06Z" }, "bytes": { "value": "aGk=" }, "object": { "value": { "": null, " ": [null] } }, "error": { "value": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, @@ -530,6 +547,7 @@ "int32": { "value": 42 }, "int64": { "value": 12345678910 }, "decimal": { "value": 1.5 }, + "datetime": { "value": "2001-02-03T04:05:06Z" }, "bytes": { "value": "aGk=" }, "object": { "value": { "": null, " ": [null] } }, "error": { "value": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, @@ -767,7 +785,7 @@ "method": "checkPath", "httpRequest": { "method": "GET", - "path": "/CHECKPATH/pathCase/true/1.5/3/4/0.625/yes" + "path": "/CHECKPATH/pathCase/true/1.5/3/4/0.625/yes/2001-02-03T04:05:06Z" }, "request": { "string": "pathCase", @@ -776,7 +794,8 @@ "int32": 3, "int64": 4, "decimal": 0.625, - "enum": "yes" + "enum": "yes", + "datetime": "2001-02-03T04:05:06Z" }, "response": {} }, @@ -785,7 +804,7 @@ "method": "checkPath", "httpRequest": { "method": "GET", - "path": "/checkPath/booleanCase/TRUE/1.5/3/4/0.625/yes" + "path": "/checkPath/booleanCase/TRUE/1.5/3/4/0.625/yes/2001-02-03T04:05:06Z" }, "request": { "string": "booleanCase", @@ -794,7 +813,8 @@ "int32": 3, "int64": 4, "decimal": 0.625, - "enum": "yes" + "enum": "yes", + "datetime": "2001-02-03T04:05:06Z" }, "response": {} }, @@ -803,7 +823,7 @@ "method": "checkQuery", "httpRequest": { "method": "GET", - "path": "/CHECKQUERY?string=pathCase&boolean=true&double=1.5&int32=3&int64=4&decimal=0.625&enum=yes" + "path": "/CHECKQUERY?string=pathCase&boolean=true&double=1.5&int32=3&int64=4&decimal=0.625&enum=yes&datetime=2001-02-03T04:05:06Z" }, "request": { "string": "pathCase", @@ -812,7 +832,8 @@ "int32": 3, "int64": 4, "decimal": 0.625, - "enum": "yes" + "enum": "yes", + "datetime": "2001-02-03T04:05:06Z" }, "response": {} }, @@ -821,7 +842,7 @@ "method": "checkQuery", "httpRequest": { "method": "GET", - "path": "/checkQuery?STRING=queryCase&boolean=true&double=1.5&int32=3&int64=4&decimal=0.625&enum=yes" + "path": "/checkQuery?STRING=queryCase&boolean=true&double=1.5&int32=3&int64=4&decimal=0.625&enum=yes&datetime=2001-02-03T04:05:06Z" }, "request": { "string": "queryCase", @@ -830,7 +851,8 @@ "int32": 3, "int64": 4, "decimal": 0.625, - "enum": "yes" + "enum": "yes", + "datetime": "2001-02-03T04:05:06Z" }, "response": {} }, diff --git a/conformance/ts/src/conformanceApi.ts b/conformance/ts/src/conformanceApi.ts index 814ec7d..e18424a 100644 --- a/conformance/ts/src/conformanceApi.ts +++ b/conformance/ts/src/conformanceApi.ts @@ -259,6 +259,7 @@ class ConformanceApiHttpClient implements IConformanceApi { request.int64 == null || query.push('int64=' + request.int64.toString()); request.decimal == null || query.push('decimal=' + request.decimal.toString()); request.enum == null || query.push('enum=' + request.enum); + request.datetime == null || query.push('datetime=' + encodeURIComponent(request.datetime)); if (query.length) { uri = uri + '?' + query.join('&'); } @@ -308,7 +309,11 @@ class ConformanceApiHttpClient implements IConformanceApi { if (!uriPartEnum) { return Promise.resolve(createRequiredRequestFieldError('enum')); } - const uri = `checkPath/${uriPartString}/${uriPartBoolean}/${uriPartDouble}/${uriPartInt32}/${uriPartInt64}/${uriPartDecimal}/${uriPartEnum}`; + const uriPartDatetime = request.datetime != null && encodeURIComponent(request.datetime); + if (!uriPartDatetime) { + return Promise.resolve(createRequiredRequestFieldError('datetime')); + } + const uri = `checkPath/${uriPartString}/${uriPartBoolean}/${uriPartDouble}/${uriPartInt32}/${uriPartInt64}/${uriPartDecimal}/${uriPartEnum}/${uriPartDatetime}`; const fetchRequest: IFetchRequest = { method: 'GET', }; @@ -353,6 +358,9 @@ class ConformanceApiHttpClient implements IConformanceApi { if (request.enum != null) { fetchRequest.headers!['enum'] = request.enum; } + if (request.datetime != null) { + fetchRequest.headers!['datetime'] = request.datetime; + } return fetchResponse(this._fetch, this._baseUri + uri, fetchRequest, context) .then(result => { const status = result.response.status; @@ -392,6 +400,10 @@ class ConformanceApiHttpClient implements IConformanceApi { if (headerValue != null) { value.enum = headerValue as Answer; } + headerValue = result.response.headers.get('datetime'); + if (headerValue != null) { + value.datetime = headerValue; + } return { value: value }; }); } diff --git a/conformance/ts/src/conformanceApiTypes.ts b/conformance/ts/src/conformanceApiTypes.ts index 05479e4..42586af 100644 --- a/conformance/ts/src/conformanceApiTypes.ts +++ b/conformance/ts/src/conformanceApiTypes.ts @@ -165,6 +165,8 @@ export interface ICheckQueryRequest { decimal?: number; enum?: Answer; + + datetime?: string; } /** Response for CheckQuery. */ @@ -186,6 +188,8 @@ export interface ICheckPathRequest { decimal?: number; enum?: Answer; + + datetime?: string; } /** Response for CheckPath. */ @@ -207,6 +211,8 @@ export interface IMirrorHeadersRequest { decimal?: number; enum?: Answer; + + datetime?: string; } /** Response for MirrorHeaders. */ @@ -224,6 +230,8 @@ export interface IMirrorHeadersResponse { decimal?: number; enum?: Answer; + + datetime?: string; } /** Request for Mixed. */ @@ -336,6 +344,8 @@ export interface IAny { decimal?: number; + datetime?: string; + bytes?: string; object?: { [name: string]: any }; @@ -368,6 +378,8 @@ export interface IAnyArray { decimal?: number[]; + datetime?: string[]; + bytes?: string[]; object?: { [name: string]: any }[]; @@ -400,6 +412,8 @@ export interface IAnyMap { decimal?: { [name: string]: number }; + datetime?: { [name: string]: string }; + bytes?: { [name: string]: string }; object?: { [name: string]: { [name: string]: any } }; @@ -432,6 +446,8 @@ export interface IAnyResult { decimal?: IServiceResult; + datetime?: IServiceResult; + bytes?: IServiceResult; object?: IServiceResult<{ [name: string]: any }>; @@ -464,6 +480,8 @@ export interface IAnyNullable { decimal?: (number | null); + datetime?: (string | null); + bytes?: (string | null); object?: ({ [name: string]: any } | null); diff --git a/dotnet-tools.json b/dotnet-tools.json index 8e16642..9562684 100644 --- a/dotnet-tools.json +++ b/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "facilityconformance": { - "version": "2.19.0", + "version": "2.21.0", "commands": [ "FacilityConformance" ] diff --git a/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs b/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs index d82e0d5..514608c 100644 --- a/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs +++ b/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs @@ -632,6 +632,7 @@ private static string RenderFieldType(ServiceTypeInfo fieldType) { case ServiceTypeKind.String: case ServiceTypeKind.Bytes: + case ServiceTypeKind.DateTime: return "string"; case ServiceTypeKind.Enum: return fieldType.Enum!.Name; @@ -676,6 +677,7 @@ private static string RenderUriComponent(ServiceFieldInfo field, ServiceInfo ser return $"request.{fieldName}"; case ServiceTypeKind.String: case ServiceTypeKind.Bytes: + case ServiceTypeKind.DateTime: return $"encodeURIComponent(request.{fieldName})"; case ServiceTypeKind.Boolean: case ServiceTypeKind.Int32: @@ -703,6 +705,7 @@ private static string ParseFieldValue(ServiceFieldInfo field, ServiceInfo servic return $"{value} as {field.TypeName}"; case ServiceTypeKind.String: case ServiceTypeKind.Bytes: + case ServiceTypeKind.DateTime: return value; case ServiceTypeKind.Boolean: return $"parseBoolean({value})"; @@ -730,6 +733,7 @@ private static string RenderFieldValue(ServiceFieldInfo field, ServiceInfo servi case ServiceTypeKind.Enum: case ServiceTypeKind.String: case ServiceTypeKind.Bytes: + case ServiceTypeKind.DateTime: return value; case ServiceTypeKind.Boolean: case ServiceTypeKind.Int32: