azuki-api 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.travis.yml +16 -0
  4. data/Gemfile +4 -0
  5. data/README.md +128 -0
  6. data/Rakefile +41 -0
  7. data/azuki-api.gemspec +23 -0
  8. data/changelog.txt +204 -0
  9. data/lib/azuki-api.rb +1 -0
  10. data/lib/azuki/api.rb +146 -0
  11. data/lib/azuki/api/addons.rb +48 -0
  12. data/lib/azuki/api/apps.rb +71 -0
  13. data/lib/azuki/api/attachments.rb +14 -0
  14. data/lib/azuki/api/collaborators.rb +33 -0
  15. data/lib/azuki/api/config_vars.rb +33 -0
  16. data/lib/azuki/api/domains.rb +42 -0
  17. data/lib/azuki/api/errors.rb +26 -0
  18. data/lib/azuki/api/features.rb +45 -0
  19. data/lib/azuki/api/keys.rb +42 -0
  20. data/lib/azuki/api/login.rb +14 -0
  21. data/lib/azuki/api/logs.rb +18 -0
  22. data/lib/azuki/api/mock.rb +179 -0
  23. data/lib/azuki/api/mock/addons.rb +153 -0
  24. data/lib/azuki/api/mock/apps.rb +205 -0
  25. data/lib/azuki/api/mock/attachments.rb +19 -0
  26. data/lib/azuki/api/mock/cache/get_addons.json +1 -0
  27. data/lib/azuki/api/mock/cache/get_features.json +1 -0
  28. data/lib/azuki/api/mock/cache/get_user.json +1 -0
  29. data/lib/azuki/api/mock/collaborators.rb +55 -0
  30. data/lib/azuki/api/mock/config_vars.rb +49 -0
  31. data/lib/azuki/api/mock/domains.rb +80 -0
  32. data/lib/azuki/api/mock/features.rb +120 -0
  33. data/lib/azuki/api/mock/keys.rb +46 -0
  34. data/lib/azuki/api/mock/login.rb +22 -0
  35. data/lib/azuki/api/mock/logs.rb +20 -0
  36. data/lib/azuki/api/mock/processes.rb +198 -0
  37. data/lib/azuki/api/mock/releases.rb +69 -0
  38. data/lib/azuki/api/mock/stacks.rb +83 -0
  39. data/lib/azuki/api/mock/user.rb +16 -0
  40. data/lib/azuki/api/processes.rb +77 -0
  41. data/lib/azuki/api/releases.rb +33 -0
  42. data/lib/azuki/api/ssl_endpoints.rb +62 -0
  43. data/lib/azuki/api/stacks.rb +22 -0
  44. data/lib/azuki/api/user.rb +14 -0
  45. data/lib/azuki/api/vendor/okjson.rb +600 -0
  46. data/lib/azuki/api/version.rb +5 -0
  47. data/test/data/site.crt +19 -0
  48. data/test/data/site.key +27 -0
  49. data/test/test_addons.rb +193 -0
  50. data/test/test_apps.rb +147 -0
  51. data/test/test_attachments.rb +23 -0
  52. data/test/test_collaborators.rb +73 -0
  53. data/test/test_config_vars.rb +54 -0
  54. data/test/test_domains.rb +65 -0
  55. data/test/test_error_conditions.rb +11 -0
  56. data/test/test_features.rb +87 -0
  57. data/test/test_helper.rb +48 -0
  58. data/test/test_keys.rb +39 -0
  59. data/test/test_login.rb +20 -0
  60. data/test/test_logs.rb +29 -0
  61. data/test/test_processes.rb +245 -0
  62. data/test/test_releases.rb +73 -0
  63. data/test/test_ssl_endpoints.rb +132 -0
  64. data/test/test_stacks.rb +49 -0
  65. data/test/test_user.rb +13 -0
  66. metadata +168 -0
@@ -0,0 +1,48 @@
1
+ module Azuki
2
+ class API
3
+
4
+ # DELETE /apps/:app/addons/:addon
5
+ def delete_addon(app, addon)
6
+ request(
7
+ :expects => 200,
8
+ :method => :delete,
9
+ :path => "/apps/#{app}/addons/#{addon}"
10
+ )
11
+ end
12
+
13
+ # GET /addons
14
+ # GET /apps/:app/addons
15
+ def get_addons(app=nil)
16
+ path = if app
17
+ "/apps/#{app}/addons"
18
+ else
19
+ "/addons"
20
+ end
21
+ request(
22
+ :expects => 200,
23
+ :method => :get,
24
+ :path => path
25
+ )
26
+ end
27
+
28
+ # POST /apps/:app/addons/:addon
29
+ def post_addon(app, addon, config = {})
30
+ request(
31
+ :expects => 200,
32
+ :method => :post,
33
+ :path => "/apps/#{app}/addons/#{addon}",
34
+ :query => addon_params(config)
35
+ )
36
+ end
37
+
38
+ # PUT /apps/:app/addons/:addon
39
+ def put_addon(app, addon, config = {})
40
+ request(
41
+ :expects => 200,
42
+ :method => :put,
43
+ :path => "/apps/#{app}/addons/#{addon}",
44
+ :query => addon_params(config)
45
+ )
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,71 @@
1
+ module Azuki
2
+ class API
3
+
4
+ # DELETE /apps/:app
5
+ def delete_app(app)
6
+ request(
7
+ :expects => 200,
8
+ :method => :delete,
9
+ :path => "/apps/#{app}"
10
+ )
11
+ end
12
+
13
+ # GET /apps
14
+ def get_apps
15
+ request(
16
+ :expects => 200,
17
+ :method => :get,
18
+ :path => "/apps"
19
+ )
20
+ end
21
+
22
+ # GET /apps/:app
23
+ def get_app(app)
24
+ request(
25
+ :expects => 200,
26
+ :method => :get,
27
+ :path => "/apps/#{app}"
28
+ )
29
+ end
30
+
31
+ # GET /apps/:app/server/maintenance
32
+ def get_app_maintenance(app)
33
+ request(
34
+ :expects => 200,
35
+ :method => :get,
36
+ :path => "/apps/#{app}/server/maintenance"
37
+ )
38
+ end
39
+
40
+ # POST /apps
41
+ def post_app(params={})
42
+ request(
43
+ :expects => 202,
44
+ :method => :post,
45
+ :path => '/apps',
46
+ :query => app_params(params)
47
+ )
48
+ end
49
+
50
+ # POST /apps/:app/server/maintenance
51
+ def post_app_maintenance(app, maintenance_mode)
52
+ request(
53
+ :expects => 200,
54
+ :method => :post,
55
+ :path => "/apps/#{app}/server/maintenance",
56
+ :query => {'maintenance_mode' => maintenance_mode}
57
+ )
58
+ end
59
+
60
+ # PUT /apps/:app
61
+ def put_app(app, params)
62
+ request(
63
+ :expects => 200,
64
+ :method => :put,
65
+ :path => "/apps/#{app}",
66
+ :query => app_params(params)
67
+ )
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,14 @@
1
+ module Azuki
2
+ class API
3
+
4
+ # GET /apps/:app/attachments
5
+ def get_attachments(app)
6
+ request(
7
+ :expects => 200,
8
+ :method => :get,
9
+ :path => "/apps/#{app}/attachments"
10
+ )
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,33 @@
1
+ module Azuki
2
+ class API
3
+
4
+ # DELETE /apps/:app/collaborators/:email
5
+ def delete_collaborator(app, email)
6
+ request(
7
+ :expects => 200,
8
+ :method => :delete,
9
+ :path => "/apps/#{app}/collaborators/#{email}"
10
+ )
11
+ end
12
+
13
+ # GET /apps/:app/collaborators
14
+ def get_collaborators(app)
15
+ request(
16
+ :expects => 200,
17
+ :method => :get,
18
+ :path => "/apps/#{app}/collaborators"
19
+ )
20
+ end
21
+
22
+ # POST /apps/:app/collaborators
23
+ def post_collaborator(app, email)
24
+ request(
25
+ :expects => [200, 201],
26
+ :method => :post,
27
+ :path => "/apps/#{app}/collaborators",
28
+ :query => {'collaborator[email]' => email}
29
+ )
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ module Azuki
2
+ class API
3
+
4
+ # DELETE /apps/:app/config_vars/:key
5
+ def delete_config_var(app, key)
6
+ request(
7
+ :expects => 200,
8
+ :method => :delete,
9
+ :path => "/apps/#{app}/config_vars/#{escape(key)}"
10
+ )
11
+ end
12
+
13
+ # GET /apps/:app/config_vars
14
+ def get_config_vars(app)
15
+ request(
16
+ :expects => 200,
17
+ :method => :get,
18
+ :path => "/apps/#{app}/config_vars"
19
+ )
20
+ end
21
+
22
+ # PUT /apps/:app/config_vars
23
+ def put_config_vars(app, vars)
24
+ request(
25
+ :body => Azuki::API::OkJson.encode(vars),
26
+ :expects => 200,
27
+ :method => :put,
28
+ :path => "/apps/#{app}/config_vars"
29
+ )
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,42 @@
1
+ module Azuki
2
+ class API
3
+
4
+ # DELETE /apps/:app/domains/:domain
5
+ def delete_domain(app, domain)
6
+ request(
7
+ :expects => 200,
8
+ :method => :delete,
9
+ :path => "/apps/#{app}/domains/#{escape(domain)}"
10
+ )
11
+ end
12
+
13
+ # DELETE /apps/:app/domains
14
+ def delete_domains(app)
15
+ request(
16
+ :expects => 200,
17
+ :method => :delete,
18
+ :path => "/apps/#{app}/domains"
19
+ )
20
+ end
21
+
22
+ # GET /apps/:app/domains
23
+ def get_domains(app)
24
+ request(
25
+ :expects => 200,
26
+ :method => :get,
27
+ :path => "/apps/#{app}/domains"
28
+ )
29
+ end
30
+
31
+ # POST /apps/:app/domains
32
+ def post_domain(app, domain)
33
+ request(
34
+ :expects => 201,
35
+ :method => :post,
36
+ :path => "/apps/#{app}/domains",
37
+ :query => {'domain_name[domain]' => domain}
38
+ )
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,26 @@
1
+ module Azuki
2
+ class API
3
+ module Errors
4
+ class Error < StandardError; end
5
+
6
+ class ErrorWithResponse < Error
7
+ attr_reader :response
8
+
9
+ def initialize(message, response)
10
+ super message
11
+ @response = response
12
+ end
13
+ end
14
+
15
+ class Unauthorized < ErrorWithResponse; end
16
+ class VerificationRequired < ErrorWithResponse; end
17
+ class Forbidden < ErrorWithResponse; end
18
+ class NotFound < ErrorWithResponse; end
19
+ class Timeout < ErrorWithResponse; end
20
+ class Locked < ErrorWithResponse; end
21
+ class RequestFailed < ErrorWithResponse; end
22
+ class NilApp < ErrorWithResponse; end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,45 @@
1
+ module Azuki
2
+ class API
3
+
4
+ # DELETE /features/:feature
5
+ def delete_feature(feature, app = nil)
6
+ request(
7
+ :expects => 200,
8
+ :method => :delete,
9
+ :path => "/features/#{feature}",
10
+ :query => { 'app' => app }
11
+ )
12
+ end
13
+
14
+ # GET /features
15
+ def get_features(app = nil)
16
+ request(
17
+ :expects => 200,
18
+ :method => :get,
19
+ :path => "/features",
20
+ :query => { 'app' => app }
21
+ )
22
+ end
23
+
24
+ # GET /features/:feature
25
+ def get_feature(feature, app = nil)
26
+ request(
27
+ :expects => 200,
28
+ :method => :get,
29
+ :path => "/features/#{feature}",
30
+ :query => { 'app' => app }
31
+ )
32
+ end
33
+
34
+ # POST /features/:feature
35
+ def post_feature(feature, app = nil)
36
+ request(
37
+ :expects => [200, 201],
38
+ :method => :post,
39
+ :path => "/features/#{feature}",
40
+ :query => { 'app' => app }
41
+ )
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,42 @@
1
+ module Azuki
2
+ class API
3
+
4
+ # DELETE /user/keys/:key
5
+ def delete_key(key)
6
+ request(
7
+ :expects => 200,
8
+ :method => :delete,
9
+ :path => "/user/keys/#{escape(key)}"
10
+ )
11
+ end
12
+
13
+ # DELETE /user/keys
14
+ def delete_keys
15
+ request(
16
+ :expects => 200,
17
+ :method => :delete,
18
+ :path => "/user/keys"
19
+ )
20
+ end
21
+
22
+ # GET /user/keys
23
+ def get_keys
24
+ request(
25
+ :expects => 200,
26
+ :method => :get,
27
+ :path => "/user/keys"
28
+ )
29
+ end
30
+
31
+ # POST /user/keys
32
+ def post_key(key)
33
+ request(
34
+ :body => key,
35
+ :expects => 200,
36
+ :method => :post,
37
+ :path => "/user/keys"
38
+ )
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,14 @@
1
+ module Azuki
2
+ class API
3
+
4
+ def post_login(username, password)
5
+ request(
6
+ :expects => 200,
7
+ :method => :post,
8
+ :path => '/login',
9
+ :query => { 'username' => username, 'password' => password }
10
+ )
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ module Azuki
2
+ class API
3
+
4
+ # GET /apps/:app/logs
5
+ def get_logs(app, options = {})
6
+ options = {
7
+ 'logplex' => 'true'
8
+ }.merge(options)
9
+ request(
10
+ :expects => 200,
11
+ :method => :get,
12
+ :path => "/apps/#{app}/logs",
13
+ :query => options
14
+ )
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,179 @@
1
+ require 'azuki/api/mock/addons'
2
+ require 'azuki/api/mock/apps'
3
+ require 'azuki/api/mock/attachments'
4
+ require 'azuki/api/mock/collaborators'
5
+ require 'azuki/api/mock/config_vars'
6
+ require 'azuki/api/mock/domains'
7
+ require 'azuki/api/mock/features'
8
+ require 'azuki/api/mock/keys'
9
+ require 'azuki/api/mock/login'
10
+ require 'azuki/api/mock/logs'
11
+ require 'azuki/api/mock/processes'
12
+ require 'azuki/api/mock/releases'
13
+ require 'azuki/api/mock/stacks'
14
+ require 'azuki/api/mock/user'
15
+
16
+ module Azuki
17
+ class API
18
+ module Mock
19
+
20
+ APP_NOT_FOUND = { :body => 'App not found.', :status => 404 }
21
+ USER_NOT_FOUND = { :body => 'User not found.', :status => 404 }
22
+
23
+ @mock_data = Hash.new do |hash, key|
24
+ hash[key] = {
25
+ :addons => {},
26
+ :apps => [],
27
+ :attachments => {},
28
+ :collaborators => {},
29
+ :config_vars => {},
30
+ :domains => {},
31
+ :features => {
32
+ :app => Hash.new {|hash,key| hash[key] = []},
33
+ :user => []
34
+ },
35
+ :keys => [],
36
+ :maintenance_mode => [],
37
+ :ps => {},
38
+ :releases => {},
39
+ :user => {}
40
+ }
41
+ end
42
+
43
+ def self.add_mock_app_addon(mock_data, app, addon)
44
+ addon_data = get_mock_addon(mock_data, addon)
45
+ mock_data[:addons][app] << addon_data.reject {|key, value| !['attachable', 'beta', 'configured', 'consumes_dyno_hours', 'description', 'group_description', 'name', 'plan_description', 'price', 'slug', 'state', 'terms_of_service', 'url'].include?(key)}
46
+ add_mock_release(mock_data, app, {'descr' => "Add-on add #{addon_data['name']}"})
47
+ end
48
+
49
+ def self.add_mock_release(mock_data, app, release_data)
50
+ env = if get_mock_app(mock_data, app)['stack'] == 'cedar'
51
+ {
52
+ 'BUNDLE_WITHOUT' => 'development:test',
53
+ 'DATABASE_URL' => 'postgres://username:password@ec2-123-123-123-123.compute-1.amazonaws.com/username',
54
+ 'LANG' => 'en_US.UTF-8',
55
+ 'RACK_ENV' => 'production'
56
+ }
57
+ else
58
+ {}
59
+ end
60
+ version = mock_data[:releases][app].map {|release| release['name'][1..-1].to_i}.max || 0
61
+ mock_data[:releases][app] << {
62
+ 'addons' => mock_data[:addons][app].map {|addon| addon['name']},
63
+ 'commit' => nil,
64
+ 'created_at' => timestamp,
65
+ 'descr' => "",
66
+ 'env' => env,
67
+ 'name' => "v#{version + 1}",
68
+ 'pstable' => { 'web' => '' },
69
+ 'user' => 'email@example.com'
70
+ }.merge(release_data)
71
+ end
72
+
73
+ def self.get_mock_addon(mock_data, addon)
74
+ @addons ||= begin
75
+ data = File.read("#{File.dirname(__FILE__)}/mock/cache/get_addons.json")
76
+ Azuki::API::OkJson.decode(data)
77
+ end
78
+ @addons.detect {|addon_data| addon_data['name'] == addon}
79
+ end
80
+
81
+ def self.get_mock_addon_price(mock_data, addon)
82
+ addon_data = get_mock_addon(mock_data, addon)
83
+ price_cents = addon_data['price_cents'] || 0
84
+ price = format("$%d/mo", price_cents / 100)
85
+ if price == '$0/mo'
86
+ price = 'free'
87
+ end
88
+ price
89
+ end
90
+
91
+ def self.get_mock_app(mock_data, app)
92
+ mock_data[:apps].detect {|app_data| app_data['name'] == app}
93
+ end
94
+
95
+ def self.get_mock_app_addon(mock_data, app, addon)
96
+ mock_data[:addons][app].detect {|addon_data| addon_data['name'] == addon}
97
+ end
98
+
99
+ def self.get_mock_app_domain(mock_data, app, domain)
100
+ mock_data[:domains][app].detect {|domain_data| domain_data['domain'] == domain}
101
+ end
102
+
103
+ def self.get_mock_collaborator(mock_data, app, email)
104
+ mock_data[:collaborators][app].detect {|collaborator_data| collaborator_data['email'] == email}
105
+ end
106
+
107
+ def self.get_mock_feature(mock_data, feature)
108
+ @features ||= begin
109
+ data = File.read("#{File.dirname(__FILE__)}/mock/cache/get_features.json")
110
+ Azuki::API::OkJson.decode(data)
111
+ end
112
+ @features.detect {|feature_data| feature_data['name'] == feature}
113
+ end
114
+
115
+ def self.get_mock_key(mock_data, key)
116
+ mock_data[:keys].detect {|key_data| %r{ #{Regexp.escape(key)}$}.match(key_data['contents'])}
117
+ end
118
+
119
+ def self.get_mock_processes(mock_data, app)
120
+ mock_data[:ps][app].map do |ps|
121
+
122
+ elapsed = Time.now.to_i - Time.parse(ps['transitioned_at']).to_i
123
+ ps['elapsed'] = elapsed
124
+
125
+ pretty_elapsed = if elapsed < 60
126
+ "#{elapsed}s"
127
+ elsif elapsed < (60 * 60)
128
+ "#{elapsed / 60}m"
129
+ else
130
+ "#{elapsed / 60 / 60}h"
131
+ end
132
+ ps['pretty_state'] = "#{ps['state']} for #{pretty_elapsed}"
133
+
134
+ ps
135
+ end
136
+ end
137
+
138
+ def self.parse_stub_params(params)
139
+ mock_data = nil
140
+
141
+ if params[:headers].has_key?('Authorization')
142
+ api_key = Base64.decode64(params[:headers]['Authorization']).split(':').last
143
+
144
+ parsed = params.dup
145
+ begin # try to JSON decode
146
+ parsed[:body] &&= Azuki::API::OkJson.decode(parsed[:body])
147
+ rescue # else leave as is
148
+ end
149
+ mock_data = @mock_data[api_key]
150
+ end
151
+
152
+ [parsed, mock_data]
153
+ end
154
+
155
+ def self.remove_mock_app_addon(mock_data, app, addon)
156
+ addon_data = mock_data[:addons][app].detect {|addon_data| addon_data['name'] == addon}
157
+ mock_data[:addons][app].delete(addon_data)
158
+ add_mock_release(mock_data, app, {'descr' => "Add-on remove #{addon_data['name']}"})
159
+ end
160
+
161
+ def self.unescape(string)
162
+ CGI.unescape(string)
163
+ end
164
+
165
+ def self.with_mock_app(mock_data, app, &block)
166
+ if app_data = get_mock_app(mock_data, app)
167
+ yield(app_data)
168
+ else
169
+ APP_NOT_FOUND
170
+ end
171
+ end
172
+
173
+ def self.timestamp
174
+ Time.now.strftime("%G/%m/%d %H:%M:%S %z")
175
+ end
176
+
177
+ end
178
+ end
179
+ end