webflow-api 0.8.0 → 1.0.0
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/README.md +222 -17
- package/dist/ResponseWrapper.js +128 -197
- package/dist/Webflow.js +322 -436
- package/dist/WebflowClient.js +115 -0
- package/dist/index.js +8 -2
- package/index.d.ts +86 -50
- package/package.json +25 -27
- package/src/ResponseWrapper.js +25 -28
- package/src/Webflow.js +195 -215
- package/src/WebflowClient.js +98 -0
- package/src/index.js +3 -2
- package/yarn.lock +2056 -1933
- package/dist/WebflowError.js +0 -54
- package/dist/utils.js +0 -29
- package/src/WebflowError.js +0 -5
- package/src/utils.js +0 -13
package/src/Webflow.js
CHANGED
|
@@ -1,314 +1,294 @@
|
|
|
1
|
-
import
|
|
2
|
-
import qs from "qs";
|
|
3
|
-
|
|
4
|
-
import { isObjectEmpty } from "./utils";
|
|
1
|
+
import { WebflowClient, WebflowRequestError } from "./WebflowClient";
|
|
5
2
|
import ResponseWrapper from "./ResponseWrapper";
|
|
6
|
-
import WebflowError, { buildRequiredArgError } from "./WebflowError";
|
|
7
|
-
|
|
8
|
-
const DEFAULT_ENDPOINT = "https://api.webflow.com";
|
|
9
|
-
|
|
10
|
-
const buildMeta = (res) => {
|
|
11
|
-
if (!res || !res.headers) {
|
|
12
|
-
return {};
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return {
|
|
16
|
-
rateLimit: {
|
|
17
|
-
limit: parseInt(res.headers.get("x-ratelimit-limit"), 10),
|
|
18
|
-
remaining: parseInt(res.headers.get("x-ratelimit-remaining"), 10),
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const responseHandler = (res) =>
|
|
24
|
-
res
|
|
25
|
-
.json()
|
|
26
|
-
.catch((err) =>
|
|
27
|
-
// Catch unexpected server errors where json isn't sent and rewrite
|
|
28
|
-
// with proper class (WebflowError)
|
|
29
|
-
Promise.reject(new WebflowError(err))
|
|
30
|
-
)
|
|
31
|
-
.then((body) => {
|
|
32
|
-
if (res.status >= 400) {
|
|
33
|
-
const errOpts = {
|
|
34
|
-
code: body.code,
|
|
35
|
-
msg: body.msg,
|
|
36
|
-
_meta: buildMeta(res),
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
if (body.problems && body.problems.length > 0) {
|
|
40
|
-
errOpts.problems = body.problems;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const errMsg = body && body.err ? body.err : "Unknown error occured";
|
|
44
|
-
const err = new WebflowError(errMsg);
|
|
45
|
-
|
|
46
|
-
return Promise.reject(Object.assign(err, errOpts));
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
body._meta = buildMeta(res); // eslint-disable-line no-param-reassign
|
|
50
|
-
|
|
51
|
-
return body;
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
export default class Webflow {
|
|
55
|
-
constructor({ endpoint = DEFAULT_ENDPOINT, token, version = "1.0.0" } = {}) {
|
|
56
|
-
if (!token) throw buildRequiredArgError("token");
|
|
57
3
|
|
|
4
|
+
export { WebflowRequestError };
|
|
5
|
+
export class WebflowArgumentError extends Error {
|
|
6
|
+
constructor(name) {
|
|
7
|
+
super(`Argument '${name}' is required but was not present`);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export class Webflow {
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
this.client = new WebflowClient(options);
|
|
58
13
|
this.responseWrapper = new ResponseWrapper(this);
|
|
59
|
-
|
|
60
|
-
this.endpoint = endpoint;
|
|
61
|
-
this.token = token;
|
|
62
|
-
|
|
63
|
-
this.headers = {
|
|
64
|
-
Accept: "application/json",
|
|
65
|
-
Authorization: `Bearer ${token}`,
|
|
66
|
-
"accept-version": version,
|
|
67
|
-
"Content-Type": "application/json",
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
this.authenticatedFetch = (method, path, data, query) => {
|
|
71
|
-
const queryString =
|
|
72
|
-
query && !isObjectEmpty(query) ? `?${qs.stringify(query)}` : "";
|
|
73
|
-
|
|
74
|
-
const uri = `${this.endpoint}${path}${queryString}`;
|
|
75
|
-
const opts = {
|
|
76
|
-
method,
|
|
77
|
-
headers: this.headers,
|
|
78
|
-
mode: "cors",
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
if (data) {
|
|
82
|
-
opts.body = JSON.stringify(data);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return fetch(uri, opts).then(responseHandler);
|
|
86
|
-
};
|
|
87
14
|
}
|
|
88
15
|
|
|
89
|
-
// Generic HTTP request handlers
|
|
90
|
-
|
|
91
16
|
get(path, query = {}) {
|
|
92
|
-
return this.
|
|
17
|
+
return this.client.get(path, query);
|
|
93
18
|
}
|
|
94
19
|
|
|
95
20
|
post(path, data, query = {}) {
|
|
96
|
-
return this.
|
|
21
|
+
return this.client.post(path, data, query);
|
|
97
22
|
}
|
|
98
23
|
|
|
99
24
|
put(path, data, query = {}) {
|
|
100
|
-
return this.
|
|
25
|
+
return this.client.put(path, data, query);
|
|
101
26
|
}
|
|
102
27
|
|
|
103
28
|
patch(path, data, query = {}) {
|
|
104
|
-
return this.
|
|
29
|
+
return this.client.patch(path, data, query);
|
|
105
30
|
}
|
|
106
31
|
|
|
107
|
-
delete(path, query = {}) {
|
|
108
|
-
return this.
|
|
32
|
+
delete(path, data, query = {}) {
|
|
33
|
+
return this.client.delete(path, data, query);
|
|
109
34
|
}
|
|
110
35
|
|
|
111
36
|
// Meta
|
|
37
|
+
info() {
|
|
38
|
+
return this.get("/info");
|
|
39
|
+
}
|
|
112
40
|
|
|
113
|
-
|
|
114
|
-
return this.get("/
|
|
41
|
+
installer() {
|
|
42
|
+
return this.get("/user");
|
|
115
43
|
}
|
|
116
44
|
|
|
117
45
|
// Sites
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
return
|
|
121
|
-
sites.map((site) => this.responseWrapper.site(site))
|
|
122
|
-
);
|
|
46
|
+
async sites(query = {}) {
|
|
47
|
+
const sites = await this.get("/sites", query);
|
|
48
|
+
return sites.map((site) => this.responseWrapper.site(site));
|
|
123
49
|
}
|
|
124
50
|
|
|
125
|
-
site({ siteId }, query = {}) {
|
|
126
|
-
if (!siteId)
|
|
51
|
+
async site({ siteId }, query = {}) {
|
|
52
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
127
53
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
);
|
|
54
|
+
const site = await this.get(`/sites/${siteId}`, query);
|
|
55
|
+
return this.responseWrapper.site(site);
|
|
131
56
|
}
|
|
132
57
|
|
|
133
58
|
publishSite({ siteId, domains }) {
|
|
134
|
-
if (!siteId)
|
|
135
|
-
if (!domains)
|
|
59
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
60
|
+
if (!domains) throw new WebflowArgumentError("domains");
|
|
136
61
|
|
|
137
62
|
return this.post(`/sites/${siteId}/publish`, { domains });
|
|
138
63
|
}
|
|
139
64
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
domains({ siteId }) {
|
|
143
|
-
if (!siteId) return Promise.reject(buildRequiredArgError("siteId"));
|
|
65
|
+
async domains({ siteId }) {
|
|
66
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
144
67
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
);
|
|
68
|
+
const domains = await this.client.get(`/sites/${siteId}/domains`);
|
|
69
|
+
return domains.map((domain) => this.responseWrapper.domain(domain, siteId));
|
|
148
70
|
}
|
|
149
71
|
|
|
150
72
|
// Collections
|
|
73
|
+
async collections({ siteId }, query = {}) {
|
|
74
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
151
75
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
return this.get(`/sites/${siteId}/collections`, query).then((collections) =>
|
|
156
|
-
collections.map((collection) =>
|
|
157
|
-
this.responseWrapper.collection(collection)
|
|
158
|
-
)
|
|
76
|
+
const collections = await this.get(`/sites/${siteId}/collections`, query);
|
|
77
|
+
return collections.map((collection) =>
|
|
78
|
+
this.responseWrapper.collection(collection)
|
|
159
79
|
);
|
|
160
80
|
}
|
|
161
81
|
|
|
162
|
-
collection({ collectionId }, query = {}) {
|
|
163
|
-
if (!collectionId)
|
|
164
|
-
return Promise.reject(buildRequiredArgError("collectionId"));
|
|
82
|
+
async collection({ collectionId }, query = {}) {
|
|
83
|
+
if (!collectionId) throw new WebflowArgumentError("collectionId");
|
|
165
84
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
);
|
|
85
|
+
const uri = `/collections/${collectionId}`;
|
|
86
|
+
const collection = await this.client.get(uri, query);
|
|
87
|
+
return this.responseWrapper.collection(collection);
|
|
169
88
|
}
|
|
170
89
|
|
|
171
90
|
// Items
|
|
91
|
+
async items({ collectionId }, query = {}) {
|
|
92
|
+
if (!collectionId) throw new WebflowArgumentError("collectionId");
|
|
172
93
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
return Promise.reject(buildRequiredArgError("collectionId"));
|
|
94
|
+
const uri = `/collections/${collectionId}/items`;
|
|
95
|
+
const res = await this.client.get(uri, query);
|
|
176
96
|
|
|
177
|
-
return
|
|
178
|
-
|
|
179
|
-
...res,
|
|
97
|
+
return {
|
|
98
|
+
...res,
|
|
180
99
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
);
|
|
100
|
+
items: res.items.map((item) =>
|
|
101
|
+
this.responseWrapper.item(item, collectionId)
|
|
102
|
+
),
|
|
103
|
+
};
|
|
186
104
|
}
|
|
187
105
|
|
|
188
|
-
item({ collectionId, itemId }, query = {}) {
|
|
189
|
-
if (!collectionId)
|
|
190
|
-
|
|
191
|
-
if (!itemId) return Promise.reject(buildRequiredArgError("itemId"));
|
|
106
|
+
async item({ collectionId, itemId }, query = {}) {
|
|
107
|
+
if (!collectionId) throw new WebflowArgumentError("collectionId");
|
|
108
|
+
if (!itemId) throw new WebflowArgumentError("itemId");
|
|
192
109
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
);
|
|
110
|
+
const uri = `/collections/${collectionId}/items/${itemId}`;
|
|
111
|
+
const { items } = await this.client.get(uri, query);
|
|
112
|
+
return this.responseWrapper.item(items[0], collectionId);
|
|
196
113
|
}
|
|
197
114
|
|
|
198
|
-
createItem({ collectionId, ...data }, query = {}) {
|
|
199
|
-
if (!collectionId)
|
|
200
|
-
return Promise.reject(buildRequiredArgError("collectionId"));
|
|
115
|
+
async createItem({ collectionId, ...data }, query = {}) {
|
|
116
|
+
if (!collectionId) throw new WebflowArgumentError("collectionId");
|
|
201
117
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
);
|
|
118
|
+
const uri = `/collections/${collectionId}/items`;
|
|
119
|
+
const item = await this.post(uri, data, query);
|
|
120
|
+
return this.responseWrapper.item(item, collectionId);
|
|
205
121
|
}
|
|
206
122
|
|
|
207
123
|
updateItem({ collectionId, itemId, ...data }, query = {}) {
|
|
208
|
-
if (!collectionId)
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
return this.put(
|
|
213
|
-
`/collections/${collectionId}/items/${itemId}`,
|
|
214
|
-
data,
|
|
215
|
-
query
|
|
216
|
-
);
|
|
124
|
+
if (!collectionId) throw new WebflowArgumentError("collectionId");
|
|
125
|
+
if (!itemId) throw new WebflowArgumentError("itemId");
|
|
126
|
+
|
|
127
|
+
const uri = `/collections/${collectionId}/items/${itemId}`;
|
|
128
|
+
return this.put(uri, data, query);
|
|
217
129
|
}
|
|
218
130
|
|
|
219
131
|
removeItem({ collectionId, itemId }, query = {}) {
|
|
220
|
-
if (!collectionId)
|
|
221
|
-
|
|
222
|
-
if (!itemId) return Promise.reject(buildRequiredArgError("itemId"));
|
|
132
|
+
if (!collectionId) throw new WebflowArgumentError("collectionId");
|
|
133
|
+
if (!itemId) throw new WebflowArgumentError("itemId");
|
|
223
134
|
|
|
224
|
-
|
|
135
|
+
const uri = `/collections/${collectionId}/items/${itemId}`;
|
|
136
|
+
return this.delete(uri, null, query);
|
|
225
137
|
}
|
|
226
138
|
|
|
227
139
|
patchItem({ collectionId, itemId, ...data }, query = {}) {
|
|
228
|
-
if (!collectionId)
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
return this.patch(
|
|
233
|
-
`/collections/${collectionId}/items/${itemId}`,
|
|
234
|
-
data,
|
|
235
|
-
query
|
|
236
|
-
);
|
|
140
|
+
if (!collectionId) throw new WebflowArgumentError("collectionId");
|
|
141
|
+
if (!itemId) throw new WebflowArgumentError("itemId");
|
|
142
|
+
|
|
143
|
+
const uri = `/collections/${collectionId}/items/${itemId}`;
|
|
144
|
+
return this.patch(uri, data, query);
|
|
237
145
|
}
|
|
238
146
|
|
|
239
|
-
|
|
147
|
+
deleteItems({ collectionId, itemIds, ...data }, query = {}) {
|
|
148
|
+
if (!collectionId) throw new WebflowArgumentError("collectionId");
|
|
149
|
+
if (!itemIds) throw new WebflowArgumentError("itemIds");
|
|
150
|
+
|
|
151
|
+
const uri = `/collections/${collectionId}/items`;
|
|
152
|
+
return this.delete(uri, { ...data, itemIds }, query);
|
|
153
|
+
}
|
|
240
154
|
|
|
241
|
-
|
|
242
|
-
if (!
|
|
155
|
+
publishItems({ collectionId, itemIds, ...data }, query = {}) {
|
|
156
|
+
if (!collectionId) throw new WebflowArgumentError("collectionId");
|
|
157
|
+
if (!itemIds) throw new WebflowArgumentError("itemIds");
|
|
243
158
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
);
|
|
159
|
+
const uri = `/collections/${collectionId}/items/publish`;
|
|
160
|
+
return this.put(uri, { ...data, itemIds }, query);
|
|
247
161
|
}
|
|
248
162
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
if (!
|
|
163
|
+
// Users
|
|
164
|
+
async users({ siteId }, query = {}) {
|
|
165
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
252
166
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
167
|
+
const res = await this.get(`/sites/${siteId}/users`, query);
|
|
168
|
+
return {
|
|
169
|
+
...res,
|
|
170
|
+
users: res.users.map((user) => this.responseWrapper.user(user, siteId)),
|
|
171
|
+
};
|
|
256
172
|
}
|
|
257
173
|
|
|
258
|
-
|
|
259
|
-
if (!siteId)
|
|
260
|
-
if (!userId)
|
|
174
|
+
async user({ siteId, userId }, query = {}) {
|
|
175
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
176
|
+
if (!userId) throw new WebflowArgumentError("userId");
|
|
261
177
|
|
|
262
|
-
|
|
178
|
+
const uri = `/sites/${siteId}/users/${userId}`;
|
|
179
|
+
const user = await this.get(uri, query);
|
|
180
|
+
return this.responseWrapper.user(user, siteId);
|
|
263
181
|
}
|
|
264
182
|
|
|
265
|
-
|
|
266
|
-
if (!siteId)
|
|
267
|
-
if (!
|
|
183
|
+
async updateUser({ siteId, userId, ...data }, query = {}) {
|
|
184
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
185
|
+
if (!userId) throw new WebflowArgumentError("userId");
|
|
268
186
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
);
|
|
187
|
+
const uri = `/sites/${siteId}/users/${userId}`;
|
|
188
|
+
const user = await this.patch(uri, data, query);
|
|
189
|
+
return this.responseWrapper.user(user, siteId);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async inviteUser({ siteId, email }, query = {}) {
|
|
193
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
194
|
+
if (!email) throw new WebflowArgumentError("email");
|
|
195
|
+
|
|
196
|
+
const uri = `/sites/${siteId}/users/invite`;
|
|
197
|
+
const user = await this.post(uri, { email }, query);
|
|
198
|
+
return this.responseWrapper.user(user, siteId);
|
|
272
199
|
}
|
|
273
200
|
|
|
274
201
|
removeUser({ siteId, userId }, query = {}) {
|
|
275
|
-
if (!siteId)
|
|
276
|
-
if (!userId)
|
|
202
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
203
|
+
if (!userId) throw new WebflowArgumentError("userId");
|
|
277
204
|
|
|
278
|
-
return this.delete(`/sites/${siteId}/users/${userId}`, query);
|
|
205
|
+
return this.delete(`/sites/${siteId}/users/${userId}`, null, query);
|
|
279
206
|
}
|
|
280
207
|
|
|
281
208
|
// Webhooks
|
|
209
|
+
async webhooks({ siteId }, query = {}) {
|
|
210
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
282
211
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
webhooks.map((webhook) => this.responseWrapper.webhook(webhook, siteId))
|
|
212
|
+
const uri = `/sites/${siteId}/webhooks`;
|
|
213
|
+
const webhooks = await this.client.get(uri, query);
|
|
214
|
+
return webhooks.map((webhook) =>
|
|
215
|
+
this.responseWrapper.webhook(webhook, siteId)
|
|
288
216
|
);
|
|
289
217
|
}
|
|
290
218
|
|
|
291
|
-
webhook({ siteId, webhookId }, query = {}) {
|
|
292
|
-
if (!siteId)
|
|
293
|
-
if (!webhookId)
|
|
219
|
+
async webhook({ siteId, webhookId }, query = {}) {
|
|
220
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
221
|
+
if (!webhookId) throw new WebflowArgumentError("webhookId");
|
|
294
222
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
);
|
|
223
|
+
const uri = `/sites/${siteId}/webhooks/${webhookId}`;
|
|
224
|
+
const webhook = await this.client.get(uri, query);
|
|
225
|
+
return this.responseWrapper.webhook(webhook, siteId);
|
|
298
226
|
}
|
|
299
227
|
|
|
300
|
-
createWebhook({ siteId, ...data }, query = {}) {
|
|
301
|
-
if (!siteId)
|
|
228
|
+
async createWebhook({ siteId, triggerType, ...data }, query = {}) {
|
|
229
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
230
|
+
if (!triggerType) throw new WebflowArgumentError("triggerType");
|
|
302
231
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
);
|
|
232
|
+
const uri = `/sites/${siteId}/webhooks`;
|
|
233
|
+
const webhook = { ...data, triggerType };
|
|
234
|
+
const createdWebhook = await this.client.post(uri, webhook, query);
|
|
235
|
+
return this.responseWrapper.webhook(createdWebhook, siteId);
|
|
306
236
|
}
|
|
307
237
|
|
|
308
238
|
removeWebhook({ siteId, webhookId }, query = {}) {
|
|
309
|
-
if (!siteId)
|
|
310
|
-
if (!webhookId)
|
|
239
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
240
|
+
if (!webhookId) throw new WebflowArgumentError("webhookId");
|
|
241
|
+
|
|
242
|
+
return this.delete(`/sites/${siteId}/webhooks/${webhookId}`, null, query);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// OAuth
|
|
246
|
+
authorizeUrl({
|
|
247
|
+
client_id,
|
|
248
|
+
redirect_uri,
|
|
249
|
+
state,
|
|
250
|
+
scope,
|
|
251
|
+
response_type = "code",
|
|
252
|
+
}) {
|
|
253
|
+
if (!client_id) throw new WebflowArgumentError("clientId");
|
|
254
|
+
|
|
255
|
+
const query = new URLSearchParams({ response_type, client_id });
|
|
256
|
+
|
|
257
|
+
if (redirect_uri) query.set("redirect_uri", redirect_uri);
|
|
258
|
+
if (state) query.set("state", state);
|
|
259
|
+
if (scope) query.set("scope", scope);
|
|
311
260
|
|
|
312
|
-
return this.
|
|
261
|
+
return `https://${this.host}/oauth/authorize?${query}`;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
accessToken({
|
|
265
|
+
client_id,
|
|
266
|
+
client_secret,
|
|
267
|
+
code,
|
|
268
|
+
redirect_uri,
|
|
269
|
+
grant_type = "authorization_code",
|
|
270
|
+
}) {
|
|
271
|
+
if (!client_id) throw new WebflowArgumentError("client_id");
|
|
272
|
+
if (!client_secret) throw new WebflowArgumentError("client_secret");
|
|
273
|
+
if (!code) throw new WebflowArgumentError("code");
|
|
274
|
+
|
|
275
|
+
return this.post("/oauth/access_token", {
|
|
276
|
+
client_id,
|
|
277
|
+
client_secret,
|
|
278
|
+
code,
|
|
279
|
+
redirect_uri,
|
|
280
|
+
grant_type,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
revokeToken({ client_id, client_secret, access_token }) {
|
|
285
|
+
if (!client_id) throw new WebflowArgumentError("client_id");
|
|
286
|
+
if (!client_secret) throw new WebflowArgumentError("client_secret");
|
|
287
|
+
if (!access_token) throw new WebflowArgumentError("access_token");
|
|
288
|
+
|
|
289
|
+
const uri = "/oauth/revoke_authorization";
|
|
290
|
+
return this.post(uri, { client_id, client_secret, access_token });
|
|
313
291
|
}
|
|
314
292
|
}
|
|
293
|
+
|
|
294
|
+
export default Webflow;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import fetch from "isomorphic-fetch";
|
|
2
|
+
|
|
3
|
+
const DEFAULT_HOST = "webflow.com";
|
|
4
|
+
const USER_AGENT = "Webflow Javascript SDK / 1.0";
|
|
5
|
+
|
|
6
|
+
export class WebflowRequestError extends Error {
|
|
7
|
+
constructor(error) {
|
|
8
|
+
super(error.err ? error.err : "Unknown error occured");
|
|
9
|
+
Object.assign(this, error);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class WebflowClient {
|
|
14
|
+
constructor({ host, token, version, headers, mode } = {}) {
|
|
15
|
+
this.host = host || DEFAULT_HOST;
|
|
16
|
+
this.headers = headers || {};
|
|
17
|
+
this.version = version;
|
|
18
|
+
this.token = token;
|
|
19
|
+
this.mode = mode;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
getUri(path, query = {}) {
|
|
23
|
+
const hasQuery = Object.keys(query).length > 0;
|
|
24
|
+
const queryString = hasQuery ? `?${new URLSearchParams(query)}` : "";
|
|
25
|
+
return `https://api.${this.host}${path}${queryString}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getHeaders() {
|
|
29
|
+
const { version, token } = this;
|
|
30
|
+
|
|
31
|
+
const headers = {
|
|
32
|
+
"Content-Type": "application/json",
|
|
33
|
+
Accept: "application/json",
|
|
34
|
+
"User-Agent": USER_AGENT,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// set authorization header if token is set
|
|
38
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
39
|
+
|
|
40
|
+
// set the API version
|
|
41
|
+
if (version) headers["accept-version"] = version;
|
|
42
|
+
|
|
43
|
+
// merge headers with user headers;
|
|
44
|
+
return { ...headers, ...this.headers };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async parseBody(res) {
|
|
48
|
+
const body = await res.json();
|
|
49
|
+
|
|
50
|
+
// append ratelimit meta data to response
|
|
51
|
+
if (res.headers) {
|
|
52
|
+
const limit = parseInt(res.headers.get("x-ratelimit-limit"), 10);
|
|
53
|
+
const remaining = parseInt(res.headers.get("x-ratelimit-remaining"), 10);
|
|
54
|
+
body._meta = { rateLimit: { limit, remaining } };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// webflow error
|
|
58
|
+
if (body.err) throw new WebflowRequestError(body);
|
|
59
|
+
|
|
60
|
+
return body;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
fetch(method, path, data, query) {
|
|
64
|
+
// build uri
|
|
65
|
+
const uri = this.getUri(path, query);
|
|
66
|
+
|
|
67
|
+
// build request options
|
|
68
|
+
const headers = this.getHeaders();
|
|
69
|
+
const opts = { method, headers, mode: this.mode };
|
|
70
|
+
if (data) opts.body = JSON.stringify(data);
|
|
71
|
+
|
|
72
|
+
// call fetch and wrap response
|
|
73
|
+
return fetch(uri, opts).then(this.parseBody.bind(this));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Generic HTTP request handlers
|
|
77
|
+
get(path, query = {}) {
|
|
78
|
+
return this.fetch("GET", path, null, query);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
post(path, data, query = {}) {
|
|
82
|
+
return this.fetch("POST", path, data, query);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
put(path, data, query = {}) {
|
|
86
|
+
return this.fetch("PUT", path, data, query);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
patch(path, data, query = {}) {
|
|
90
|
+
return this.fetch("PATCH", path, data, query);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
delete(path, data, query = {}) {
|
|
94
|
+
return this.fetch("DELETE", path, data, query);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export default WebflowClient;
|
package/src/index.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { Webflow } from "./Webflow";
|
|
2
|
+
|
|
3
|
+
export default Webflow;
|