azuki-api 0.0.2

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.
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