webflow-api 0.8.1 → 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 -466
- 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 +187 -235
- 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,342 +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
32
|
delete(path, data, query = {}) {
|
|
108
|
-
return this.
|
|
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
|
-
|
|
223
|
-
|
|
224
|
-
return this.delete(
|
|
225
|
-
`/collections/${collectionId}/items/${itemId}`,
|
|
226
|
-
null,
|
|
227
|
-
query
|
|
228
|
-
);
|
|
132
|
+
if (!collectionId) throw new WebflowArgumentError("collectionId");
|
|
133
|
+
if (!itemId) throw new WebflowArgumentError("itemId");
|
|
134
|
+
|
|
135
|
+
const uri = `/collections/${collectionId}/items/${itemId}`;
|
|
136
|
+
return this.delete(uri, null, query);
|
|
229
137
|
}
|
|
230
138
|
|
|
231
139
|
patchItem({ collectionId, itemId, ...data }, query = {}) {
|
|
232
|
-
if (!collectionId)
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
return this.patch(
|
|
237
|
-
`/collections/${collectionId}/items/${itemId}`,
|
|
238
|
-
data,
|
|
239
|
-
query
|
|
240
|
-
);
|
|
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);
|
|
241
145
|
}
|
|
242
146
|
|
|
243
147
|
deleteItems({ collectionId, itemIds, ...data }, query = {}) {
|
|
244
|
-
if (!collectionId)
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
return this.delete(
|
|
249
|
-
`/collections/${collectionId}/items`,
|
|
250
|
-
{ ...data, itemIds },
|
|
251
|
-
query
|
|
252
|
-
);
|
|
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);
|
|
253
153
|
}
|
|
254
154
|
|
|
255
155
|
publishItems({ collectionId, itemIds, ...data }, query = {}) {
|
|
256
|
-
if (!collectionId)
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
return this.put(
|
|
261
|
-
`/collections/${collectionId}/items/publish`,
|
|
262
|
-
{ ...data, itemIds },
|
|
263
|
-
query
|
|
264
|
-
);
|
|
156
|
+
if (!collectionId) throw new WebflowArgumentError("collectionId");
|
|
157
|
+
if (!itemIds) throw new WebflowArgumentError("itemIds");
|
|
158
|
+
|
|
159
|
+
const uri = `/collections/${collectionId}/items/publish`;
|
|
160
|
+
return this.put(uri, { ...data, itemIds }, query);
|
|
265
161
|
}
|
|
266
162
|
|
|
267
163
|
// Users
|
|
164
|
+
async users({ siteId }, query = {}) {
|
|
165
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
268
166
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
);
|
|
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
|
+
};
|
|
275
172
|
}
|
|
276
173
|
|
|
277
|
-
user({ siteId, userId }, query = {}) {
|
|
278
|
-
if (!siteId)
|
|
279
|
-
if (!userId)
|
|
174
|
+
async user({ siteId, userId }, query = {}) {
|
|
175
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
176
|
+
if (!userId) throw new WebflowArgumentError("userId");
|
|
280
177
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
);
|
|
178
|
+
const uri = `/sites/${siteId}/users/${userId}`;
|
|
179
|
+
const user = await this.get(uri, query);
|
|
180
|
+
return this.responseWrapper.user(user, siteId);
|
|
284
181
|
}
|
|
285
182
|
|
|
286
|
-
updateUser({ siteId, userId, ...data }, query = {}) {
|
|
287
|
-
if (!siteId)
|
|
288
|
-
if (!userId)
|
|
183
|
+
async updateUser({ siteId, userId, ...data }, query = {}) {
|
|
184
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
185
|
+
if (!userId) throw new WebflowArgumentError("userId");
|
|
289
186
|
|
|
290
|
-
|
|
187
|
+
const uri = `/sites/${siteId}/users/${userId}`;
|
|
188
|
+
const user = await this.patch(uri, data, query);
|
|
189
|
+
return this.responseWrapper.user(user, siteId);
|
|
291
190
|
}
|
|
292
191
|
|
|
293
|
-
inviteUser({ siteId, email }, query = {}) {
|
|
294
|
-
if (!siteId)
|
|
295
|
-
if (!email)
|
|
192
|
+
async inviteUser({ siteId, email }, query = {}) {
|
|
193
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
194
|
+
if (!email) throw new WebflowArgumentError("email");
|
|
296
195
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
);
|
|
196
|
+
const uri = `/sites/${siteId}/users/invite`;
|
|
197
|
+
const user = await this.post(uri, { email }, query);
|
|
198
|
+
return this.responseWrapper.user(user, siteId);
|
|
300
199
|
}
|
|
301
200
|
|
|
302
201
|
removeUser({ siteId, userId }, query = {}) {
|
|
303
|
-
if (!siteId)
|
|
304
|
-
if (!userId)
|
|
202
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
203
|
+
if (!userId) throw new WebflowArgumentError("userId");
|
|
305
204
|
|
|
306
205
|
return this.delete(`/sites/${siteId}/users/${userId}`, null, query);
|
|
307
206
|
}
|
|
308
207
|
|
|
309
208
|
// Webhooks
|
|
209
|
+
async webhooks({ siteId }, query = {}) {
|
|
210
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
310
211
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
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)
|
|
316
216
|
);
|
|
317
217
|
}
|
|
318
218
|
|
|
319
|
-
webhook({ siteId, webhookId }, query = {}) {
|
|
320
|
-
if (!siteId)
|
|
321
|
-
if (!webhookId)
|
|
219
|
+
async webhook({ siteId, webhookId }, query = {}) {
|
|
220
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
221
|
+
if (!webhookId) throw new WebflowArgumentError("webhookId");
|
|
322
222
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
);
|
|
223
|
+
const uri = `/sites/${siteId}/webhooks/${webhookId}`;
|
|
224
|
+
const webhook = await this.client.get(uri, query);
|
|
225
|
+
return this.responseWrapper.webhook(webhook, siteId);
|
|
326
226
|
}
|
|
327
227
|
|
|
328
|
-
createWebhook({ siteId, ...data }, query = {}) {
|
|
329
|
-
if (!siteId)
|
|
228
|
+
async createWebhook({ siteId, triggerType, ...data }, query = {}) {
|
|
229
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
230
|
+
if (!triggerType) throw new WebflowArgumentError("triggerType");
|
|
330
231
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
);
|
|
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);
|
|
334
236
|
}
|
|
335
237
|
|
|
336
238
|
removeWebhook({ siteId, webhookId }, query = {}) {
|
|
337
|
-
if (!siteId)
|
|
338
|
-
if (!webhookId)
|
|
239
|
+
if (!siteId) throw new WebflowArgumentError("siteId");
|
|
240
|
+
if (!webhookId) throw new WebflowArgumentError("webhookId");
|
|
339
241
|
|
|
340
242
|
return this.delete(`/sites/${siteId}/webhooks/${webhookId}`, null, query);
|
|
341
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);
|
|
260
|
+
|
|
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 });
|
|
291
|
+
}
|
|
342
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;
|