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,80 @@
1
+ module Azuki
2
+ class API
3
+ module Mock
4
+
5
+ # stub DELETE /apps/:app/domains/:domain
6
+ Excon.stub(:expects => 200, :method => :delete, :path => %r{^/apps/([^/]+)/domains/([^/]+)$} ) do |params|
7
+ request_params, mock_data = parse_stub_params(params)
8
+ app, domain, _ = request_params[:captures][:path]
9
+ domain = unescape(domain)
10
+ with_mock_app(mock_data, app) do |app_data|
11
+ if domain = get_mock_app_domain(mock_data, app, domain)
12
+ mock_data[:domains][app].delete(domain)
13
+ {
14
+ :body => {},
15
+ :status => 200
16
+ }
17
+ else
18
+ {
19
+ :body => 'Domain not found.',
20
+ :status => 404
21
+ }
22
+ end
23
+ end
24
+ end
25
+
26
+ # stub DELETE /apps/:app/domains/:domain
27
+ Excon.stub(:expects => 200, :method => :delete, :path => %r{^/apps/([^/]+)/domains$} ) do |params|
28
+ request_params, mock_data = parse_stub_params(params)
29
+ app, _ = request_params[:captures][:path]
30
+ with_mock_app(mock_data, app) do |app_data|
31
+ mock_data[:domains][app] = []
32
+ {
33
+ :body => "",
34
+ :status => 200
35
+ }
36
+ end
37
+ end
38
+
39
+ # stub DELETE /apps//domains/:domain
40
+ Excon.stub({:method => :delete, :path => %r{^/apps//domains/([^/]+)$} }, {:status => 404})
41
+
42
+ # stub GET /apps/:app/domains
43
+ Excon.stub(:expects => 200, :method => :get, :path => %r{^/apps/([^/]+)/domains$} ) do |params|
44
+ request_params, mock_data = parse_stub_params(params)
45
+ app, _ = request_params[:captures][:path]
46
+ with_mock_app(mock_data, app) do |app_data|
47
+ {
48
+ :body => Azuki::API::OkJson.encode(mock_data[:domains][app]),
49
+ :status => 200
50
+ }
51
+ end
52
+ end
53
+
54
+ # stub POST /apps/:app/domains
55
+ Excon.stub(:expects => 201, :method => :post, :path => %r{^/apps/([^/]+)/domains$} ) do |params|
56
+ request_params, mock_data = parse_stub_params(params)
57
+ app, _ = request_params[:captures][:path]
58
+ domain = request_params[:query]['domain_name[domain]']
59
+ with_mock_app(mock_data, app) do |app_data|
60
+ unless get_mock_app_domain(mock_data, app, domain)
61
+ mock_data[:domains][app] << {
62
+ 'app_id' => app_data['id'],
63
+ 'base_domain' => domain.split('.')[-2..-1].join('.'),
64
+ 'created_at' => timestamp,
65
+ 'domain' => domain,
66
+ 'default' => nil,
67
+ 'id' => rand(999999),
68
+ 'updated_at' => timestamp
69
+ }
70
+ end
71
+ {
72
+ :body => Azuki::API::OkJson.encode('domain' => domain),
73
+ :status => 201
74
+ }
75
+ end
76
+ end
77
+
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,120 @@
1
+ module Azuki
2
+ class API
3
+ module Mock
4
+
5
+ # stub DELETE /features/:feature
6
+ Excon.stub(:expects => 200, :method => :delete, :path => %r{^/features/([^/]+)$}) do |params|
7
+ request_params, mock_data = parse_stub_params(params)
8
+ app = request_params[:query].has_key?('app') && request_params[:query]['app']
9
+ feature, _ = request_params[:captures][:path]
10
+ if !app || get_mock_app(mock_data, app)
11
+ # app found
12
+ if feature_data = get_mock_feature(mock_data, feature)
13
+ feature_data = feature_data.merge('enabled' => true)
14
+ # feature exists
15
+ case feature_data['kind']
16
+ when 'app'
17
+ mock_data[:features][:app][app].delete(feature_data)
18
+ {
19
+ :body => Azuki::API::OkJson.encode(feature_data.merge('enabled' => false)),
20
+ :status => 200
21
+ }
22
+ when 'user'
23
+ mock_data[:features][:user].delete(feature_data.merge('enabled' => false))
24
+ {
25
+ :body => Azuki::API::OkJson.encode(feature_data),
26
+ :status => 200
27
+ }
28
+ end
29
+ else
30
+ # feature does not exist
31
+ {
32
+ :body => Azuki::API::OkJson.encode({'error' => "Feature not found."}),
33
+ :status => 404
34
+ }
35
+ end
36
+ else
37
+ {
38
+ :body => 'Feature not enabled.',
39
+ :status => 422
40
+ }
41
+ end
42
+ end
43
+
44
+ # stub GET /features
45
+ Excon.stub(:expects => 200, :method => :get, :path => %r|^/features$|) do |params|
46
+ request_params, mock_data = parse_stub_params(params)
47
+ {
48
+ :body => File.read("#{File.dirname(__FILE__)}/cache/get_features.json"),
49
+ :status => 200
50
+ }
51
+ end
52
+
53
+ # stub GET /features/:feature
54
+ Excon.stub(:expects => 200, :method => :get, :path => %r{^/features/([^/]+)}) do |params|
55
+ request_params, mock_data = parse_stub_params(params)
56
+ feature, _ = request_params[:captures][:path]
57
+ if feature_data = get_mock_feature(mock_data, feature)
58
+ {
59
+ :body => Azuki::API::OkJson.encode(feature_data),
60
+ :status => 200
61
+ }
62
+ else
63
+ # feature does not exist
64
+ {
65
+ :body => Azuki::API::OkJson.encode({'error' => "Feature not found."}),
66
+ :status => 404
67
+ }
68
+ end
69
+ end
70
+
71
+ # stub POST /features/:feature
72
+ Excon.stub(:expects => [200, 201], :method => :post, :path => %r{^/features/([^/]+)$}) do |params|
73
+ request_params, mock_data = parse_stub_params(params)
74
+ app = request_params[:query].has_key?('app') && request_params[:query]['app']
75
+ feature, _ = request_params[:captures][:path]
76
+ if !app || get_mock_app(mock_data, app)
77
+ # app found
78
+ if feature_data = get_mock_feature(mock_data, feature)
79
+ feature_data = feature_data.merge('enabled' => true)
80
+ # feature exists
81
+ case feature_data['kind']
82
+ when 'app'
83
+ status = if mock_data[:features][:app][app].include?(feature_data)
84
+ 200
85
+ else
86
+ mock_data[:features][:app][app] << feature_data
87
+ 201
88
+ end
89
+ {
90
+ :body => Azuki::API::OkJson.encode(feature_data),
91
+ :status => status
92
+ }
93
+ when 'user'
94
+ status = if mock_data[:features][:user].include(feature_data)
95
+ 200
96
+ else
97
+ mock_data[:features][:user] << feature_data
98
+ 201
99
+ end
100
+ {
101
+ :body => '',
102
+ :status => status
103
+ }
104
+ end
105
+ else
106
+ # feature does not exist
107
+ {
108
+ :body => Azuki::API::OkJson.encode({'error' => "Feature not found."}),
109
+ :status => 404
110
+ }
111
+ end
112
+ else
113
+ # app not found
114
+ Azuki::API::Mock::APP_NOT_FOUND
115
+ end
116
+ end
117
+
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,46 @@
1
+ module Azuki
2
+ class API
3
+ module Mock
4
+
5
+ # stub DELETE /user/keys/:key
6
+ Excon.stub(:expects => 200, :method => :delete, :path => %r{^/user/keys/([^/]+)}) do |params|
7
+ request_params, mock_data = parse_stub_params(params)
8
+ key, _ = request_params[:captures][:path]
9
+ key = unescape(key)
10
+ if key_data = get_mock_key(mock_data, key)
11
+ mock_data[:keys].delete(key_data)
12
+ { :status => 200 }
13
+ else
14
+ { :body => "Key not found.", :status => 404 }
15
+ end
16
+ end
17
+
18
+ # stub DELETE /user/keys
19
+ Excon.stub(:expects => 200, :method => :delete, :path => %r{^/user/keys$}) do |params|
20
+ request_params, mock_data = parse_stub_params(params)
21
+ mock_data[:keys] = []
22
+ { :status => 200 }
23
+ end
24
+
25
+ # stub GET /user/keys
26
+ Excon.stub(:expects => 200, :method => :get, :path => %r{^/user/keys}) do |params|
27
+ request_params, mock_data = parse_stub_params(params)
28
+ {
29
+ :body => Azuki::API::OkJson.encode(mock_data[:keys]),
30
+ :status => 200
31
+ }
32
+ end
33
+
34
+ # stub POST /user/keys
35
+ Excon.stub(:expects => 200, :method => :post, :path => %r{^/user/keys}) do |params|
36
+ request_params, mock_data = parse_stub_params(params)
37
+ mock_data[:keys] |= [{
38
+ 'email' => 'email@example.com',
39
+ 'contents' => request_params[:body]
40
+ }]
41
+ { :status => 200 }
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,22 @@
1
+ module Azuki
2
+ class API
3
+ module Mock
4
+
5
+ # stub POST /login
6
+ Excon.stub(:expects => 200, :method => :post, :path => '/login') do |params|
7
+ request_params, mock_data = parse_stub_params(params)
8
+ {
9
+ :body => {
10
+ 'api_key' => SecureRandom.hex(20),
11
+ 'email' => 'email@example.com',
12
+ 'id' => '123456@users.azukiapp.com',
13
+ 'verified' => true,
14
+ 'verified_at' => timestamp
15
+ },
16
+ :status => 200
17
+ }
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ module Azuki
2
+ class API
3
+ module Mock
4
+
5
+ # stub GET /apps/:app/logs
6
+ Excon.stub(:expects => 200, :method => :get, :path => %r{^/apps/([^/]+)/logs}) do |params|
7
+ request_params, mock_data = parse_stub_params(params)
8
+ app, _ = request_params[:captures][:path]
9
+ with_mock_app(mock_data, app) do
10
+ uuid = [SecureRandom.hex(4), SecureRandom.hex(2), SecureRandom.hex(2), SecureRandom.hex(2), SecureRandom.hex(6)].join('-')
11
+ {
12
+ :body => "https://logplex.azukiapp.com/sessions/#{uuid}?srv=#{Time.now.to_i}",
13
+ :status => 200
14
+ }
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,198 @@
1
+ module Azuki
2
+ class API
3
+ module Mock
4
+
5
+ # stub GET /apps/:app/ps
6
+ Excon.stub(:expects => 200, :method => :get, :path => %r{^/apps/([^/]+)/ps}) do |params|
7
+ request_params, mock_data = parse_stub_params(params)
8
+ app, _ = request_params[:captures][:path]
9
+ with_mock_app(mock_data, app) do |app_data|
10
+ {
11
+ :body => Azuki::API::OkJson.encode(get_mock_processes(mock_data, app)),
12
+ :status => 200
13
+ }
14
+ end
15
+ end
16
+
17
+ # stub POST /apps/:app/ps
18
+ Excon.stub(:expects => 200, :method => :post, :path => %r{^/apps/([^/]+)/ps\/?$}) do |params|
19
+ request_params, mock_data = parse_stub_params(params)
20
+ app, _ = request_params[:captures][:path]
21
+ with_mock_app(mock_data, app) do |app_data|
22
+ unless attached = request_params[:query].has_key?('attach') && request_params[:query]['attach'].to_s == 'true'
23
+ type = 'Ps'
24
+ end
25
+ command = request_params[:query].has_key?('command') && request_params[:query]['command']
26
+ rendezvous_url = if attached
27
+ "s1.runtime.azukiapp.com:5000/#{SecureRandom.hex(32)}"
28
+ end
29
+ max_run_id = mock_data[:ps][app].map { |process| process['process'].split('run.').last.to_i}.max
30
+ data = {
31
+ 'action' => 'complete',
32
+ 'attached' => attached,
33
+ 'command' => command,
34
+ 'elapsed' => 0,
35
+ 'pretty_state' => 'completed for 0s',
36
+ 'process' => "run.#{max_run_id + 1}",
37
+ 'rendezvous_url' => rendezvous_url,
38
+ 'slug' => 'NONE',
39
+ 'state' => 'created',
40
+ 'transitioned_at' => timestamp,
41
+ 'type' => type,
42
+ 'upid' => rand(99999999).to_s
43
+ }
44
+ mock_data[:ps][app] << data
45
+ {
46
+ :body => Azuki::API::OkJson.encode(data),
47
+ :status => 200,
48
+ }
49
+ end
50
+ end
51
+
52
+ # stub POST /apps/:app/ps/restart
53
+ Excon.stub(:expects => 200, :method => :post, :path => %r{^/apps/([^/]+)/ps/restart}) do |params|
54
+ request_params, mock_data = parse_stub_params(params)
55
+ app, _ = request_params[:captures][:path]
56
+ with_mock_app(mock_data, app) do |app_data|
57
+ ps = request_params[:query].has_key?('ps') && request_params[:query]['ps']
58
+ type = request_params[:query].has_key?('type') && request_params[:query]['type']
59
+ if !ps && !type
60
+ mock_data[:ps][app].select {|process| process['state'] != 'complete'}.each do |process|
61
+ process['transitioned_at'] = timestamp
62
+ end
63
+ elsif ps
64
+ mock_data[:ps][app].select {|process| process['process'] == 'ps' && process['state'] != 'complete'}.each do |process|
65
+ process['transitioned_at'] = timestamp
66
+ end
67
+ elsif type
68
+ mock_data[:ps][app].select {|process| process['process'] =~ %r{^#{type}\.\d+} && process['state'] != 'complete'}.each do |process|
69
+ process['transitioned_at'] = timestamp
70
+ end
71
+ end
72
+ {
73
+ :body => 'ok',
74
+ :status => 200
75
+ }
76
+ end
77
+ end
78
+
79
+ # stub POST /apps/:app/ps/scale
80
+ Excon.stub(:expects => 200, :method => :post, :path => %r{^/apps/([^/]+)/ps/scale}) do |params|
81
+ request_params, mock_data = parse_stub_params(params)
82
+ app, _ = request_params[:captures][:path]
83
+ with_mock_app(mock_data, app) do |app_data|
84
+ type = request_params[:query].has_key?('type') && request_params[:query]['type']
85
+ qty = request_params[:query].has_key?('qty') && request_params[:query]['qty']
86
+ if app_data['stack'] == 'cedar'
87
+ if type == 'web'
88
+ current_qty = mock_data[:ps][app].count {|process| process['process'] =~ %r{^web\.\d+}}
89
+
90
+ new_qty = case qty
91
+ when /[+-]\d+/
92
+ current_qty + qty.to_i
93
+ else
94
+ qty.to_i
95
+ end
96
+
97
+ if new_qty >= current_qty
98
+ (new_qty - current_qty).times do
99
+ max_web_id = mock_data[:ps][app].map {|process| process['process'].split('web.').last.to_i}.max
100
+ data = mock_data[:ps][app].first.merge({
101
+ 'process' => "web.#{max_web_id + 1}",
102
+ 'transitioned_at' => timestamp,
103
+ 'upid' => rand(99999999).to_s
104
+ })
105
+ mock_data[:ps][app] << data
106
+ end
107
+ elsif new_qty < current_qty
108
+ (current_qty - new_qty).times do
109
+ max_web_id = mock_data[:ps][app].map {|process| process['process'].split('web.').last.to_i}.max
110
+ process = mock_data[:ps][app].detect {|process| process['process'] == "web.#{max_web_id}"}
111
+ mock_data[:ps][app].delete(process)
112
+ end
113
+ end
114
+ {
115
+ :body => new_qty.to_s,
116
+ :status => 200
117
+ }
118
+ else
119
+ {
120
+ :body => Azuki::API::OkJson.encode('error' => "No such type as #{type}") ,
121
+ :status => 422
122
+ }
123
+ end
124
+ else
125
+ {
126
+ :body => Azuki::API::OkJson.encode('error' => "That feature is not available on this app's stack"),
127
+ :status => 422
128
+ }
129
+ end
130
+ end
131
+ end
132
+
133
+ # stub POST /apps/:app/ps/stop
134
+ Excon.stub(:expects => 200, :method => :post, :path => %r{^/apps/([^/]+)/ps/stop}) do |params|
135
+ request_params, mock_data = parse_stub_params(params)
136
+ app, _ = request_params[:captures][:path]
137
+ with_mock_app(mock_data, app) do |app_data|
138
+ ps = request_params[:query].has_key?('ps') && request_params[:query]['ps']
139
+ type = request_params[:query].has_key?('type') && request_params[:query]['type']
140
+ if !ps && !type
141
+ {
142
+ :body => Azuki::API::OkJson.encode({'error' => 'Missing process argument'}),
143
+ :status => 422
144
+ }
145
+ else
146
+ {
147
+ :body => 'ok',
148
+ :status => 200
149
+ }
150
+ end
151
+ end
152
+ end
153
+
154
+ # stub PUT /apps/:app/dynos
155
+ Excon.stub(:expects => 200, :method => :put, :path => %r{^/apps/([^/]+)/dynos}) do |params|
156
+ request_params, mock_data = parse_stub_params(params)
157
+ app, _ = request_params[:captures][:path]
158
+ with_mock_app(mock_data, app) do |app_data|
159
+ dynos = request_params[:query].has_key?('dynos') && request_params[:query]['dynos'].to_i
160
+ unless app_data['stack'] == 'cedar'
161
+ app_data['dynos'] = dynos
162
+ {
163
+ :body => Azuki::API::OkJson.encode({'name' => app, 'dynos' => dynos}),
164
+ :status => 200
165
+ }
166
+ else
167
+ {
168
+ :body => Azuki::API::OkJson.encode({'error' => "For Cedar apps, use `azuki scale web=#{dynos}`"}),
169
+ :status => 422
170
+ }
171
+ end
172
+ end
173
+ end
174
+
175
+ # stub PUT /apps/:app/workers
176
+ Excon.stub(:expects => 200, :method => :put, :path => %r{^/apps/([^/]+)/workers}) do |params|
177
+ request_params, mock_data = parse_stub_params(params)
178
+ app, _ = request_params[:captures][:path]
179
+ with_mock_app(mock_data, app) do |app_data|
180
+ workers = request_params[:query].has_key?('workers') && request_params[:query]['workers'].to_i
181
+ unless app_data['stack'] == 'cedar'
182
+ app_data['workers'] = workers
183
+ {
184
+ :body => Azuki::API::OkJson.encode({'name' => app, 'workers' => workers}),
185
+ :status => 200
186
+ }
187
+ else
188
+ {
189
+ :body => Azuki::API::OkJson.encode({'error' => "For Cedar apps, use `azuki scale worker=#{workers}`"}),
190
+ :status => 422
191
+ }
192
+ end
193
+ end
194
+ end
195
+
196
+ end
197
+ end
198
+ end