@compassdigital/sdk.typescript 3.0.0-beta.10 → 3.0.0-beta.11

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.
package/src/base.ts CHANGED
@@ -19,6 +19,10 @@ export interface RequestOptions {
19
19
  intercept?: InterceptFn;
20
20
  }
21
21
 
22
+ /**
23
+ * The request data provided to intercept
24
+ * functions.
25
+ */
22
26
  export interface RequestData {
23
27
  name: string;
24
28
  service: string;
@@ -28,23 +32,78 @@ export interface RequestData {
28
32
  body?: string;
29
33
  }
30
34
 
35
+ /**
36
+ * The response data expected from
37
+ * intercept functions.
38
+ */
31
39
  export interface ResponseData {
32
40
  ok: boolean;
33
41
  status: number;
34
42
  body: string;
35
43
  }
36
44
 
45
+ /**
46
+ * A function that performs an https request.
47
+ */
37
48
  export type FetchFn = (req: RequestData) => Promise<ResponseData>;
38
49
 
50
+ /**
51
+ * A function that intercepts an http request and returns a response.
52
+ */
39
53
  export type InterceptFn = (req: RequestData, fetch: FetchFn) => Promise<ResponseData>;
40
54
 
41
- export type ServiceRequestOptions = Omit<RequestOptions, "allow">;
55
+ /**
56
+ * We have to re-implement the promise methods instead of just existing
57
+ * the existing Promise class due to: https://github.com/microsoft/TypeScript/issues/15202
58
+ */
59
+ export class ResponsePromise<T> implements Promise<T> {
42
60
 
43
- export interface ServiceClientOptions extends ServiceRequestOptions {
44
- services?: Record<string, ServiceRequestOptions>;
45
- methods?: Record<string, ServiceRequestOptions>;
61
+ /**
62
+ * Implements Promise interface.
63
+ */
64
+ [Symbol.toStringTag] = "[object ResponsePromise]";
65
+
66
+ constructor(private promise: Promise<T>) {}
67
+
68
+ /**
69
+ * Implements Promise interface.
70
+ */
71
+ then<TResult1 = T, TResult2 = never>(
72
+ onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
73
+ onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
74
+ ): ResponsePromise<TResult1 | TResult2> {
75
+ return new ResponsePromise(this.promise.then(onfulfilled, onrejected));
76
+ }
77
+
78
+ /**
79
+ * Implements Promise interface.
80
+ */
81
+ catch<TResult = never>(
82
+ onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
83
+ ): ResponsePromise<T | TResult> {
84
+ return new ResponsePromise(this.promise.catch(onrejected));
85
+ }
86
+
87
+ /**
88
+ * Implements Promise interface.
89
+ */
90
+ finally(onfinally?: (() => void) | null): ResponsePromise<T> {
91
+ return new ResponsePromise(this.promise.finally(onfinally));
92
+ }
93
+
94
+ /**
95
+ * Returns a promise that resolves to null if http status code
96
+ * or service error code matches one of the provided codes.
97
+ * If no codes are specified, all codes are ignored.
98
+ */
99
+ ignore(...codes: number[]): ResponsePromise<T | null> {
100
+ return new ResponsePromise(ServiceError.ignore(codes, this.promise));
101
+ }
46
102
  }
47
103
 
104
+ /**
105
+ * An error returned from an api service.
106
+ */
48
107
  export class ServiceError extends Error {
49
108
  constructor(
50
109
  public status: number, // http status
@@ -58,6 +117,10 @@ export class ServiceError extends Error {
58
117
  Object.setPrototypeOf(this, ServiceError.prototype);
59
118
  }
60
119
 
120
+ /**
121
+ * Returns the http status code from err.
122
+ * If err isn't an instance of ServiceError, -1 is returned.
123
+ */
61
124
  static status(err: any): number {
62
125
  if (err instanceof ServiceError) {
63
126
  return err.status;
@@ -65,6 +128,10 @@ export class ServiceError extends Error {
65
128
  return -1;
66
129
  }
67
130
 
131
+ /**
132
+ * Returns the service error code from err.
133
+ * If err isn't an instance of ServiceError, -1 is returned.
134
+ */
68
135
  static code(err: any): number {
69
136
  if (err instanceof ServiceError) {
70
137
  return err.code;
@@ -72,11 +139,19 @@ export class ServiceError extends Error {
72
139
  return -1;
73
140
  }
74
141
 
142
+ /**
143
+ * Returns a promise that resolves to null if http status code
144
+ * or service error code matches one of the provided codes.
145
+ * If no codes are specified, all codes are ignored.
146
+ */
75
147
  static async ignore<T>(codes: number[], response: Promise<T>): Promise<T | null> {
76
148
  try {
77
149
  return await response;
78
150
  } catch (err) {
79
151
  if (err instanceof ServiceError) {
152
+ if (codes.length === 0) {
153
+ return null;
154
+ }
80
155
  for (const code of codes) {
81
156
  if (err.status === code || err.code === code) {
82
157
  return null;
@@ -87,6 +162,9 @@ export class ServiceError extends Error {
87
162
  }
88
163
  }
89
164
 
165
+ /**
166
+ * Constructs a ServiceError from the provided response data.
167
+ */
90
168
  static from_res(res: ResponseData): ServiceError {
91
169
  let message = res.body;
92
170
  let code = res.status;
@@ -101,10 +179,14 @@ export class ServiceError extends Error {
101
179
  }
102
180
  }
103
181
 
182
+ /**
183
+ * BaseServiceClient contains the logic for executing http requests.
184
+ * This class is meant to be extended by the generated code.
185
+ */
104
186
  export abstract class BaseServiceClient {
105
- private options: ServiceClientOptions;
187
+ private options: RequestOptions;
106
188
 
107
- constructor(options?: ServiceClientOptions) {
189
+ constructor(options?: RequestOptions) {
108
190
  this.options = options ?? {};
109
191
  }
110
192
 
@@ -131,6 +213,10 @@ export abstract class BaseServiceClient {
131
213
  return url;
132
214
  }
133
215
 
216
+ /**
217
+ * Perform the http request or pass it to the intercept function
218
+ * if one was configured.
219
+ */
134
220
  private async fetch(req: RequestData, options?: RequestOptions): Promise<ResponseData> {
135
221
  if (options?.intercept) {
136
222
  return options.intercept(req, this.fetch.bind(this));
@@ -147,28 +233,47 @@ export abstract class BaseServiceClient {
147
233
  };
148
234
  }
149
235
 
236
+ /**
237
+ * Returns true if the response status was a 4xx error.
238
+ */
150
239
  private is_4xx(res: ResponseData): boolean {
151
240
  return 400 <= res.status && res.status < 500;
152
241
  }
153
242
 
154
- private get_options(service: string, name: string, options: RequestOptions): RequestOptions {
155
- const service_options = this.options.services?.[service];
156
- const method_options = this.options.methods?.[name];
243
+ /**
244
+ * Returns the merged options.
245
+ */
246
+ private get_options(options: RequestOptions): RequestOptions {
157
247
  return {
158
248
  ...this.options,
159
- ...service_options,
160
- ...method_options,
161
249
  ...options,
162
250
  headers: {
163
251
  ...this.options.headers,
164
- ...method_options?.headers,
165
- ...service_options?.headers,
166
252
  ...options.headers,
167
253
  }
168
254
  };
169
255
  }
170
256
 
171
- protected async request(
257
+ /**
258
+ * A delegate to _request that wraps the response in a ResponsePromise.
259
+ */
260
+ protected request(
261
+ service: string,
262
+ name: string,
263
+ method: string,
264
+ route: string,
265
+ body: any,
266
+ options: { query?: any } & RequestOptions = {}
267
+ ): ResponsePromise<any> {
268
+ const promise = this._request(service, name, method, route, body, options);
269
+ return new ResponsePromise(promise);
270
+ }
271
+
272
+ /**
273
+ * The main logic for fulfilling a request as perscribed by
274
+ * the request options.
275
+ */
276
+ private async _request(
172
277
  service: string,
173
278
  name: string,
174
279
  method: string,
@@ -176,7 +281,7 @@ export abstract class BaseServiceClient {
176
281
  body: any,
177
282
  options: { query?: any } & RequestOptions = {}
178
283
  ): Promise<any> {
179
- options = this.get_options(service, name, options);
284
+ options = this.get_options(options);
180
285
  const url = this.build_url(route, options, options.query ?? {});
181
286
  const headers = Object.assign({
182
287
  "User-Agent": "CDL/ServiceClient"