-
Notifications
You must be signed in to change notification settings - Fork 1
/
request_makers.ts
111 lines (93 loc) · 3.3 KB
/
request_makers.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import {parse as urlParse} from "url";
import {fetch} from "./fetch";
import {readUtf8Async} from "./files";
import {steinsaltzApiUrl} from "./steinsaltz";
import {writeJson} from "./util/json_files";
export abstract class RequestMaker {
abstract makeRequest<T>(endpoint: string): Promise<T>;
abstract makeSteinsaltzRequest(masechet: string, daf: string): Promise<any>;
}
const RETRY_OPTIONS = {
retry: {
retries: 4,
minTimeout: 200,
},
};
const STEINSALTZ_OPTIONS = {
headers: {
"api-key": "Qt23AFSTt9AAXhct",
},
...RETRY_OPTIONS,
};
export class RealRequestMaker extends RequestMaker {
makeRequest<T>(endpoint: string): Promise<T> {
return fetch(`https://www.sefaria.org/api${encodeURI(endpoint)}`, RETRY_OPTIONS)
.then(x => x.json())
.then(json => (json.error ? Promise.reject(json) : Promise.resolve(json)));
}
makeSteinsaltzRequest(masechet: string, daf: string): Promise<any> {
return fetch(steinsaltzApiUrl(masechet, daf), STEINSALTZ_OPTIONS)
.then(x => x.json())
.then(json => (json.error ? Promise.reject(json) : Promise.resolve(json)));
}
}
export const TEST_DATA_ROOT = `${__dirname}/test_data/api_request_handler`;
interface Endpoint {
ref: string;
requestBase: string;
}
function parseEndpoint(endpoint: string): Endpoint {
const match = endpoint.match(/\/([a-z]+)\/(.*)\?.*/)!;
const requestBase = match[1];
if (requestBase === "bulktext") {
const id = new URLSearchParams(urlParse(endpoint).query!).get("tp")!;
return {requestBase, ref: id};
}
return {requestBase, ref: match[2]};
}
function inputFileName(endpoint: Endpoint): string {
return `${endpoint.ref.replace(/ /g, "_")}.${endpoint.requestBase}.input.json`;
}
abstract class FileRequestMaker extends RequestMaker {
constructor(private rootDirectory: string) {
super();
}
inputFile(endpoint: string): string {
return `${this.rootDirectory}/${inputFileName(parseEndpoint(endpoint))}`;
}
steinsaltzInputFile(masechet: string, daf: string): string {
return `${this.rootDirectory}/steinsaltz/${masechet}_${daf}.json`;
}
}
export class RecordingRequestMaker extends FileRequestMaker {
private realRequestMaker = new RealRequestMaker();
makeRequest<T>(endpoint: string): Promise<T> {
const promise = this.realRequestMaker.makeRequest<T>(endpoint);
promise.then(results => writeJson(this.inputFile(endpoint), results));
return promise;
}
makeSteinsaltzRequest(masechet: string, daf: string): Promise<any> {
const promise = this.realRequestMaker.makeSteinsaltzRequest(masechet, daf);
promise.then(results => {
delete results.details.speed;
writeJson(this.steinsaltzInputFile(masechet, daf), results);
});
return promise;
}
}
export class FakeRequestMaker extends FileRequestMaker {
makeRequest<T>(endpoint: string): Promise<T> {
return this.readJson<T>(this.inputFile(endpoint));
}
makeSteinsaltzRequest(masechet: string, daf: string): Promise<any> {
return this.readJson<any>(this.steinsaltzInputFile(masechet, daf));
}
private readJson<T>(fileName: string): Promise<T> {
return readUtf8Async(fileName)
.catch(e => {
throw new Error(
`Error opening ${e.path}. This likely means that the test data needs to be updated.`);
})
.then(content => JSON.parse(content) as T);
}
}