webflow-api 0.5.4

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/dist/utils.js ADDED
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ var isObjectEmpty = exports.isObjectEmpty = function isObjectEmpty(obj) {
7
+ return Object.keys(obj).length === 0;
8
+ };
9
+
10
+ var pick = exports.pick = function pick(obj) {
11
+ for (var _len = arguments.length, props = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
12
+ props[_key - 1] = arguments[_key];
13
+ }
14
+
15
+ var picked = {};
16
+
17
+ props.forEach(function (prop) {
18
+ if (obj[prop] !== undefined) {
19
+ picked[prop] = obj[prop];
20
+ }
21
+ });
22
+
23
+ return picked;
24
+ };
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "webflow-api",
3
+ "version": "0.5.4",
4
+ "description": "SDK for the Webflow CMS API",
5
+ "main": "dist/index.js",
6
+ "jsnext:main": "src/index.js",
7
+ "repository": {
8
+ "url": "https://github.com/webflow/js-webflow-api.git",
9
+ "type": "git"
10
+ },
11
+ "license": "MIT",
12
+ "types": "index.d.ts",
13
+ "files": [
14
+ "dist",
15
+ "src",
16
+ "LICENSE",
17
+ "yarn.lock"
18
+ ],
19
+ "scripts": {
20
+ "build": "BABEL_ENV=production babel --out-dir dist src/",
21
+ "lint": "eslint src",
22
+ "prepublish": "npm run build",
23
+ "report": "nyc report --reporter=html",
24
+ "test": "nyc ava",
25
+ "watch": "npm run build -- --watch",
26
+ "watch:test": "npm run test -- --watch"
27
+ },
28
+ "devDependencies": {
29
+ "ava": "^0.16.0",
30
+ "babel-cli": "^6.18.0",
31
+ "babel-core": "^6.18.2",
32
+ "babel-eslint": "^7.1.0",
33
+ "babel-plugin-add-module-exports": "^0.2.1",
34
+ "babel-preset-es2015": "^6.18.0",
35
+ "babel-preset-stage-1": "^6.16.0",
36
+ "babel-register": "^6.18.0",
37
+ "eslint": "^3.10.1",
38
+ "eslint-config-airbnb-base": "^10.0.1",
39
+ "eslint-plugin-import": "^2.2.0",
40
+ "nock": "^13.0.7",
41
+ "nyc": "^9.0.1"
42
+ },
43
+ "dependencies": {
44
+ "es6-error": "^4.0.0",
45
+ "isomorphic-fetch": "^3.0.0",
46
+ "qs": "^6.3.0"
47
+ },
48
+ "ava": {
49
+ "require": [
50
+ "babel-register"
51
+ ],
52
+ "babel": "inherit"
53
+ }
54
+ }
@@ -0,0 +1,72 @@
1
+ export default class ResponseWrapper {
2
+ constructor(api) {
3
+ this.api = api;
4
+ }
5
+
6
+ site(site) {
7
+ return {
8
+ ...site,
9
+
10
+ collections: this.api.collections.bind(this.api, { siteId: site._id }),
11
+ webhooks: this.api.webhooks.bind(this.api, { siteId: site._id }),
12
+ domains: this.api.domains.bind(this.api, { siteId: site._id }),
13
+ webhook(first, ...rest) {
14
+ return this.api.webhook({ ...first, siteId: site._id }, ...rest);
15
+ },
16
+ createWebhook(first, ...rest) {
17
+ return this.api.createWebhook({ ...first, siteId: site._id }, ...rest);
18
+ },
19
+ removeWebhook(first, ...rest) {
20
+ return this.api.removeWebhook({ ...first, siteId: site._id }, ...rest);
21
+ },
22
+ publishSite(domains) {
23
+ return this.api.publishSite({ siteId: site._id, domains });
24
+ },
25
+ };
26
+ }
27
+
28
+ domain(domain) {
29
+ return {
30
+ ...domain,
31
+ };
32
+ }
33
+
34
+ collection(collection) {
35
+ return {
36
+ ...collection,
37
+
38
+ items: this.api.items.bind(this.api, { collectionId: collection._id }),
39
+ item(first, ...rest) {
40
+ return this.api.item({ ...first, collectionId: collection._id }, ...rest);
41
+ },
42
+ createItem(first, ...rest) {
43
+ return this.api.createItem({ ...first, collectionId: collection._id }, ...rest);
44
+ },
45
+ updateItem(first, ...rest) {
46
+ return this.api.updateItem({ ...first, collectionId: collection._id }, ...rest);
47
+ },
48
+ removeItem(first, ...rest) {
49
+ return this.api.removeItem({ ...first, collectionId: collection._id }, ...rest);
50
+ },
51
+ };
52
+ }
53
+
54
+ item(item, collectionId) {
55
+ return {
56
+ ...item,
57
+
58
+ update(first, ...rest) {
59
+ return this.api.updateItem({ ...first, collectionId, itemId: item._id }, ...rest);
60
+ },
61
+ remove: this.api.updateItem.bind(this.api, { collectionId, itemId: item._id }),
62
+ };
63
+ }
64
+
65
+ webhook(webhook, siteId) {
66
+ return {
67
+ ...webhook,
68
+
69
+ remove: this.api.removeWebhook.bind(this.api, { siteId, webhookId: webhook._id }),
70
+ };
71
+ }
72
+ }
package/src/Webflow.js ADDED
@@ -0,0 +1,255 @@
1
+ import fetch from 'isomorphic-fetch';
2
+ import qs from 'qs';
3
+
4
+ import { isObjectEmpty } from './utils';
5
+ 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) { return {}; }
12
+
13
+ return {
14
+ rateLimit: {
15
+ limit: parseInt(res.headers.get('x-ratelimit-limit'), 10),
16
+ remaining: parseInt(res.headers.get('x-ratelimit-remaining'), 10),
17
+ },
18
+ };
19
+ };
20
+
21
+ const responseHandler = res =>
22
+ res.json()
23
+ .catch(err =>
24
+ // Catch unexpected server errors where json isn't sent and rewrite
25
+ // with proper class (WebflowError)
26
+ Promise.reject(new WebflowError(err)))
27
+ .then((body) => {
28
+ if (res.status >= 400) {
29
+ const errOpts = {
30
+ code: body.code,
31
+ msg: body.msg,
32
+ _meta: buildMeta(res),
33
+ };
34
+
35
+ if (body.problems && body.problems.length > 0) {
36
+ errOpts.problems = body.problems;
37
+ }
38
+
39
+ const errMsg = (body && body.err) ? body.err : 'Unknown error occured';
40
+ const err = new WebflowError(errMsg);
41
+
42
+ return Promise.reject(Object.assign(err, errOpts));
43
+ }
44
+
45
+ body._meta = buildMeta(res); // eslint-disable-line no-param-reassign
46
+
47
+ return body;
48
+ });
49
+
50
+ export default class Webflow {
51
+ constructor({
52
+ endpoint = DEFAULT_ENDPOINT,
53
+ token,
54
+ version = '1.0.0',
55
+ } = {}) {
56
+ if (!token) throw buildRequiredArgError('token');
57
+
58
+ 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 = query && !isObjectEmpty(query)
72
+ ? `?${qs.stringify(query)}`
73
+ : '';
74
+
75
+ const uri = `${this.endpoint}${path}${queryString}`;
76
+ const opts = {
77
+ method,
78
+ headers: this.headers,
79
+ mode: 'cors',
80
+ };
81
+
82
+ if (data) {
83
+ opts.body = JSON.stringify(data);
84
+ }
85
+
86
+ return fetch(uri, opts)
87
+ .then(responseHandler);
88
+ };
89
+ }
90
+
91
+ // Generic HTTP request handlers
92
+
93
+ get(path, query = {}) {
94
+ return this.authenticatedFetch('GET', path, false, query);
95
+ }
96
+
97
+ post(path, data, query = {}) {
98
+ return this.authenticatedFetch('POST', path, data, query);
99
+ }
100
+
101
+ put(path, data, query = {}) {
102
+ return this.authenticatedFetch('PUT', path, data, query);
103
+ }
104
+
105
+ patch(path, data, query = {}) {
106
+ return this.authenticatedFetch('PATCH', path, data, query);
107
+ }
108
+
109
+ delete(path, query = {}) {
110
+ return this.authenticatedFetch('DELETE', path, query);
111
+ }
112
+
113
+ // Meta
114
+
115
+ info(query = {}) {
116
+ return this.get('/info', query);
117
+ }
118
+
119
+ // Sites
120
+
121
+ sites(query = {}) {
122
+ return this.get('/sites', query).then(sites => sites.map(site => this.responseWrapper.site(site)));
123
+ }
124
+
125
+ site({ siteId }, query = {}) {
126
+ if (!siteId) return Promise.reject(buildRequiredArgError('siteId'));
127
+
128
+ return this.get(`/sites/${siteId}`, query).then(site => this.responseWrapper.site(site));
129
+ }
130
+
131
+ publishSite({ siteId, domains }) {
132
+ if (!siteId) return Promise.reject(buildRequiredArgError('siteId'));
133
+ if (!domains) return Promise.reject(buildRequiredArgError('domains'));
134
+
135
+ return this.post(`/sites/${siteId}/publish`, { domains });
136
+ }
137
+
138
+ // Domains
139
+
140
+ domains({ siteId }) {
141
+ if (!siteId) return Promise.reject(buildRequiredArgError('siteId'));
142
+
143
+ return this.get(`/sites/${siteId}/domains`).then(
144
+ domains => domains.map(domain => this.responseWrapper.domain(domain, siteId)),
145
+ );
146
+ }
147
+
148
+ // Collections
149
+
150
+ collections({ siteId }, query = {}) {
151
+ if (!siteId) return Promise.reject(buildRequiredArgError('siteId'));
152
+
153
+ return this.get(`/sites/${siteId}/collections`, query).then(
154
+ collections => collections.map(collection => this.responseWrapper.collection(collection)),
155
+ );
156
+ }
157
+
158
+ collection({ collectionId }, query = {}) {
159
+ if (!collectionId) return Promise.reject(buildRequiredArgError('collectionId'));
160
+
161
+ return this.get(`/collections/${collectionId}`, query).then(
162
+ collection => this.responseWrapper.collection(collection),
163
+ );
164
+ }
165
+
166
+ // Items
167
+
168
+ items({ collectionId }, query = {}) {
169
+ if (!collectionId) return Promise.reject(buildRequiredArgError('collectionId'));
170
+
171
+ return this.get(`/collections/${collectionId}/items`, query).then(
172
+ res => ({
173
+ ...res,
174
+
175
+ items: res.items.map(item => this.responseWrapper.item(item, collectionId)),
176
+ }),
177
+ );
178
+ }
179
+
180
+ item({ collectionId, itemId }, query = {}) {
181
+ if (!collectionId) return Promise.reject(buildRequiredArgError('collectionId'));
182
+ if (!itemId) return Promise.reject(buildRequiredArgError('siteId'));
183
+
184
+ return this.get(`/collections/${collectionId}/items/${itemId}`, query).then(
185
+ res => this.responseWrapper.item(res.items[0], collectionId),
186
+ );
187
+ }
188
+
189
+ createItem({ collectionId, ...data }, query = {}) {
190
+ if (!collectionId) return Promise.reject(buildRequiredArgError('collectionId'));
191
+
192
+ return this.post(`/collections/${collectionId}/items`, data, query).then(
193
+ item => this.responseWrapper.item(item, collectionId),
194
+ );
195
+ }
196
+
197
+ updateItem({ collectionId, itemId, ...data }, query = {}) {
198
+ if (!collectionId) return Promise.reject(buildRequiredArgError('collectionId'));
199
+ if (!itemId) return Promise.reject(buildRequiredArgError('itemId'));
200
+
201
+ return this.put(`/collections/${collectionId}/items/${itemId}`, data, query);
202
+ }
203
+
204
+ removeItem({ collectionId, itemId }, query = {}) {
205
+ if (!collectionId) return Promise.reject(buildRequiredArgError('collectionId'));
206
+ if (!itemId) return Promise.reject(buildRequiredArgError('itemId'));
207
+
208
+ return this.delete(`/collections/${collectionId}/items/${itemId}`, query);
209
+ }
210
+
211
+ patchItem({ collectionId, itemId, ...data }, query = {}) {
212
+ if (!collectionId) return Promise.reject(buildRequiredArgError('collectionId'));
213
+ if (!itemId) return Promise.reject(buildRequiredArgError('itemId'));
214
+
215
+ return this.patch(`/collections/${collectionId}/items/${itemId}`, data, query);
216
+ }
217
+
218
+ // Images
219
+
220
+ // TODO
221
+
222
+ // Webhooks
223
+
224
+ webhooks({ siteId }, query = {}) {
225
+ if (!siteId) return Promise.reject(buildRequiredArgError('siteId'));
226
+
227
+ return this.get(`/sites/${siteId}/webhooks`, query).then(
228
+ webhooks => webhooks.map(webhook => this.responseWrapper.webhook(webhook, siteId)),
229
+ );
230
+ }
231
+
232
+ webhook({ siteId, webhookId }, query = {}) {
233
+ if (!siteId) return Promise.reject(buildRequiredArgError('siteId'));
234
+ if (!webhookId) return Promise.reject(buildRequiredArgError('webhookId'));
235
+
236
+ return this.get(`/sites/${siteId}/webhooks/${webhookId}`, query).then(
237
+ webhook => this.responseWrapper.webhook(webhook, siteId),
238
+ );
239
+ }
240
+
241
+ createWebhook({ siteId, ...data }, query = {}) {
242
+ if (!siteId) return Promise.reject(buildRequiredArgError('siteId'));
243
+
244
+ return this.post(`/sites/${siteId}/webhooks`, data, query).then(
245
+ webhook => this.responseWrapper.webhook(webhook, siteId),
246
+ );
247
+ }
248
+
249
+ removeWebhook({ siteId, webhookId }, query = {}) {
250
+ if (!siteId) return Promise.reject(buildRequiredArgError('siteId'));
251
+ if (!webhookId) return Promise.reject(buildRequiredArgError('webhookId'));
252
+
253
+ return this.delete(`/sites/${siteId}/webhooks/${webhookId}`, query);
254
+ }
255
+ }
@@ -0,0 +1,6 @@
1
+ import ExtendableError from 'es6-error';
2
+
3
+ export default class WebflowError extends ExtendableError {}
4
+
5
+ export const buildRequiredArgError = name =>
6
+ new WebflowError(`Argument '${name}' is required but was not present`);
package/src/index.js ADDED
@@ -0,0 +1,2 @@
1
+ module.exports = require('./Webflow');
2
+ module.exports.WebflowError = require('./WebflowError').default;
package/src/utils.js ADDED
@@ -0,0 +1,13 @@
1
+ export const isObjectEmpty = obj => Object.keys(obj).length === 0;
2
+
3
+ export const pick = (obj, ...props) => {
4
+ const picked = {};
5
+
6
+ props.forEach((prop) => {
7
+ if (obj[prop] !== undefined) {
8
+ picked[prop] = obj[prop];
9
+ }
10
+ });
11
+
12
+ return picked;
13
+ };