From 6a08084e37f0571abb9b85f95f73066774f8aafd Mon Sep 17 00:00:00 2001 From: Michael Baldwin Date: Fri, 15 Sep 2023 17:13:20 -0400 Subject: [PATCH 1/6] generate error sets --- .../JavaScriptGenerator.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs b/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs index 514608c..1846cf0 100644 --- a/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs +++ b/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs @@ -158,6 +158,23 @@ public override CodeGenOutput GenerateOutput(ServiceInfo service) } } + foreach (var errorSetInfo in service.ErrorSets) + { + typeNames.Add(errorSetInfo.Name); + code.WriteLine(); + WriteJsDoc(code, errorSetInfo); + using (code.Block($"export enum {errorSetInfo.Name} {{", "}")) + { + code.WriteLineSkipOnce(); + foreach (var error in errorSetInfo.Errors) + { + code.WriteLineSkipOnce(); + WriteJsDoc(code, error); + code.WriteLine($"{error.Name} = '{error.Name}',"); + } + } + } + code.WriteLine(); })); } From 5fd1da000b8eb92651e9c8d75bf48f677b2b584a Mon Sep 17 00:00:00 2001 From: Michael Baldwin Date: Fri, 15 Sep 2023 17:19:25 -0400 Subject: [PATCH 2/6] run codegen --- conformance/ts/src/conformanceApi.ts | 2 +- conformance/ts/src/conformanceApiTypes.ts | 10 ++++++++++ example/ts/src/exampleApi.ts | 2 +- example/ts/src/exampleApiServer.ts | 2 +- example/ts/src/exampleApiTypes.ts | 7 +++++++ 5 files changed, 20 insertions(+), 3 deletions(-) diff --git a/conformance/ts/src/conformanceApi.ts b/conformance/ts/src/conformanceApi.ts index e18424a..8c1e794 100644 --- a/conformance/ts/src/conformanceApi.ts +++ b/conformance/ts/src/conformanceApi.ts @@ -2,7 +2,7 @@ /* eslint-disable */ import { HttpClientUtility, IServiceResult, IHttpClientOptions } from 'facility-core'; -import { IConformanceApi, IGetApiInfoRequest, IGetApiInfoResponse, IGetWidgetsRequest, IGetWidgetsResponse, ICreateWidgetRequest, ICreateWidgetResponse, IGetWidgetRequest, IGetWidgetResponse, IDeleteWidgetRequest, IDeleteWidgetResponse, IGetWidgetBatchRequest, IGetWidgetBatchResponse, IMirrorFieldsRequest, IMirrorFieldsResponse, ICheckQueryRequest, ICheckQueryResponse, ICheckPathRequest, ICheckPathResponse, IMirrorHeadersRequest, IMirrorHeadersResponse, IMixedRequest, IMixedResponse, IRequiredRequest, IRequiredResponse, IMirrorBytesRequest, IMirrorBytesResponse, IMirrorTextRequest, IMirrorTextResponse, IBodyTypesRequest, IBodyTypesResponse, IWidget, IAny, IAnyArray, IAnyMap, IAnyResult, IAnyNullable, IHasWidget, Answer } from './conformanceApiTypes'; +import { IConformanceApi, IGetApiInfoRequest, IGetApiInfoResponse, IGetWidgetsRequest, IGetWidgetsResponse, ICreateWidgetRequest, ICreateWidgetResponse, IGetWidgetRequest, IGetWidgetResponse, IDeleteWidgetRequest, IDeleteWidgetResponse, IGetWidgetBatchRequest, IGetWidgetBatchResponse, IMirrorFieldsRequest, IMirrorFieldsResponse, ICheckQueryRequest, ICheckQueryResponse, ICheckPathRequest, ICheckPathResponse, IMirrorHeadersRequest, IMirrorHeadersResponse, IMixedRequest, IMixedResponse, IRequiredRequest, IRequiredResponse, IMirrorBytesRequest, IMirrorBytesResponse, IMirrorTextRequest, IMirrorTextResponse, IBodyTypesRequest, IBodyTypesResponse, IWidget, IAny, IAnyArray, IAnyMap, IAnyResult, IAnyNullable, IHasWidget, Answer, ApiErrors } from './conformanceApiTypes'; export * from './conformanceApiTypes'; /** Provides access to ConformanceApi over HTTP via fetch. */ diff --git a/conformance/ts/src/conformanceApiTypes.ts b/conformance/ts/src/conformanceApiTypes.ts index 42586af..a208504 100644 --- a/conformance/ts/src/conformanceApiTypes.ts +++ b/conformance/ts/src/conformanceApiTypes.ts @@ -515,3 +515,13 @@ export enum Answer { maybe = 'maybe', } +/** Custom errors. */ +export enum ApiErrors { + + /** The user is not an administrator. */ + NotAdmin = 'NotAdmin', + + /** I'm "too" 😄! */ + TooHappy = 'TooHappy', +} + diff --git a/example/ts/src/exampleApi.ts b/example/ts/src/exampleApi.ts index de5424a..fd6775b 100644 --- a/example/ts/src/exampleApi.ts +++ b/example/ts/src/exampleApi.ts @@ -2,7 +2,7 @@ /* eslint-disable */ import { HttpClientUtility, IServiceResult, IHttpClientOptions } from 'facility-core'; -import { IExampleApi, IGetWidgetsRequest, IGetWidgetsResponse, ICreateWidgetRequest, ICreateWidgetResponse, IGetWidgetRequest, IGetWidgetResponse, IDeleteWidgetRequest, IDeleteWidgetResponse, IEditWidgetRequest, IEditWidgetResponse, IGetWidgetBatchRequest, IGetWidgetBatchResponse, IGetWidgetWeightRequest, IGetWidgetWeightResponse, IGetPreferenceRequest, IGetPreferenceResponse, ISetPreferenceRequest, ISetPreferenceResponse, IGetInfoRequest, IGetInfoResponse, INotRestfulRequest, INotRestfulResponse, IKitchenRequest, IKitchenResponse, IWidget, IWidgetJob, IPreference, IObsoleteData, IKitchenSink, WidgetField, ObsoleteEnum } from './exampleApiTypes'; +import { IExampleApi, IGetWidgetsRequest, IGetWidgetsResponse, ICreateWidgetRequest, ICreateWidgetResponse, IGetWidgetRequest, IGetWidgetResponse, IDeleteWidgetRequest, IDeleteWidgetResponse, IEditWidgetRequest, IEditWidgetResponse, IGetWidgetBatchRequest, IGetWidgetBatchResponse, IGetWidgetWeightRequest, IGetWidgetWeightResponse, IGetPreferenceRequest, IGetPreferenceResponse, ISetPreferenceRequest, ISetPreferenceResponse, IGetInfoRequest, IGetInfoResponse, INotRestfulRequest, INotRestfulResponse, IKitchenRequest, IKitchenResponse, IWidget, IWidgetJob, IPreference, IObsoleteData, IKitchenSink, WidgetField, ObsoleteEnum, ExampleApiErrors } from './exampleApiTypes'; export * from './exampleApiTypes'; /** Provides access to ExampleApi over HTTP via fetch. */ diff --git a/example/ts/src/exampleApiServer.ts b/example/ts/src/exampleApiServer.ts index 0609e3e..6146a2c 100644 --- a/example/ts/src/exampleApiServer.ts +++ b/example/ts/src/exampleApiServer.ts @@ -4,7 +4,7 @@ import * as bodyParser from 'body-parser'; import * as express from 'express'; import { IServiceResult } from 'facility-core'; -import { IExampleApi, IGetWidgetsRequest, IGetWidgetsResponse, ICreateWidgetRequest, ICreateWidgetResponse, IGetWidgetRequest, IGetWidgetResponse, IDeleteWidgetRequest, IDeleteWidgetResponse, IEditWidgetRequest, IEditWidgetResponse, IGetWidgetBatchRequest, IGetWidgetBatchResponse, IGetWidgetWeightRequest, IGetWidgetWeightResponse, IGetPreferenceRequest, IGetPreferenceResponse, ISetPreferenceRequest, ISetPreferenceResponse, IGetInfoRequest, IGetInfoResponse, INotRestfulRequest, INotRestfulResponse, IKitchenRequest, IKitchenResponse, IWidget, IWidgetJob, IPreference, IObsoleteData, IKitchenSink, WidgetField, ObsoleteEnum } from './exampleApiTypes'; +import { IExampleApi, IGetWidgetsRequest, IGetWidgetsResponse, ICreateWidgetRequest, ICreateWidgetResponse, IGetWidgetRequest, IGetWidgetResponse, IDeleteWidgetRequest, IDeleteWidgetResponse, IEditWidgetRequest, IEditWidgetResponse, IGetWidgetBatchRequest, IGetWidgetBatchResponse, IGetWidgetWeightRequest, IGetWidgetWeightResponse, IGetPreferenceRequest, IGetPreferenceResponse, ISetPreferenceRequest, ISetPreferenceResponse, IGetInfoRequest, IGetInfoResponse, INotRestfulRequest, INotRestfulResponse, IKitchenRequest, IKitchenResponse, IWidget, IWidgetJob, IPreference, IObsoleteData, IKitchenSink, WidgetField, ObsoleteEnum, ExampleApiErrors } from './exampleApiTypes'; export * from './exampleApiTypes'; const standardErrorCodes: { [code: string]: number } = { diff --git a/example/ts/src/exampleApiTypes.ts b/example/ts/src/exampleApiTypes.ts index ef70dcb..19c43c9 100644 --- a/example/ts/src/exampleApiTypes.ts +++ b/example/ts/src/exampleApiTypes.ts @@ -350,3 +350,10 @@ export enum ObsoleteEnum { unused = 'unused', } +/** Custom errors. */ +export enum ExampleApiErrors { + + /** The user is not an administrator. */ + NotAdmin = 'NotAdmin', +} + From 638e175753c50753b54c7eb49a496b06b2482560 Mon Sep 17 00:00:00 2001 From: Michael Baldwin Date: Fri, 15 Sep 2023 17:24:26 -0400 Subject: [PATCH 3/6] match enums output --- conformance/ts/src/conformanceApiTypes.ts | 1 - example/ts/src/exampleApiTypes.ts | 1 - src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs | 1 - 3 files changed, 3 deletions(-) diff --git a/conformance/ts/src/conformanceApiTypes.ts b/conformance/ts/src/conformanceApiTypes.ts index a208504..c321db2 100644 --- a/conformance/ts/src/conformanceApiTypes.ts +++ b/conformance/ts/src/conformanceApiTypes.ts @@ -517,7 +517,6 @@ export enum Answer { /** Custom errors. */ export enum ApiErrors { - /** The user is not an administrator. */ NotAdmin = 'NotAdmin', diff --git a/example/ts/src/exampleApiTypes.ts b/example/ts/src/exampleApiTypes.ts index 19c43c9..45ce934 100644 --- a/example/ts/src/exampleApiTypes.ts +++ b/example/ts/src/exampleApiTypes.ts @@ -352,7 +352,6 @@ export enum ObsoleteEnum { /** Custom errors. */ export enum ExampleApiErrors { - /** The user is not an administrator. */ NotAdmin = 'NotAdmin', } diff --git a/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs b/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs index 1846cf0..145014c 100644 --- a/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs +++ b/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs @@ -165,7 +165,6 @@ public override CodeGenOutput GenerateOutput(ServiceInfo service) WriteJsDoc(code, errorSetInfo); using (code.Block($"export enum {errorSetInfo.Name} {{", "}")) { - code.WriteLineSkipOnce(); foreach (var error in errorSetInfo.Errors) { code.WriteLineSkipOnce(); From 3648cbf9692b23bf35c7f4aba8b919630de2567d Mon Sep 17 00:00:00 2001 From: Michael Baldwin Date: Mon, 18 Sep 2023 10:19:49 -0400 Subject: [PATCH 4/6] add test. --- .../JavaScriptGeneratorTests.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/Facility.CodeGen.JavaScript.UnitTests/JavaScriptGeneratorTests.cs b/tests/Facility.CodeGen.JavaScript.UnitTests/JavaScriptGeneratorTests.cs index 1133267..e9fa96e 100644 --- a/tests/Facility.CodeGen.JavaScript.UnitTests/JavaScriptGeneratorTests.cs +++ b/tests/Facility.CodeGen.JavaScript.UnitTests/JavaScriptGeneratorTests.cs @@ -246,6 +246,37 @@ public void GenerateExampleApiTypeScript_ExternEnumNameIsSameAsAlias() StringAssert.Contains("thing?: Thing;", typesFile.Text); } + [Test] + public void GenerateExampleApiTypeScript_IncludesErrorSets() + { + ServiceInfo service; + const string fileName = "Facility.CodeGen.JavaScript.UnitTests.ExampleApi.fsd"; + var parser = new FsdParser(); + var stream = GetType().GetTypeInfo().Assembly.GetManifestResourceStream(fileName)!; + Assert.IsNotNull(stream); + using (var reader = new StreamReader(stream)) + service = parser.ParseDefinition(new ServiceDefinitionText(Path.GetFileName(fileName), reader.ReadToEnd())); + + var generator = new JavaScriptGenerator + { + GeneratorName = "JavaScriptGeneratorTests", + TypeScript = true, + NewLine = "\n", + }; + var result = generator.GenerateOutput(service); + Assert.IsNotNull(result); + + var typesFile = result.Files.Single(f => f.Name == "exampleApiTypes.ts"); + const string expectedErrorSet = """ + /** Custom errors. */ + export enum ExampleApiErrors { + /** The user is not an administrator. */ + NotAdmin = 'NotAdmin', + } + """; + Assert.That(typesFile.Text, Contains.Substring(expectedErrorSet)); + } + private void ThrowsServiceDefinitionException(string definition, string message) { var parser = new FsdParser(); From 9973b3a6ef46271b9ca04950c22730ad483018e1 Mon Sep 17 00:00:00 2001 From: Michael Baldwin Date: Mon, 18 Sep 2023 12:33:10 -0400 Subject: [PATCH 5/6] add error sets to standard error codes. --- example/js/exampleApiServer.js | 1 + example/ts/src/exampleApiServer.ts | 1 + src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs | 8 ++++++++ 3 files changed, 10 insertions(+) diff --git a/example/js/exampleApiServer.js b/example/js/exampleApiServer.js index 5ba997a..88aed80 100644 --- a/example/js/exampleApiServer.js +++ b/example/js/exampleApiServer.js @@ -16,6 +16,7 @@ const standardErrorCodes = { 'TooManyRequests': 429, 'InternalError': 500, 'ServiceUnavailable': 503, + 'NotAdmin': 403, }; function parseBoolean(value) { diff --git a/example/ts/src/exampleApiServer.ts b/example/ts/src/exampleApiServer.ts index 6146a2c..ac3ac2f 100644 --- a/example/ts/src/exampleApiServer.ts +++ b/example/ts/src/exampleApiServer.ts @@ -18,6 +18,7 @@ const standardErrorCodes: { [code: string]: number } = { 'TooManyRequests': 429, 'InternalError': 500, 'ServiceUnavailable': 503, + 'NotAdmin': 403, }; function parseBoolean(value: string | undefined) { diff --git a/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs b/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs index 145014c..6df802f 100644 --- a/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs +++ b/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs @@ -456,6 +456,14 @@ public override CodeGenOutput GenerateOutput(ServiceInfo service) code.WriteLine("'TooManyRequests': 429,"); code.WriteLine("'InternalError': 500,"); code.WriteLine("'ServiceUnavailable': 503,"); + + httpServiceInfo.ErrorSets.ToList().ForEach(errorSetInfo => + { + foreach (var error in errorSetInfo.Errors) + { + code.WriteLine($"'{error.ServiceError.Name}': {(int) error.StatusCode},"); + } + }); } // TODO: export this from facility-core? From 4679eb1bc03c095dcd8be45c98bd3c00d95aa28b Mon Sep 17 00:00:00 2001 From: Michael Baldwin Date: Mon, 18 Sep 2023 13:29:04 -0400 Subject: [PATCH 6/6] prefer foreach --- src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs b/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs index 6df802f..f8c4e58 100644 --- a/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs +++ b/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs @@ -457,13 +457,13 @@ public override CodeGenOutput GenerateOutput(ServiceInfo service) code.WriteLine("'InternalError': 500,"); code.WriteLine("'ServiceUnavailable': 503,"); - httpServiceInfo.ErrorSets.ToList().ForEach(errorSetInfo => + foreach (var errorSetInfo in httpServiceInfo.ErrorSets) { foreach (var error in errorSetInfo.Errors) { code.WriteLine($"'{error.ServiceError.Name}': {(int) error.StatusCode},"); } - }); + } } // TODO: export this from facility-core?