netlify 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ t.pattern = "test/*_test.rb"
7
+ end
@@ -0,0 +1,17 @@
1
+ require "json"
2
+ require "netlify/version"
3
+ require "netlify/client"
4
+ require "netlify/collection_proxy"
5
+ require "netlify/model"
6
+ require "netlify/sites"
7
+ require "netlify/forms"
8
+ require "netlify/submissions"
9
+ require "netlify/files"
10
+ require "netlify/snippets"
11
+ require "netlify/users"
12
+ require "netlify/deploys"
13
+ require "netlify/dns_zones"
14
+ require "netlify/access_tokens"
15
+ require "netlify/multipass"
16
+
17
+ module Netlify; end
@@ -0,0 +1,5 @@
1
+ module Netlify
2
+ class AccessToken < Model
3
+ fields :id, :access_token, :user_id, :created_at
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ require "Netlify/access_token"
2
+
3
+ module Netlify
4
+ class AccessTokens < CollectionProxy
5
+ path "/access_tokens"
6
+ end
7
+ end
@@ -0,0 +1,113 @@
1
+ require 'oauth2'
2
+
3
+ module Netlify
4
+ class Client
5
+ ENDPOINT = ENV['OAUTH_CLIENT_API_URL'] || 'https://api.netlify.com'
6
+ API_VERSION = "v1"
7
+ RETRIES = 3
8
+
9
+ class NetlifyError < StandardError; end
10
+ class NotFoundError < NetlifyError; end
11
+ class ConnectionError < NetlifyError; end
12
+ class InternalServerError < NetlifyError; end
13
+ class AuthenticationError < NetlifyError; end
14
+
15
+ attr_accessor :client_id, :client_secret, :oauth, :access_token, :endpoint
16
+
17
+ def initialize(options)
18
+ self.client_id = options[:client_id]
19
+ self.client_secret = options[:client_secret]
20
+ self.access_token = options[:access_token]
21
+ self.endpoint = options[:endpoint] || ENDPOINT
22
+ self.oauth = OAuth2::Client.new(client_id, client_secret, :site => endpoint, :connection_build => lambda {|f|
23
+ f.request :multipart
24
+ f.request :url_encoded
25
+ f.adapter :net_http
26
+ })
27
+ end
28
+
29
+ def authorize_url(options)
30
+ oauth.auth_code.authorize_url(options)
31
+ end
32
+
33
+ def authorize_from_code!(authorization_code, options)
34
+ @oauth_token = oauth.auth_code.get_token(authorization_code, options)
35
+ self.access_token = oauth_token.token
36
+ end
37
+
38
+ def authorize_from_credentials!
39
+ @oauth_token = oauth.client_credentials.get_token
40
+ self.access_token = oauth_token.token
41
+ end
42
+
43
+ def sites
44
+ Sites.new(self)
45
+ end
46
+
47
+ def forms
48
+ Forms.new(self)
49
+ end
50
+
51
+ def submissions
52
+ Submissions.new(self)
53
+ end
54
+
55
+ def users
56
+ Users.new(self)
57
+ end
58
+
59
+ def dns_zones
60
+ DnsZones.new(self)
61
+ end
62
+
63
+ def access_tokens
64
+ AccessTokens.new(self)
65
+ end
66
+
67
+ def request(verb, path, opts={}, &block)
68
+ retries = 0
69
+ begin
70
+ raise AuthenticationError, "Authorize with Netlify before making requests" unless oauth_token
71
+
72
+ oauth_token.request(verb, ::File.join("/api", API_VERSION, path), opts, &block)
73
+ rescue OAuth2::Error => e
74
+ case e.response.status
75
+ when 401
76
+ raise AuthenticationError, message_for(e, "Authentication Error")
77
+ when 404
78
+ raise NotFoundError, message_for(e, "Not Found")
79
+ when 500
80
+ if retry_request?(verb, e.response.status, retries)
81
+ retries += 1
82
+ retry
83
+ else
84
+ raise InternalServerError, message_for(e, "Internal Server Error")
85
+ end
86
+ else
87
+ raise NetlifyError, message_for(e, "OAuth2 Error")
88
+ end
89
+ rescue Faraday::Error::ConnectionFailed, Faraday::Error::TimeoutError => e
90
+ if retry_request?(verb, e.response && e.response.status, retries)
91
+ retries += 1
92
+ retry
93
+ else
94
+ raise ConnectionError, message_for(e, "Connection Error")
95
+ end
96
+ end
97
+ end
98
+
99
+ private
100
+ def retry_request?(http_verb, status_code, retries)
101
+ return false unless [:get, :put, :delete, :head].include?(http_verb.to_s.downcase.to_sym)
102
+ return retries < 3 unless status_code && status_code == 422
103
+ end
104
+
105
+ def oauth_token
106
+ @oauth_token ||= access_token && OAuth2::AccessToken.new(oauth, access_token)
107
+ end
108
+
109
+ def message_for(error, default)
110
+ error.message.strip == "" ? default : error.message
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,48 @@
1
+ module Netlify
2
+ class CollectionProxy
3
+ include Enumerable
4
+
5
+ attr_accessor :client, :prefix
6
+
7
+ def self.path(value = nil)
8
+ return @path unless value
9
+ @path = value
10
+ end
11
+
12
+ def self.model(value = nil)
13
+ @model ||= Netlify.const_get(to_s.split("::").last.sub(/s$/, ''))
14
+ end
15
+
16
+ def initialize(client, prefix = nil)
17
+ self.client = client
18
+ self.prefix = prefix
19
+ end
20
+
21
+ def all(options = {})
22
+ response = client.request(:get, path, {:params => options})
23
+ response.parsed.map {|attributes| model.new(client, attributes.merge(:prefix => prefix)) } if response.parsed
24
+ end
25
+
26
+ def each(&block)
27
+ all.each(&block)
28
+ end
29
+
30
+ def get(id)
31
+ response = client.request(:get, ::File.join(path, id))
32
+ model.new(client, response.parsed.merge(:prefix => prefix)) if response.parsed
33
+ end
34
+
35
+ def create(attributes)
36
+ response = client.request(:post, path, :body => attributes)
37
+ model.new(client, response.parsed.merge(:prefix => prefix)) if response.parsed
38
+ end
39
+
40
+ def model
41
+ self.class.model
42
+ end
43
+
44
+ def path
45
+ [prefix, self.class.path].compact.join("/")
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,58 @@
1
+ module Netlify
2
+ class Deploy < Model
3
+ fields :id, :state, :premium, :claimed, :name, :custom_domain, :url,
4
+ :admin_url, :deploy_url, :screenshot_url, :created_at, :updated_at,
5
+ :user_id, :required
6
+
7
+ def upload_dir(dir)
8
+ return unless state == "uploading"
9
+
10
+ shas = Hash.new { [] }
11
+ glob = ::File.join(dir, "**", "*")
12
+
13
+ Dir.glob(glob) do |file|
14
+ next unless ::File.file?(file)
15
+ pathname = ::File.join("/", file[dir.length..-1])
16
+ next if pathname.match(/(^\/?__MACOSX\/|\/\.)/)
17
+ sha = Digest::SHA1.hexdigest(::File.read(file))
18
+ shas[sha] = shas[sha] + [pathname]
19
+ end
20
+
21
+ (required || []).each do |sha|
22
+ shas[sha].each do |pathname|
23
+ client.request(:put, ::File.join(path, "files", URI.encode(pathname)), :body => ::File.read(::File.join(dir, pathname)), :headers => {"Content-Type" => "application/octet-stream"})
24
+ end
25
+ end
26
+
27
+ refresh
28
+ end
29
+
30
+ def wait_for_ready(timeout = 900)
31
+ start = Time.now
32
+ while !(ready?)
33
+ sleep 5
34
+ refresh
35
+ puts "Got state: #{state}"
36
+ raise "Error processing site: #{error_message}" if error?
37
+ yield(self) if block_given?
38
+ raise "Timeout while waiting for ready" if Time.now - start > timeout
39
+ end
40
+ self
41
+ end
42
+
43
+ def ready?
44
+ state == "ready"
45
+ end
46
+
47
+ def error?
48
+ state == "error"
49
+ end
50
+
51
+ def publish
52
+ response = client.request(:post, ::File.join(path, "restore"))
53
+ process(response.parsed)
54
+ self
55
+ end
56
+ alias :restore :publish
57
+ end
58
+ end
@@ -0,0 +1,43 @@
1
+ require "Netlify/deploy"
2
+
3
+ module Netlify
4
+ class Deploys < CollectionProxy
5
+ path "/deploys"
6
+
7
+ def create(attributes)
8
+ if attributes[:dir]
9
+ response = client.request(:post, path,
10
+ :body => JSON.generate({:files => inventory(attributes[:dir]), :draft => attributes[:draft] || false}),
11
+ :headers => {"Content-Type" => "application/json"}
12
+ )
13
+ Deploy.new(client, response.parsed).tap do |deploy|
14
+ deploy.upload_dir(attributes[:dir])
15
+ end
16
+ elsif attributes[:zip]
17
+ request_path = attributes[:draft] ? "#{path}?draft=true" : path
18
+ response = client.request(:post, request_path,
19
+ :body => ::File.read(attributes[:zip]),
20
+ :headers => {"Content-Type" => "application/zip"}
21
+ )
22
+ Deploy.new(client, response.parsed)
23
+ else
24
+ raise "Need dir or zip to create a deploy"
25
+ end
26
+ end
27
+
28
+ def draft(attributes)
29
+ create(attributes.merge(:draft => true))
30
+ end
31
+
32
+ private
33
+ def inventory(dir)
34
+ files = {}
35
+ Dir[::File.join(dir, "**", "*")].each do |file|
36
+ next unless ::File.file?(file)
37
+ path = ::File.join("/", file[dir.length..-1])
38
+ files[path] = Digest::SHA1.hexdigest(::File.read(file))
39
+ end
40
+ files
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,5 @@
1
+ module Netlify
2
+ class DnsRecord < Model
3
+ fields :id, :hostname, :type, :value, :ttl, :domain_id
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ require "Netlify/dns_record"
2
+
3
+ module Netlify
4
+ class DnsRecords < CollectionProxy
5
+ path "/dns_records"
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ require "Netlify/dns_records"
2
+
3
+ module Netlify
4
+ class DnsZone < Model
5
+ fields :id, :name, :user_id, :created_at, :updated_at
6
+
7
+ def dns_records
8
+ DnsRecords.new(client, path)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require "Netlify/dns_zone"
2
+
3
+ module Netlify
4
+ class DnsZones < CollectionProxy
5
+ path "/dns_zones"
6
+
7
+ def dns_records
8
+ DnsRecords.new(client, path)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ module Netlify
2
+ class File < Model
3
+ fields :id, :path, :sha, :mime_type, :size
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ require "Netlify/file"
2
+
3
+ module Netlify
4
+ class Files < CollectionProxy
5
+ path "/files"
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ module Netlify
2
+ class Form < Model
3
+ fields :id, :site_id, :name, :paths, :submission_count, :fields, :created_at
4
+
5
+ def submissions
6
+ Submissions.new(client, path)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ require "Netlify/form"
2
+
3
+ module Netlify
4
+ class Forms < CollectionProxy
5
+ path "/forms"
6
+ end
7
+ end
@@ -0,0 +1,65 @@
1
+ module Netlify
2
+ class Model
3
+ attr_reader :client, :attributes, :prefix
4
+
5
+ def self.fields(*names)
6
+ return @fields if names.empty?
7
+
8
+ @fields ||= []
9
+
10
+ names.each do |name|
11
+ define_method name do
12
+ @attributes[name.to_sym]
13
+ end
14
+
15
+ define_method "#{name}=" do |value|
16
+ @attributes[name.to_sym] = value
17
+ end
18
+
19
+ @fields.push(name.to_sym)
20
+ end
21
+ end
22
+
23
+ def self.collection(value = nil)
24
+ @collection ||= Netlify.const_get(to_s.split("::").last + "s")
25
+ end
26
+
27
+ def initialize(client, attributes)
28
+ @client = client
29
+ @attributes = {}
30
+ @prefix = attributes.delete(:prefix)
31
+ process(attributes)
32
+ end
33
+
34
+ def process(attributes)
35
+ self.class.fields.each do |field|
36
+ if attributes.has_key?(field) || attributes.has_key?(field.to_s)
37
+ @attributes[field] = attributes[field] || attributes[field.to_s]
38
+ end
39
+ end
40
+ self
41
+ end
42
+
43
+ def update(attributes)
44
+ response = client.request(:put, path, :body => attributes)
45
+ process(response.parsed) if response.parsed
46
+ end
47
+
48
+ def destroy
49
+ client.request(:delete, path)
50
+ end
51
+
52
+ def refresh
53
+ response = client.request(:get, path)
54
+ process(response.parsed)
55
+ end
56
+
57
+ def collection
58
+ self.class.collection
59
+ end
60
+
61
+ def path
62
+ ::File.join(*[prefix, collection.path, id.to_s].compact)
63
+ end
64
+ end
65
+ end