-
Notifications
You must be signed in to change notification settings - Fork 2.7k
/
api.js
144 lines (124 loc) · 3.27 KB
/
api.js
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/* eslint-disable import/no-unresolved */
'use strict';
import { fire as fireHook } from 'hooks';
import { confirm } from 'bootbox';
const baseUrl = config.relative_path + '/api/v3';
async function call(options, callback) {
options.url = options.url.startsWith('/api') ?
config.relative_path + options.url :
baseUrl + options.url;
if (typeof callback === 'function') {
xhr(options).then(result => callback(null, result), err => callback(err));
return;
}
try {
const result = await xhr(options);
return result;
} catch (err) {
if (err.message === 'A valid login session was not found. Please log in and try again.') {
const { url } = await fireHook('filter:admin.reauth', { url: 'login' });
return confirm('[[error:api.reauth-required]]', (ok) => {
if (ok) {
ajaxify.go(url);
}
});
}
throw err;
}
}
async function xhr(options) {
// Normalize body based on type
const { url } = options;
delete options.url;
if (options.data && !(options.data instanceof FormData)) {
options.data = JSON.stringify(options.data || {});
options.headers['content-type'] = 'application/json; charset=utf-8';
}
// Allow options to be modified by plugins, etc.
({ options } = await fireHook('filter:api.options', { options }));
/**
* Note: pre-v4 backwards compatibility
*
* This module now passes in "data" to xhr().
* This is because the "filter:api.options" hook (and plugins using it) expect "data".
* fetch() expects body, so we rename it here.
*
* In v4, replace all instances of "data" with "body" and record as breaking change.
*/
if (options.data) {
options.body = options.data;
delete options.data;
}
const res = await fetch(url, options);
const { headers } = res;
const contentType = headers.get('content-type');
const isJSON = contentType && contentType.startsWith('application/json');
let response;
if (options.method !== 'HEAD') {
if (isJSON) {
response = await res.json();
} else {
response = await res.text();
}
}
if (!res.ok) {
if (response) {
throw new Error(isJSON ? response.status.message : response);
}
throw new Error(res.statusText);
}
return isJSON && response && response.hasOwnProperty('status') && response.hasOwnProperty('response') ?
response.response :
response;
}
export function get(route, data, onSuccess) {
return call({
url: route + (data && Object.keys(data).length ? ('?' + $.param(data)) : ''),
}, onSuccess);
}
export function head(route, data, onSuccess) {
return call({
url: route + (data && Object.keys(data).length ? ('?' + $.param(data)) : ''),
method: 'HEAD',
}, onSuccess);
}
export function post(route, data, onSuccess) {
return call({
url: route,
method: 'POST',
data,
headers: {
'x-csrf-token': config.csrf_token,
},
}, onSuccess);
}
export function patch(route, data, onSuccess) {
return call({
url: route,
method: 'PATCH',
data,
headers: {
'x-csrf-token': config.csrf_token,
},
}, onSuccess);
}
export function put(route, data, onSuccess) {
return call({
url: route,
method: 'PUT',
data,
headers: {
'x-csrf-token': config.csrf_token,
},
}, onSuccess);
}
export function del(route, data, onSuccess) {
return call({
url: route,
method: 'DELETE',
data,
headers: {
'x-csrf-token': config.csrf_token,
},
}, onSuccess);
}