-
Notifications
You must be signed in to change notification settings - Fork 38
/
Copy pathapi-context.ts
117 lines (107 loc) · 3.21 KB
/
api-context.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
112
113
114
115
116
117
import { Storage } from 'src/state/cache'
import { randomKey } from './randomKey'
type ApiRequestOptions = {
idempotency?: true
searchParams?: Record<string, string | number | boolean>
}
export class ApiContext {
constructor(
public readonly instanceHostName: string,
public readonly token: string
) {
if (instanceHostName.length === 0) {
throw new Error('failed to create ApiContext: instance host name missing')
}
if (token.length === 0) {
throw new Error('failed to create ApiContext: token missing')
}
}
// TODO: private
async request(
path: string,
fetch_options: RequestInit,
options?: ApiRequestOptions
): Promise<Response> {
const url = new URL(`${path}`, `https://${this.instanceHostName}`)
if (options?.searchParams) {
let { searchParams } = options
for (const key in searchParams) {
if (Object.prototype.hasOwnProperty.call(searchParams, key)) {
url.searchParams.append(key, String(searchParams[key]))
}
}
}
fetch_options.headers = {
...fetch_options.headers,
Accept: 'application/json',
Authorization: `Bearer ${this.token}`,
...(options?.idempotency ? { 'Idempotency-Key': randomKey(40) } : {}),
}
let response = await fetch(url, fetch_options)
if (!response.ok) {
let errorResponse
try {
errorResponse = await response.json()
console.warn('API Request Failed', { errorResponse })
if (
!errorResponse.error_code &&
(typeof errorResponse.error === 'undefined' || errorResponse.error.length === 0)
) {
throw new Error('request failed, but error field is empty')
}
} catch (error) {
console.error('API Request Failed - Failed to decode error:', error)
throw new Error('API Request Failed without error message')
}
if (errorResponse.error_code) {
throw new Error(
`API Request Failed: ${errorResponse.error_code}: ${errorResponse.msg}`
)
}
throw new Error(`API Request Failed: ${errorResponse.error}`)
}
return response
}
/** request in json data and gets back json data */
async jsonRequest(
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
path: string,
data = {},
searchParams: ApiRequestOptions['searchParams'] = {}
) {
return await (
await this.request(
path,
{
method,
body: JSON.stringify(data),
headers: { 'Content-Type': 'application/json' },
},
{ searchParams }
)
).json()
}
async get(url: string, searchParams: ApiRequestOptions['searchParams'] = {}) {
const response = await this.request(
url,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
},
{ searchParams }
)
return await response.json()
}
}
export function ContextFromStorage(): ApiContext {
const instance = Storage.getString('app.instance')
const token = Storage.getString('app.token')
if (!instance || !token) {
throw new Error('api token or instance undefined')
}
return new ApiContext(instance, token)
}
// For debugging
;(global as any).API = ContextFromStorage