Skip to content

Commit 8a22000

Browse files
authored
feat: retry strategy APIC-158
2 parents 67d67f5 + ea43279 commit 8a22000

24 files changed

+850
-801
lines changed

.editorconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[*]
2+
end_of_line = lf
3+
insert_final_newline = true
4+
indent_style = space
5+
indent_size = 2
6+
7+
[*.{mustache,ts,yml}]
8+
charset = utf-8

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@ yarn-error.log
1414
*/node_modules
1515
*/dist
1616
*/.openapi-generator-ignore
17+
18+
output/complement

algolia-typescript-template/api-single.mustache

Lines changed: 53 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,19 @@
1-
import localVarRequest from 'request';
21
import http from 'http';
2+
import { shuffle } from '../complement/helpers';
3+
import { Transporter } from '../complement/Transporter';
4+
import { Headers, Host, Request, RequestOptions } from '../complement/types';
35

46
{{#imports}}
57
import { {{classname}} } from '{{filename}}';
68
{{/imports}}
79

810
import { ObjectSerializer, Authentication, VoidAuth, Interceptor } from '../model/models';
911
{{#hasAuthMethods}}
10-
import { HttpBasicAuth, HttpBearerAuth, ApiKeyAuth, OAuth } from '../model/models';
12+
import { HttpBearerAuth, ApiKeyAuth, OAuth } from '../model/models';
1113
{{/hasAuthMethods}}
1214

1315
import { HttpError, RequestFile } from './apis';
1416

15-
let defaultBasePath = '{{{basePath}}}';
16-
17-
// ===============================================
18-
// This file is autogenerated - Please do not edit
19-
// ===============================================
20-
2117
{{#operations}}
2218
{{#description}}
2319
/**
@@ -33,9 +29,7 @@ export enum {{classname}}ApiKeys {
3329
}
3430

3531
export class {{classname}} {
36-
protected _basePath = defaultBasePath;
37-
protected _defaultHeaders : any = {};
38-
protected _useQuerystring : boolean = false;
32+
private transporter: Transporter;
3933
4034
protected authentications = {
4135
'default': <Authentication>new VoidAuth(),
@@ -59,35 +53,30 @@ export class {{classname}} {
5953

6054
protected interceptors: Interceptor[] = [];
6155

62-
constructor(appId: string, apiKey: string, basePath?: string) {
63-
56+
constructor(appId: string, apiKey: string) {
6457
this.setApiKey(SearchApiApiKeys.appId, appId);
6558
this.setApiKey(SearchApiApiKeys.apiKey, apiKey);
66-
this.basePath = 'https://' + appId + '-1.algolianet.com';
67-
68-
if (basePath) {
69-
this.basePath = basePath;
70-
}
71-
}
72-
73-
set useQuerystring(value: boolean) {
74-
this._useQuerystring = value;
75-
}
76-
77-
set basePath(basePath: string) {
78-
this._basePath = basePath;
79-
}
80-
81-
set defaultHeaders(defaultHeaders: any) {
82-
this._defaultHeaders = defaultHeaders;
83-
}
84-
85-
get defaultHeaders() {
86-
return this._defaultHeaders;
87-
}
88-
89-
get basePath() {
90-
return this._basePath;
59+
this.transporter = new Transporter({
60+
hosts: ([
61+
{ url: `${appId}-dsn.algolia.net`, accept: 'read', protocol: 'https' },
62+
{ url: `${appId}.algolia.net`, accept: 'write', protocol: 'https' },
63+
] as Host[]).concat(
64+
shuffle([
65+
{ url: `${appId}-1.algolianet.com`, accept: 'readWrite', protocol: 'https' },
66+
{ url: `${appId}-2.algolianet.com`, accept: 'readWrite', protocol: 'https' },
67+
{ url: `${appId}-3.algolianet.com`, accept: 'readWrite', protocol: 'https' },
68+
])
69+
),
70+
baseHeaders: {
71+
'content-type': 'application/x-www-form-urlencoded'
72+
},
73+
userAgent: 'Algolia for Javascript',
74+
timeouts: {
75+
connect: 2,
76+
read: 5,
77+
write: 30,
78+
},
79+
});
9180
}
9281

9382
public setDefaultAuthentication(auth: Authentication) {
@@ -139,20 +128,20 @@ export class {{classname}} {
139128
{{/allParams}}
140129
*/
141130
public async {{nickname}} ({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options: {headers: {[name: string]: string}} = {headers: {}}) : Promise<{ response: http.IncomingMessage; {{#returnType}}body: {{{returnType}}}; {{/returnType}}{{^returnType}}body?: any; {{/returnType}} }> {
142-
const localVarPath = this.basePath + '{{{path}}}'{{#pathParams}}
131+
const path = '{{{path}}}'{{#pathParams}}
143132
.replace('{' + '{{baseName}}' + '}', encodeURIComponent(String({{paramName}}))){{/pathParams}};
144-
let localVarQueryParameters: any = {};
145-
let localVarHeaderParams: any = (<any>Object).assign({}, this._defaultHeaders);
133+
let headers: Headers = {};
134+
let queryParameters: Record<string, string> = {};
146135
{{#hasProduces}}
147136
const produces = [{{#produces}}'{{{mediaType}}}'{{^-last}}, {{/-last}}{{/produces}}];
148137
// give precedence to 'application/json'
149138
if (produces.indexOf('application/json') >= 0) {
150-
localVarHeaderParams.Accept = 'application/json';
139+
headers.Accept = 'application/json';
151140
} else {
152-
localVarHeaderParams.Accept = produces.join(',');
141+
headers.Accept = produces.join(',');
153142
}
154143
{{/hasProduces}}
155-
let localVarFormParams: any = {};
144+
let formParams: Record<string, string> = {};
156145

157146
{{#allParams}}
158147
{{#required}}
@@ -165,103 +154,61 @@ export class {{classname}} {
165154
{{/allParams}}
166155
{{#queryParams}}
167156
if ({{paramName}} !== undefined) {
168-
localVarQueryParameters['{{baseName}}'] = ObjectSerializer.serialize({{paramName}}, "{{{dataType}}}");
157+
queryParameters['{{baseName}}'] = ObjectSerializer.serialize({{paramName}}, "{{{dataType}}}");
169158
}
170159

171160
{{/queryParams}}
172161
{{#headerParams}}
173-
localVarHeaderParams['{{baseName}}'] = ObjectSerializer.serialize({{paramName}}, "{{{dataType}}}");
162+
headers['{{baseName}}'] = ObjectSerializer.serialize({{paramName}}, "{{{dataType}}}");
174163
{{/headerParams}}
175-
(<any>Object).assign(localVarHeaderParams, options.headers);
176-
177-
let localVarUseFormData = false;
178-
179-
{{#formParams}}
180-
if ({{paramName}} !== undefined) {
181-
{{#isFile}}
182-
localVarFormParams['{{baseName}}'] = {{paramName}};
183-
{{/isFile}}
184-
{{^isFile}}
185-
localVarFormParams['{{baseName}}'] = ObjectSerializer.serialize({{paramName}}, "{{{dataType}}}");
186-
{{/isFile}}
187-
}
188-
{{#isFile}}
189-
localVarUseFormData = true;
190-
{{/isFile}}
164+
headers = {...headers, ...options.headers};
191165

192-
{{/formParams}}
193-
let localVarRequestOptions: localVarRequest.Options = {
166+
const request: Request = {
194167
method: '{{httpMethod}}',
195-
qs: localVarQueryParameters,
196-
headers: localVarHeaderParams,
197-
uri: localVarPath,
198-
useQuerystring: this._useQuerystring,
199-
{{^isResponseFile}}
200-
json: true,
201-
{{/isResponseFile}}
202-
{{#isResponseFile}}
203-
encoding: null,
204-
{{/isResponseFile}}
168+
path,
205169
{{#bodyParam}}
206-
body: ObjectSerializer.serialize({{paramName}}, "{{{dataType}}}")
170+
data: ObjectSerializer.serialize({{paramName}}, "{{{dataType}}}")
207171
{{/bodyParam}}
208172
};
209173

174+
const requestOptions: RequestOptions = {
175+
headers,
176+
queryParameters
177+
};
178+
210179
let authenticationPromise = Promise.resolve();
211180
{{#authMethods}}
212181
{{#isApiKey}}
213182
if (this.authentications.{{name}}.apiKey) {
214-
authenticationPromise = authenticationPromise.then(() => this.authentications.{{name}}.applyToRequest(localVarRequestOptions));
183+
authenticationPromise = authenticationPromise.then(() => this.authentications.{{name}}.applyToRequest(requestOptions));
215184
}
216185
{{/isApiKey}}
217186
{{#isBasicBasic}}
218187
if (this.authentications.{{name}}.username && this.authentications.{{name}}.password) {
219-
authenticationPromise = authenticationPromise.then(() => this.authentications.{{name}}.applyToRequest(localVarRequestOptions));
188+
authenticationPromise = authenticationPromise.then(() => this.authentications.{{name}}.applyToRequest(requestOptions));
220189
}
221190
{{/isBasicBasic}}
222191
{{#isBasicBearer}}
223192
if (this.authentications.{{name}}.accessToken) {
224-
authenticationPromise = authenticationPromise.then(() => this.authentications.{{name}}.applyToRequest(localVarRequestOptions));
193+
authenticationPromise = authenticationPromise.then(() => this.authentications.{{name}}.applyToRequest(requestOptions));
225194
}
226195
{{/isBasicBearer}}
227196
{{#isOAuth}}
228197
if (this.authentications.{{name}}.accessToken) {
229-
authenticationPromise = authenticationPromise.then(() => this.authentications.{{name}}.applyToRequest(localVarRequestOptions));
198+
authenticationPromise = authenticationPromise.then(() => this.authentications.{{name}}.applyToRequest(requestOptions));
230199
}
231200
{{/isOAuth}}
232201
{{/authMethods}}
233-
authenticationPromise = authenticationPromise.then(() => this.authentications.default.applyToRequest(localVarRequestOptions));
202+
authenticationPromise = authenticationPromise.then(() => this.authentications.default.applyToRequest(requestOptions));
234203

235204
let interceptorPromise = authenticationPromise;
236205
for (const interceptor of this.interceptors) {
237-
interceptorPromise = interceptorPromise.then(() => interceptor(localVarRequestOptions));
206+
interceptorPromise = interceptorPromise.then(() => interceptor(requestOptions));
238207
}
239208

240-
return interceptorPromise.then(() => {
241-
if (Object.keys(localVarFormParams).length) {
242-
if (localVarUseFormData) {
243-
(<any>localVarRequestOptions).formData = localVarFormParams;
244-
} else {
245-
localVarRequestOptions.form = localVarFormParams;
246-
}
247-
}
248-
return new Promise<{ response: http.IncomingMessage; {{#returnType}}body: {{{returnType}}}; {{/returnType}}{{^returnType}}body?: any; {{/returnType}} }>((resolve, reject) => {
249-
localVarRequest(localVarRequestOptions, (error, response, body) => {
250-
if (error) {
251-
reject(error);
252-
} else {
253-
{{#returnType}}
254-
body = ObjectSerializer.deserialize(body, "{{{returnType}}}");
255-
{{/returnType}}
256-
if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) {
257-
resolve({ response: response, body: body });
258-
} else {
259-
reject(new HttpError(response, body, response.statusCode));
260-
}
261-
}
262-
});
263-
});
264-
});
209+
await interceptorPromise;
210+
211+
return this.transporter.retryableRequest(request, requestOptions);
265212
}
266213
{{/operation}}
267214
}

algolia-typescript-template/models.mustache

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{{#generateApis}}
2-
import localVarRequest from 'request';
2+
import type { RequestOptions } from '../complement/types';
33
{{/generateApis}}
44

55
{{#models}}
@@ -182,24 +182,13 @@ export interface Authentication {
182182
/**
183183
* Apply authentication settings to header and query params.
184184
*/
185-
applyToRequest(requestOptions: localVarRequest.Options): Promise<void> | void;
186-
}
187-
188-
export class HttpBasicAuth implements Authentication {
189-
public username: string = '';
190-
public password: string = '';
191-
192-
applyToRequest(requestOptions: localVarRequest.Options): void {
193-
requestOptions.auth = {
194-
username: this.username, password: this.password
195-
}
196-
}
185+
applyToRequest(requestOptions: RequestOptions): Promise<void> | void;
197186
}
198187
199188
export class HttpBearerAuth implements Authentication {
200189
public accessToken: string | (() => string) = '';
201190
202-
applyToRequest(requestOptions: localVarRequest.Options): void {
191+
applyToRequest(requestOptions: RequestOptions): void {
203192
if (requestOptions && requestOptions.headers) {
204193
const accessToken = typeof this.accessToken === 'function'
205194
? this.accessToken()
@@ -215,9 +204,9 @@ export class ApiKeyAuth implements Authentication {
215204
constructor(private location: string, private paramName: string) {
216205
}
217206
218-
applyToRequest(requestOptions: localVarRequest.Options): void {
207+
applyToRequest(requestOptions: RequestOptions): void {
219208
if (this.location == "query") {
220-
(<any>requestOptions.qs)[this.paramName] = this.apiKey;
209+
requestOptions.queryParameters[this.paramName] = this.apiKey;
221210
} else if (this.location == "header" && requestOptions && requestOptions.headers) {
222211
requestOptions.headers[this.paramName] = this.apiKey;
223212
} else if (this.location == 'cookie' && requestOptions && requestOptions.headers) {
@@ -234,7 +223,7 @@ export class ApiKeyAuth implements Authentication {
234223
export class OAuth implements Authentication {
235224
public accessToken: string = '';
236225
237-
applyToRequest(requestOptions: localVarRequest.Options): void {
226+
applyToRequest(requestOptions: RequestOptions): void {
238227
if (requestOptions && requestOptions.headers) {
239228
requestOptions.headers["Authorization"] = "Bearer " + this.accessToken;
240229
}
@@ -245,10 +234,10 @@ export class VoidAuth implements Authentication {
245234
public username: string = '';
246235
public password: string = '';
247236
248-
applyToRequest(_: localVarRequest.Options): void {
237+
applyToRequest(_: RequestOptions): void {
249238
// Do nothing
250239
}
251240
}
252241
253-
export type Interceptor = (requestOptions: localVarRequest.Options) => (Promise<void> | void);
242+
export type Interceptor = (requestOptions: RequestOptions) => (Promise<void> | void);
254243
{{/generateApis}}

algolia-typescript-template/package.mustache

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,8 @@
1414
"test": "yarn build && node dist/client.js"
1515
},
1616
"engines": {
17-
"node": ">= 12.0.0",
18-
"yarn": ">= 2.0.0"
19-
},
20-
"dependencies": {
21-
"@types/request": "^2.48.7",
22-
"request": "^2.81.0"
17+
"node": ">= 16.0.0",
18+
"yarn": ">= 3.0.0"
2319
},
2420
"devDependencies": {
2521
"@types/node": "^16.11.6",
@@ -29,4 +25,4 @@
2925
"publishConfig": {
3026
"registry": "{{npmRepository}}"
3127
}{{/npmRepository}}
32-
}
28+
}

algolia-typescript-template/tsconfig.mustache

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"declaration": true,
1515
"lib": ["dom", "es6", "es5", "dom.iterable", "scripthost"],
1616
"outDir": "dist",
17-
"typeRoots": ["node_modules/@types"]
17+
"typeRoots": ["node_modules/@types"],
18+
"types": ["node"]
1819
},
1920
"include": ["client-search", "model", "api.ts"],
2021
"exclude": ["dist", "node_modules"]

complement/Cache.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
export interface Cache {
2+
/**
3+
* Gets the value of the given `key`.
4+
*/
5+
get: <TValue>(key: object | string, defaultValue: () => Promise<TValue>) => Promise<TValue>;
6+
7+
/**
8+
* Sets the given value with the given `key`.
9+
*/
10+
set: <TValue>(key: object | string, value: TValue) => Promise<TValue>;
11+
12+
/**
13+
* Deletes the given `key`.
14+
*/
15+
delete: (key: object | string) => Promise<void>;
16+
17+
/**
18+
* Clears the cache.
19+
*/
20+
clear: () => Promise<void>;
21+
}

0 commit comments

Comments
 (0)