UNPKG

netlify

Version:
369 lines (316 loc) 12.4 kB
var base64 = require('base64-js'), when = require("when"), nodefn = require("when/node"), fn = require("when/function"), http = require('http'), https = require('https'); var Client = function(options) { options = options || {}; this.access_token = options.access_token; this.client_id = options.client_id; this.client_secret = options.client_secret; this.redirect_uri = options.redirect_uri; this.ENDPOINT = options.endpoint || 'https://api.netlify.com'; this.VERSION = options.version || 'v1'; this.hostname = this.ENDPOINT.match(/^https?:\/\/([^:]+)/)[1]; this.ssl = this.ENDPOINT.match(/^https:\/\//) this.port = this.ssl ? 443 : (this.ENDPOINT.match(/:(\d+)$/) || [])[1] || 80; this.http = options.http || (this.ssl ? https : http); }; Client.models = { Site: require("./site").Site, Form: require("./form").Form, Submission: require("./submission").Submission, User: require("./user").User, Snippet: require("./snippet").Snippet, Deploy: require("./deploy").Deploy, DeployKey: require("./deploy_key").DeployKey, DnsZone: require("./dns-zone").DnsZone, AccessToken: require("./access-token").AccessToken, Ticket: require("./ticket").Ticket }; var stringToByteArray = function(str) { return Array.prototype.map.call(str, function (char) { return char.charCodeAt(0); }); }; var prepareBody = function(body, headers) { return typeof(body) == "string" ? body : (headers['Content-Type'] == "application/json" ? JSON.stringify(body) : body); }; Client.prototype = { isAuthorized: function() { return !(this.access_token == null); }, authorizeFromCredentials: function() { var client = this; if (!(this.client_id && this.client_secret)) { throw("Instantiate the client with a client_id and client_secret to authorize from credentials"); } return this.request({ type: "post", url: "/oauth/token", raw_path: true, contentType: "application/x-www-form-urlencoded", auth: { user: this.client_id, password: this.client_secret }, body: "grant_type=client_credentials" }).then(function(response) { client.access_token = response.data.access_token; return response.data.access_token; }).catch(function(response) { return when.reject(response.data); }); }, authorizeFromCode: function(code) { var client = this; if (!(this.client_id && this.client_secret && this.redirect_uri)) { throw("Instantiate the client with a client_id, client_secret and redirect_uri to authorize from code"); } return this.request({ type: "post", url: "/oauth/token", raw_path: true, contentType: "application/x-www-form-urlencoded", auth: { user: this.client_id, password: this.client_secret }, body: [ "grant_type=authorization_code", "code=" + code, "redirect_uri=" + encodeURIComponent(this.redirect_uri) ].join("&") }).then(function(response) { client.access_token = response.data.access_token; return response.data.access_token; }).catch(function(response) { return when.reject(response.data); }); }, authorizeUrl: function(options) { if (!(this.client_id && this.redirect_uri)) { throw("Instantiate the client with a client_id and a redirect_uri to generate an authorizeUrl"); } return this.ENDPOINT + "/oauth/authorize?" + [ "response_type=code", "client_id=" + this.client_id, "redirect_uri=" + encodeURIComponent(this.redirect_uri) ].join("&"); }, sites: function(options) { return this.collection({model: Client.models.Site}, options); }, site: function(id) { return this.element({model: Client.models.Site, id: id}); }, createSite: function(options) { return this.withAuthorization().then(function(client) { if (options.dir) { return Client.models.Site.createFromDir(client, options.dir); } else if (options.zip) { return Client.models.Site.createFromZip(client, options.zip); } else { return Client.models.Site.create(client, options); } }); }, forms: function() { return this.collection({model: Client.models.Form}); }, form: function(id) { return this.element({model: Client.models.Form, id: id}); }, submissions: function(options) { return this.collection({model: Client.models.Submission}, options); }, submission: function(id) { return this.element({model: Client.models.Submission, id: id}); }, users: function(options) { return this.collection({model: Client.models.User}, options); }, user: function(id) { return this.element({model: Client.models.User, id: id}); }, createUser: function(attributes) { return this.create({model: Client.models.User, attributes: attributes}); }, dnsZones: function(options) { return this.collection({model: Client.models.DnsZone}, options); }, dnsZone: function(id) { return this.element({model: Client.models.DnsZone, id: id}); }, createDnsZone: function(attributes) { return this.create({model: Client.models.DnsZone, attributes: attributes}); }, accessToken: function(id) { return this.element({model: Client.models.AccessToken, id: id}); }, createAccessToken: function(attributes) { return this.create({model: Client.models.AccessToken, attributes: attributes}); }, createDeployKey: function(attributes) { return this.create({model: Client.models.DeployKey, attributes: attributes}); }, ticket: function(id) { return this.element({model: Client.models.Ticket, id: id}); }, createTicket: function() { return this.request({ url: Client.models.Ticket.path, type: "post", body: {client_id: this.client_id} }).then(function(response) { return new Client.models.Ticket(response.client, response.data); }); }, collection: function(options, meta) { meta = meta || {}; var params = []; for (var key in meta.params || {}) { params.push(key + "=" + meta.params[key]); } if (meta.page) { params.push(["page", meta.page].join("=")) } if (meta.per_page) { params.push(["per_page", meta.per_page].join("=")) } return this.withAuthorization().then(function(client) { return client.request({ url: (options.prefix || "") + options.model.path + (params.length ? "?" + params.join("&") : "") }).then(function(response) { var result = response.data.map(function(data) { return new options.model(response.client, data); }); result.meta = response.meta return result; }); }); }, element: function(options) { return this.withAuthorization().then(function(client) { return client.request({ url: (options.prefix || "" ) + options.model.path + "/" + options.id }).then(function(response) { return new options.model(response.client, response.data); }); }); }, create: function(options) { return this.withAuthorization().then(function(client) { return client.request({ url: (options.prefix || "") + options.model.path, type: "post", body: options.attributes }).then(function(response) { return new options.model(response.client, response.data); }); }); }, update: function(options) { return this.withAuthorization().then(function(client) { return client.request({ url: (options.prefix || "") + options.element.apiPath, type: "put", body: options.attributes }).then(function(response) { options.model.call(options.element, response.client, response.data); return options.element; }); }); }, destroy: function(options) { return this.withAuthorization().then(function(client) { return client.request({ url: (options.prefix || "") + options.element.apiPath, type: "delete", ignoreResponse: true }); }); }, request: function(options) { var client = this, http = this.http, path = options.raw_path ? options.url : "/api/" + this.VERSION + options.url, headers = options.headers || {}, retries = options.retries ? options.retries : options.retries === 0 ? 0 : 3, body = null; headers['Content-Type'] = options.contentType || "application/json"; if (options.body) { body = prepareBody(options.body, headers); } headers['Content-Length'] = body ? (typeof body == "string" ? Buffer.byteLength(body) : body.length) : 0; if (this.access_token && !options.auth) { headers['Authorization'] = "Bearer " + this.access_token } var requestOptions = { method: (options.type || "get").toLowerCase(), hostname: this.hostname, path: path, port: this.port, headers: headers, auth: options.auth ? options.auth.user + ":" + options.auth.password : null, } return when.promise(function(resolve, reject) { var request = http.request(requestOptions, function(res) { var body = "", data = null; res.on("data", function(chunk) { body += chunk; }); res.on("end", function() { if (res.statusCode >= 200 && res.statusCode < 300) { if (body && !options.ignoreResponse) { data = JSON.parse(body); } resolve({client: client, data: data, meta: client.metadata(res)}); } else if (res.statusCode == 401 || res.statusCode == 403) { reject({data: "Authentication failed", client: client, meta: client.metadata(res)}); } else { if ((requestOptions.method === "get" || requestOptions.method === "put" || requestOptions.method === "delete") && (retries > 0 && res.statusCode !== 422 && res.statusCode !== 404)) { options.retries = retries - 1; setTimeout(function() { client.request(options).then(resolve).catch(reject); }, 500); } else { reject({client: client, data: body, meta: client.metadata(res)}); } } }); }); request.setTimeout(300000, function() { request.abort(); }); request.on("error", function(err) { if ((requestOptions.method == "get" || requestOptions.method == "put" || requestOptions.method == "delete") && retries > 0) { options.retries = retries - 1; setTimeout(function() { client.request(options).then(resolve).catch(reject); }, 500); } else { reject({client: client, data: err, meta: null}); } }); if (body) { request.write(body); } request.end(); }); }, withAuthorization: function() { if (!this.isAuthorized()) return when.reject("Not authorized: Instantiate client with access_token"); return when.resolve(this); }, metadata: function(res) { var meta = {}, links = res.headers && res.headers.link, limit = res.headers && res.headers["x-ratelimit-limit"], remaining = res.headers && res.headers["x-ratelimit-remaining"], reset = res.headers && res.headers["x-ratelimit-reset"]; if (links) { meta.pagination = {}; var parts = links.split(","); for (var i=0, len=parts.length; i<len; i++) { var part = parts[i]; var link = part.replace(/(^\s*|\s*$)/, ''); var segments = link.split(";"); var m = segments[0].match(/page=(\d+)/); var page = m && parseInt(m[1],10); m = segments[0].match(/^\<(.+)\>\s*$/); if (segments[1].match(/last/)) { meta.pagination.last = page; } else if (segments[1].match(/next/)) { meta.pagination.next = page; } else if (segments[1].match(/prev/)) { meta.pagination.prev = page; } else if (segments[1].match(/first/)) { meta.pagination.first = page; } } } meta.rate = {}; if (limit) { meta.rate.limit = parseInt(limit, 10); } if (remaining) { meta.rate.remaining = parseInt(remaining, 10); } if (reset) { meta.rate.reset = parseInt(reset, 10); } return meta; } }; exports.Client = Client;