@goast/typescript 0.2.7 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,12 +3,22 @@ import { Observable, filter, firstValueFrom } from 'rxjs';
3
3
 
4
4
  export async function waitForResponse<T extends HttpResponse<any> | HttpErrorResponse>(
5
5
  httpEvents: Observable<HttpEvent<any>>,
6
+ options: { errorResponseTypes: Record<number, 'text' | 'json'> },
6
7
  ): Promise<T> {
7
8
  try {
8
9
  const response = await firstValueFrom(httpEvents.pipe(filter((event) => event instanceof HttpResponse)));
9
10
  return response as T;
10
11
  } catch (error) {
11
12
  if (error instanceof HttpErrorResponse) {
13
+ if (options.errorResponseTypes[error.status] === 'json' && typeof error.error === 'string') {
14
+ return new HttpErrorResponse({
15
+ error: JSON.parse(error.error),
16
+ headers: error.headers,
17
+ status: error.status,
18
+ statusText: error.statusText,
19
+ url: error.url ?? undefined,
20
+ }) as T;
21
+ }
12
22
  return error as T;
13
23
  }
14
24
  throw error;
@@ -64,18 +64,39 @@ class DefaultTypeScriptModelGenerator extends file_generator_1.TypeScriptFileGen
64
64
  }
65
65
  getTypeAlias(ctx, schema) {
66
66
  var _a;
67
- const isDiscriminated = schema.discriminator && Object.keys(schema.discriminator.mapping).length > 0;
68
- const result = ast_1.ts.typeAlias(this.getDeclarationTypeName(ctx, schema), (_a = (isDiscriminated ? this.getDiscriminatorType(ctx, schema) : this.getType(ctx, schema, { skipSchemas: true }))) !== null && _a !== void 0 ? _a : this.getAnyType(ctx), {
67
+ const hasDiscriminator = schema.discriminator && Object.keys(schema.discriminator.mapping).length > 0;
68
+ const result = ast_1.ts.typeAlias(this.getDeclarationTypeName(ctx, schema), (_a = (hasDiscriminator ? this.getDiscriminatorType(ctx, schema) : this.getType(ctx, schema, { skipSchemas: true }))) !== null && _a !== void 0 ? _a : this.getAnyType(ctx), {
69
69
  export: true,
70
70
  doc: ast_1.ts.doc({ description: schema.description }),
71
71
  });
72
- if (isDiscriminated) {
72
+ if (hasDiscriminator) {
73
73
  const propertyValue = this.getDiscriminatorTypeName(ctx, ctx.schema);
74
74
  result.generics.push(ast_1.ts.genericParameter(this.getDiscriminatorGenericParamName(ctx, schema), {
75
75
  constraint: propertyValue,
76
76
  default: propertyValue,
77
77
  }));
78
78
  }
79
+ const inheritedSchemas = this.getInheritedSchemas(ctx, schema);
80
+ if (inheritedSchemas.length > 0) {
81
+ const fixedProperties = inheritedSchemas.reduce((p, c) => {
82
+ const mappingValues = Object.entries(c.discriminator.mapping)
83
+ .filter(([_, value]) => value.id === schema.id)
84
+ .map(([key]) => ast_1.ts.string(key));
85
+ if (c.discriminator.propertyName in p) {
86
+ p[c.discriminator.propertyName].types.push(...mappingValues);
87
+ }
88
+ else {
89
+ p[c.discriminator.propertyName] = ast_1.ts.unionType(mappingValues);
90
+ }
91
+ return p;
92
+ }, {});
93
+ result.type = ast_1.ts.intersectionType([
94
+ result.type,
95
+ ast_1.ts.objectType({
96
+ members: Object.entries(fixedProperties).map(([key, value]) => ast_1.ts.property(key, { type: value, readonly: ctx.config.immutableTypes })),
97
+ }),
98
+ ]);
99
+ }
79
100
  return result;
80
101
  }
81
102
  getIndexer(ctx, schema) {
@@ -53,10 +53,10 @@ class DefaultTypeScriptAngularServiceGenerator extends file_generator_1.TypeScri
53
53
  ...Object.entries(statusCodes).map(([key, value]) => {
54
54
  const isSuccess = key.startsWith('2');
55
55
  return ast_1.ts.intersectionType([
56
- isSuccess ? ast_1.ts.refs.angular.httpResponse([value]) : ast_1.ts.refs.angular.httpErrorResponse(),
56
+ isSuccess ? ast_1.ts.refs.angular.httpResponse([value.type]) : ast_1.ts.refs.angular.httpErrorResponse(),
57
57
  ast_1.ts.objectType({
58
58
  members: [
59
- isSuccess ? null : ast_1.ts.property('error', { type: ast_1.ts.unionType([value, ast_1.ts.refs.null_()]) }),
59
+ isSuccess ? null : ast_1.ts.property('error', { type: ast_1.ts.unionType([value.type, ast_1.ts.refs.null_()]) }),
60
60
  ast_1.ts.property('status', { type: key }),
61
61
  ast_1.ts.property('ok', { type: isSuccess ? 'true' : 'false' }),
62
62
  ],
@@ -90,13 +90,30 @@ class DefaultTypeScriptAngularServiceGenerator extends file_generator_1.TypeScri
90
90
  }
91
91
  getEndpointStatusCodes(ctx, endpoint) {
92
92
  return Object.fromEntries([
93
- ...Object.entries(ctx.config.defaultStatusCodeResponseTypes).map(([key, value]) => [
94
- key,
95
- typeof value === 'function' ? this.getSchemaType(ctx, value(ctx.data.schemas)) : value,
96
- ]),
93
+ ...Object.entries(ctx.config.defaultStatusCodeResponseTypes).map(([key, value]) => {
94
+ var _a;
95
+ return [
96
+ key,
97
+ {
98
+ parser: (_a = value === null || value === void 0 ? void 0 : value.parser) !== null && _a !== void 0 ? _a : 'text',
99
+ type: typeof (value === null || value === void 0 ? void 0 : value.type) === 'function'
100
+ ? this.getSchemaType(ctx, value.type(ctx.data.schemas))
101
+ : value !== null && value !== void 0 ? value : ast_1.ts.refs.never(),
102
+ },
103
+ ];
104
+ }),
97
105
  ...endpoint.responses
98
106
  .filter((x) => x.statusCode)
99
- .map((x) => { var _a; return [x.statusCode, this.getSchemaType(ctx, (_a = x.contentOptions[0]) === null || _a === void 0 ? void 0 : _a.schema, ast_1.ts.refs.never())]; }),
107
+ .map((x) => {
108
+ var _a, _b;
109
+ return [
110
+ x.statusCode,
111
+ {
112
+ parser: this.contentTypeToResponseType((_a = x.contentOptions[0]) === null || _a === void 0 ? void 0 : _a.type),
113
+ type: this.getSchemaType(ctx, (_b = x.contentOptions[0]) === null || _b === void 0 ? void 0 : _b.schema, ast_1.ts.refs.never()),
114
+ },
115
+ ];
116
+ }),
100
117
  ]);
101
118
  }
102
119
  // #endregion
@@ -157,7 +174,7 @@ class DefaultTypeScriptAngularServiceGenerator extends file_generator_1.TypeScri
157
174
  const responseModelType = ast_1.ts.reference(this.getResponseModelName(ctx, endpoint), this.getResponseModelFilePath(ctx));
158
175
  const returnType = ast_1.ts.refs.promise([responseModelType]);
159
176
  const accept = (_d = (_c = (_b = this.getEndpointSuccessResponse(ctx, endpoint)) === null || _b === void 0 ? void 0 : _b.contentOptions[0]) === null || _c === void 0 ? void 0 : _c.type) !== null && _d !== void 0 ? _d : '*/*';
160
- const responseType = accept.includes('json') ? 'json' : 'text';
177
+ const responseType = this.contentTypeToResponseType(accept);
161
178
  return ast_1.ts.method(this.getEndpointMethodName(ctx, endpoint), {
162
179
  accessModifier: 'public',
163
180
  parameters: [
@@ -199,7 +216,18 @@ class DefaultTypeScriptAngularServiceGenerator extends file_generator_1.TypeScri
199
216
  responseType: ${ast_1.ts.string(responseType)},
200
217
  accept: ${ast_1.ts.string(accept)},
201
218
  context,`}
202
- }))`}
219
+ })),
220
+ {${core_1.builderTemplate.indent `
221
+ errorResponseTypes: ${ast_1.ts.object({
222
+ members: [
223
+ ...Object.entries(this.getEndpointStatusCodes(ctx, endpoint))
224
+ .filter(([key]) => !key.startsWith('2'))
225
+ .map(([key, value]) => {
226
+ return ast_1.ts.property(key, { value: ast_1.ts.string(value.parser) });
227
+ }),
228
+ ],
229
+ })}`}
230
+ }`}
203
231
  )`,
204
232
  ], '\n'),
205
233
  });
@@ -238,5 +266,8 @@ class DefaultTypeScriptAngularServiceGenerator extends file_generator_1.TypeScri
238
266
  return fallback !== null && fallback !== void 0 ? fallback : this.getAnyType(ctx);
239
267
  return (b) => b.appendModelUsage(output);
240
268
  }
269
+ contentTypeToResponseType(contentType) {
270
+ return (contentType === null || contentType === void 0 ? void 0 : contentType.includes('json')) ? 'json' : 'text';
271
+ }
241
272
  }
242
273
  exports.DefaultTypeScriptAngularServiceGenerator = DefaultTypeScriptAngularServiceGenerator;
@@ -1,12 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.defaultTypeScriptAngularServicesGeneratorConfig = void 0;
4
- const ast_1 = require("../../../ast");
5
4
  const config_1 = require("../../../config");
6
5
  exports.defaultTypeScriptAngularServicesGeneratorConfig = Object.assign(Object.assign({}, config_1.defaultTypeScriptGeneratorConfig), { fileNameCasing: { casing: 'kebab', suffix: '.service' }, responseModelFileNameCasing: { casing: 'kebab', suffix: '-responses.model' }, provideKind: 'provide-fn', strictResponseTypes: true, defaultStatusCodeResponseTypes: {
7
- 401: ast_1.ts.refs.never(),
8
- 403: ast_1.ts.refs.never(),
9
- 500: ast_1.ts.refs.never(),
6
+ 401: null,
7
+ 403: null,
8
+ 500: null,
10
9
  }, possibleStatusCodes: [
11
10
  100, 101, 102, 103, 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, 302, 303, 304, 305, 306, 307, 308,
12
11
  400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 421, 422, 423, 424, 425,
@@ -61,18 +61,39 @@ export class DefaultTypeScriptModelGenerator extends TypeScriptFileGenerator {
61
61
  }
62
62
  getTypeAlias(ctx, schema) {
63
63
  var _a;
64
- const isDiscriminated = schema.discriminator && Object.keys(schema.discriminator.mapping).length > 0;
65
- const result = ts.typeAlias(this.getDeclarationTypeName(ctx, schema), (_a = (isDiscriminated ? this.getDiscriminatorType(ctx, schema) : this.getType(ctx, schema, { skipSchemas: true }))) !== null && _a !== void 0 ? _a : this.getAnyType(ctx), {
64
+ const hasDiscriminator = schema.discriminator && Object.keys(schema.discriminator.mapping).length > 0;
65
+ const result = ts.typeAlias(this.getDeclarationTypeName(ctx, schema), (_a = (hasDiscriminator ? this.getDiscriminatorType(ctx, schema) : this.getType(ctx, schema, { skipSchemas: true }))) !== null && _a !== void 0 ? _a : this.getAnyType(ctx), {
66
66
  export: true,
67
67
  doc: ts.doc({ description: schema.description }),
68
68
  });
69
- if (isDiscriminated) {
69
+ if (hasDiscriminator) {
70
70
  const propertyValue = this.getDiscriminatorTypeName(ctx, ctx.schema);
71
71
  result.generics.push(ts.genericParameter(this.getDiscriminatorGenericParamName(ctx, schema), {
72
72
  constraint: propertyValue,
73
73
  default: propertyValue,
74
74
  }));
75
75
  }
76
+ const inheritedSchemas = this.getInheritedSchemas(ctx, schema);
77
+ if (inheritedSchemas.length > 0) {
78
+ const fixedProperties = inheritedSchemas.reduce((p, c) => {
79
+ const mappingValues = Object.entries(c.discriminator.mapping)
80
+ .filter(([_, value]) => value.id === schema.id)
81
+ .map(([key]) => ts.string(key));
82
+ if (c.discriminator.propertyName in p) {
83
+ p[c.discriminator.propertyName].types.push(...mappingValues);
84
+ }
85
+ else {
86
+ p[c.discriminator.propertyName] = ts.unionType(mappingValues);
87
+ }
88
+ return p;
89
+ }, {});
90
+ result.type = ts.intersectionType([
91
+ result.type,
92
+ ts.objectType({
93
+ members: Object.entries(fixedProperties).map(([key, value]) => ts.property(key, { type: value, readonly: ctx.config.immutableTypes })),
94
+ }),
95
+ ]);
96
+ }
76
97
  return result;
77
98
  }
78
99
  getIndexer(ctx, schema) {
@@ -50,10 +50,10 @@ export class DefaultTypeScriptAngularServiceGenerator extends TypeScriptFileGene
50
50
  ...Object.entries(statusCodes).map(([key, value]) => {
51
51
  const isSuccess = key.startsWith('2');
52
52
  return ts.intersectionType([
53
- isSuccess ? ts.refs.angular.httpResponse([value]) : ts.refs.angular.httpErrorResponse(),
53
+ isSuccess ? ts.refs.angular.httpResponse([value.type]) : ts.refs.angular.httpErrorResponse(),
54
54
  ts.objectType({
55
55
  members: [
56
- isSuccess ? null : ts.property('error', { type: ts.unionType([value, ts.refs.null_()]) }),
56
+ isSuccess ? null : ts.property('error', { type: ts.unionType([value.type, ts.refs.null_()]) }),
57
57
  ts.property('status', { type: key }),
58
58
  ts.property('ok', { type: isSuccess ? 'true' : 'false' }),
59
59
  ],
@@ -87,13 +87,30 @@ export class DefaultTypeScriptAngularServiceGenerator extends TypeScriptFileGene
87
87
  }
88
88
  getEndpointStatusCodes(ctx, endpoint) {
89
89
  return Object.fromEntries([
90
- ...Object.entries(ctx.config.defaultStatusCodeResponseTypes).map(([key, value]) => [
91
- key,
92
- typeof value === 'function' ? this.getSchemaType(ctx, value(ctx.data.schemas)) : value,
93
- ]),
90
+ ...Object.entries(ctx.config.defaultStatusCodeResponseTypes).map(([key, value]) => {
91
+ var _a;
92
+ return [
93
+ key,
94
+ {
95
+ parser: (_a = value === null || value === void 0 ? void 0 : value.parser) !== null && _a !== void 0 ? _a : 'text',
96
+ type: typeof (value === null || value === void 0 ? void 0 : value.type) === 'function'
97
+ ? this.getSchemaType(ctx, value.type(ctx.data.schemas))
98
+ : value !== null && value !== void 0 ? value : ts.refs.never(),
99
+ },
100
+ ];
101
+ }),
94
102
  ...endpoint.responses
95
103
  .filter((x) => x.statusCode)
96
- .map((x) => { var _a; return [x.statusCode, this.getSchemaType(ctx, (_a = x.contentOptions[0]) === null || _a === void 0 ? void 0 : _a.schema, ts.refs.never())]; }),
104
+ .map((x) => {
105
+ var _a, _b;
106
+ return [
107
+ x.statusCode,
108
+ {
109
+ parser: this.contentTypeToResponseType((_a = x.contentOptions[0]) === null || _a === void 0 ? void 0 : _a.type),
110
+ type: this.getSchemaType(ctx, (_b = x.contentOptions[0]) === null || _b === void 0 ? void 0 : _b.schema, ts.refs.never()),
111
+ },
112
+ ];
113
+ }),
97
114
  ]);
98
115
  }
99
116
  // #endregion
@@ -154,7 +171,7 @@ export class DefaultTypeScriptAngularServiceGenerator extends TypeScriptFileGene
154
171
  const responseModelType = ts.reference(this.getResponseModelName(ctx, endpoint), this.getResponseModelFilePath(ctx));
155
172
  const returnType = ts.refs.promise([responseModelType]);
156
173
  const accept = (_d = (_c = (_b = this.getEndpointSuccessResponse(ctx, endpoint)) === null || _b === void 0 ? void 0 : _b.contentOptions[0]) === null || _c === void 0 ? void 0 : _c.type) !== null && _d !== void 0 ? _d : '*/*';
157
- const responseType = accept.includes('json') ? 'json' : 'text';
174
+ const responseType = this.contentTypeToResponseType(accept);
158
175
  return ts.method(this.getEndpointMethodName(ctx, endpoint), {
159
176
  accessModifier: 'public',
160
177
  parameters: [
@@ -196,7 +213,18 @@ export class DefaultTypeScriptAngularServiceGenerator extends TypeScriptFileGene
196
213
  responseType: ${ts.string(responseType)},
197
214
  accept: ${ts.string(accept)},
198
215
  context,`}
199
- }))`}
216
+ })),
217
+ {${s.indent `
218
+ errorResponseTypes: ${ts.object({
219
+ members: [
220
+ ...Object.entries(this.getEndpointStatusCodes(ctx, endpoint))
221
+ .filter(([key]) => !key.startsWith('2'))
222
+ .map(([key, value]) => {
223
+ return ts.property(key, { value: ts.string(value.parser) });
224
+ }),
225
+ ],
226
+ })}`}
227
+ }`}
200
228
  )`,
201
229
  ], '\n'),
202
230
  });
@@ -235,4 +263,7 @@ export class DefaultTypeScriptAngularServiceGenerator extends TypeScriptFileGene
235
263
  return fallback !== null && fallback !== void 0 ? fallback : this.getAnyType(ctx);
236
264
  return (b) => b.appendModelUsage(output);
237
265
  }
266
+ contentTypeToResponseType(contentType) {
267
+ return (contentType === null || contentType === void 0 ? void 0 : contentType.includes('json')) ? 'json' : 'text';
268
+ }
238
269
  }
@@ -1,9 +1,8 @@
1
- import { ts } from '../../../ast';
2
1
  import { defaultTypeScriptGeneratorConfig } from '../../../config';
3
2
  export const defaultTypeScriptAngularServicesGeneratorConfig = Object.assign(Object.assign({}, defaultTypeScriptGeneratorConfig), { fileNameCasing: { casing: 'kebab', suffix: '.service' }, responseModelFileNameCasing: { casing: 'kebab', suffix: '-responses.model' }, provideKind: 'provide-fn', strictResponseTypes: true, defaultStatusCodeResponseTypes: {
4
- 401: ts.refs.never(),
5
- 403: ts.refs.never(),
6
- 500: ts.refs.never(),
3
+ 401: null,
4
+ 403: null,
5
+ 500: null,
7
6
  }, possibleStatusCodes: [
8
7
  100, 101, 102, 103, 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, 302, 303, 304, 305, 306, 307, 308,
9
8
  400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 421, 422, 423, 424, 425,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goast/typescript",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "repository": "https://github.com/MaSch0212/goast.git",
5
5
  "author": {
6
6
  "name": "Marc Schmidt (MaSch0212)",
@@ -14,7 +14,10 @@ export declare class DefaultTypeScriptAngularServiceGenerator extends TypeScript
14
14
  generate(ctx: Context): Output;
15
15
  protected generateResponseModels(ctx: Context): Record<string, TypeScriptExportOutput>;
16
16
  protected getResponseModelFileContent(ctx: Context, endpoint: ApiEndpoint): AppendValueGroup<Builder>;
17
- protected getEndpointStatusCodes(ctx: Context, endpoint: ApiEndpoint): Record<number, ts.Type<Builder>>;
17
+ protected getEndpointStatusCodes(ctx: Context, endpoint: ApiEndpoint): Record<number, {
18
+ parser: 'text' | 'json';
19
+ type: ts.Type<Builder>;
20
+ }>;
18
21
  protected getServiceFileContent(ctx: Context): AppendValueGroup<Builder>;
19
22
  protected getEndpointParamsType(ctx: Context, endpoint: ApiEndpoint): ts.TypeAlias<Builder>;
20
23
  protected getClass(ctx: Context): ts.Class<Builder>;
@@ -29,5 +32,6 @@ export declare class DefaultTypeScriptAngularServiceGenerator extends TypeScript
29
32
  protected getResponseModelFilePath(ctx: Context): string;
30
33
  protected hasEndpointParams(ctx: Context, endpoint: ApiEndpoint): boolean;
31
34
  protected getSchemaType(ctx: Context, schema: ApiSchema | undefined, fallback?: ts.Type<Builder>): ts.Type<Builder>;
35
+ private contentTypeToResponseType;
32
36
  }
33
37
  export {};
@@ -29,20 +29,29 @@ export type TypeScriptAngularServicesGeneratorConfig = TypeScriptGeneratorConfig
29
29
  * The default response types for status codes that are not defined in the OpenAPI specification.
30
30
  * @example
31
31
  * {
32
- * 400: schemas =>
33
- * schemas.find(s => s.name === 'BadRequestResponse')
34
- * ?? throwExp('BadRequestResponse not found'),
35
- * 404: 'never',
36
- * 500: ts.refs.any(),
32
+ * 400: {
33
+ * parser: 'json',
34
+ * type: schemas =>
35
+ * schemas.find(s => s.name === 'BadRequestResponse')
36
+ * ?? throwExp('BadRequestResponse not found'),
37
+ * },
38
+ * 404: null,
39
+ * 500: {
40
+ * parser: 'json',
41
+ * type: ts.refs.any(),
42
+ * },
37
43
  * }
38
44
  * @default
39
45
  * {
40
- * 401: ts.refs.never(),
41
- * 403: ts.refs.never(),
42
- * 500: ts.refs.never(),
46
+ * 401: null,
47
+ * 403: null,
48
+ * 500: null,
43
49
  * }
44
50
  */
45
- defaultStatusCodeResponseTypes: Record<number, Exclude<ts.Type<TypeScriptFileBuilder>, Function> | ((schemas: readonly ApiSchema[]) => ApiSchema)>;
51
+ defaultStatusCodeResponseTypes: Record<number, {
52
+ parser: 'text' | 'json';
53
+ type: Exclude<ts.Type<TypeScriptFileBuilder>, Function> | ((schemas: readonly ApiSchema[]) => ApiSchema);
54
+ } | null>;
46
55
  /**
47
56
  * The possible status codes that any API endpoint can return.
48
57
  * This is only used when `strictResponseTypes` is set to `false`;