heroku-api 0.1.0

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 (46) hide show
  1. data/.gitignore +4 -0
  2. data/.travis.yml +14 -0
  3. data/Gemfile +4 -0
  4. data/README.md +120 -0
  5. data/Rakefile +23 -0
  6. data/changelog.txt +4 -0
  7. data/heroku-api.gemspec +23 -0
  8. data/lib/heroku-api.rb +1 -0
  9. data/lib/heroku/api.rb +85 -0
  10. data/lib/heroku/api/addons.rb +47 -0
  11. data/lib/heroku/api/apps.rb +62 -0
  12. data/lib/heroku/api/collaborators.rb +33 -0
  13. data/lib/heroku/api/config_vars.rb +33 -0
  14. data/lib/heroku/api/domains.rb +33 -0
  15. data/lib/heroku/api/errors.rb +9 -0
  16. data/lib/heroku/api/keys.rb +42 -0
  17. data/lib/heroku/api/logs.rb +18 -0
  18. data/lib/heroku/api/mock.rb +176 -0
  19. data/lib/heroku/api/mock/addons.rb +153 -0
  20. data/lib/heroku/api/mock/apps.rb +184 -0
  21. data/lib/heroku/api/mock/cache/get_addons.json +1 -0
  22. data/lib/heroku/api/mock/collaborators.rb +55 -0
  23. data/lib/heroku/api/mock/config_vars.rb +46 -0
  24. data/lib/heroku/api/mock/domains.rb +71 -0
  25. data/lib/heroku/api/mock/keys.rb +46 -0
  26. data/lib/heroku/api/mock/logs.rb +20 -0
  27. data/lib/heroku/api/mock/processes.rb +191 -0
  28. data/lib/heroku/api/mock/releases.rb +124 -0
  29. data/lib/heroku/api/mock/stacks.rb +84 -0
  30. data/lib/heroku/api/processes.rb +77 -0
  31. data/lib/heroku/api/releases.rb +33 -0
  32. data/lib/heroku/api/stacks.rb +22 -0
  33. data/lib/heroku/api/vendor/okjson.rb +559 -0
  34. data/lib/heroku/api/version.rb +5 -0
  35. data/test/test_addons.rb +169 -0
  36. data/test/test_apps.rb +119 -0
  37. data/test/test_collaborators.rb +73 -0
  38. data/test/test_config_vars.rb +54 -0
  39. data/test/test_domains.rb +63 -0
  40. data/test/test_helper.rb +35 -0
  41. data/test/test_keys.rb +39 -0
  42. data/test/test_logs.rb +20 -0
  43. data/test/test_processes.rb +245 -0
  44. data/test/test_releases.rb +91 -0
  45. data/test/test_stacks.rb +49 -0
  46. metadata +134 -0
@@ -0,0 +1,33 @@
1
+ module Heroku
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/#{CGI.escape(domain)}"
10
+ )
11
+ end
12
+
13
+ # GET /apps/:app/domains
14
+ def get_domains(app)
15
+ request(
16
+ :expects => 200,
17
+ :method => :get,
18
+ :path => "/apps/#{app}/domains"
19
+ )
20
+ end
21
+
22
+ # POST /apps/:app/domains
23
+ def post_domain(app, domain)
24
+ request(
25
+ :expects => 201,
26
+ :method => :post,
27
+ :path => "/apps/#{app}/domains",
28
+ :query => {'domain_name[domain]' => domain}
29
+ )
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,9 @@
1
+ module Heroku
2
+ class API
3
+ module Errors
4
+ class Error < StandardError; end
5
+
6
+ class NotFound < Error; end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,42 @@
1
+ module Heroku
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/#{CGI.escape(key).gsub('.','%2E')}"
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,18 @@
1
+ module Heroku
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,176 @@
1
+ require 'heroku/api/mock/addons'
2
+ require 'heroku/api/mock/apps'
3
+ require 'heroku/api/mock/collaborators'
4
+ require 'heroku/api/mock/config_vars'
5
+ require 'heroku/api/mock/domains'
6
+ require 'heroku/api/mock/keys'
7
+ require 'heroku/api/mock/logs'
8
+ require 'heroku/api/mock/processes'
9
+ require 'heroku/api/mock/releases'
10
+ require 'heroku/api/mock/stacks'
11
+
12
+ module Heroku
13
+ class API
14
+ module Mock
15
+
16
+ APP_NOT_FOUND = { :body => 'App not found.', :status => 404 }
17
+ USER_NOT_FOUND = { :body => 'User not found.', :status => 404 }
18
+
19
+ @mock_data = Hash.new do |hash, key|
20
+ hash[key] = {
21
+ :addons => {},
22
+ :apps => [],
23
+ :collaborators => {},
24
+ :config_vars => {},
25
+ :domains => {},
26
+ :keys => [],
27
+ :maintenance_mode => [],
28
+ :ps => {},
29
+ :releases => {}
30
+ }
31
+ end
32
+
33
+ def self.add_mock_app_addon(mock_data, app, addon)
34
+ addon_data = get_mock_addon(mock_data, addon)
35
+ mock_data[:addons][app] << addon_data.reject {|key, value| !['beta', 'configured', 'description', 'name', 'state', 'url'].include?(key)}
36
+ version = mock_data[:releases][app].map {|release| release['name'][1..-1].to_i}.max || 0
37
+ env = if get_mock_app(mock_data, app)['stack'] == 'cedar'
38
+ {
39
+ 'BUNDLE_WITHOUT' => 'development:test',
40
+ 'DATABASE_URL' => 'postgres://username:password@ec2-123-123-123-123.compute-1.amazonaws.com/username',
41
+ 'LANG' => 'en_US.UTF-8',
42
+ 'RACK_ENV' => 'production',
43
+ 'SHARED_DATABASE_URL' => 'postgres://username:password@ec2-123-123-123-123.compute-1.amazonaws.com/username'
44
+ }
45
+ else
46
+ {}
47
+ end
48
+ mock_data[:releases][app] << {
49
+ 'addons' => mock_data[:addons][app].map {|addon| addon['name']},
50
+ 'commit' => nil,
51
+ 'created_at' => timestamp,
52
+ 'descr' => "Add-on add #{addon_data['name']}",
53
+ 'env' => env,
54
+ 'name' => "v#{version + 1}",
55
+ 'pstable' => { 'web' => '' },
56
+ 'user' => 'email@example.com'
57
+ }
58
+ end
59
+
60
+ def self.get_mock_addon(mock_data, addon)
61
+ @addons ||= begin
62
+ data = File.read("#{File.dirname(__FILE__)}/mock/cache/get_addons.json")
63
+ Heroku::API::OkJson.decode(data)
64
+ end
65
+ @addons.detect {|addon_data| addon_data['name'] == addon}
66
+ end
67
+
68
+ def self.get_mock_addon_price(mock_data, addon)
69
+ addon_data = get_mock_addon(mock_data, addon)
70
+ price_cents = addon_data['price_cents'] || 0
71
+ price = format("$%d/mo", price_cents / 100)
72
+ if price == '$0/mo'
73
+ price = 'free'
74
+ end
75
+ price
76
+ end
77
+
78
+ def self.get_mock_app(mock_data, app)
79
+ mock_data[:apps].detect {|app_data| app_data['name'] == app}
80
+ end
81
+
82
+ def self.get_mock_app_addon(mock_data, app, addon)
83
+ mock_data[:addons][app].detect {|addon_data| addon_data['name'] == addon}
84
+ end
85
+
86
+ def self.get_mock_app_domain(mock_data, app, domain)
87
+ mock_data[:domains][app].detect {|domain_data| domain_data['domain'] == domain}
88
+ end
89
+
90
+ def self.get_mock_collaborator(mock_data, app, email)
91
+ mock_data[:collaborators][app].detect {|collaborator_data| collaborator_data['email'] == email}
92
+ end
93
+
94
+ def self.get_mock_key(mock_data, key)
95
+ mock_data[:keys].detect {|key_data| %r{ #{Regexp.escape(key)}$}.match(key_data['contents'])}
96
+ end
97
+
98
+ def self.get_mock_processes(mock_data, app)
99
+ mock_data[:ps][app].map do |ps|
100
+
101
+ # swap day/month positions for Time.parse
102
+ time_parts = ps['transitioned_at'].split(' ')
103
+ date_parts = time_parts.first.split('/')
104
+ date_parts[1], date_parts[2] = date_parts[2], date_parts[1]
105
+ time_parts[0] = date_parts.join('/')
106
+ munged_time = time_parts.join(' ')
107
+ elapsed = Time.now.to_i - Time.parse(munged_time).to_i
108
+ ps['elapsed'] = elapsed
109
+
110
+ pretty_elapsed = if elapsed < 60
111
+ "#{elapsed}s"
112
+ elsif elapsed < (60 * 60)
113
+ "#{elapsed / 60}m"
114
+ else
115
+ "#{elapsed / 60 / 60}h"
116
+ end
117
+ ps['pretty_state'] = "#{ps['state']} for #{pretty_elapsed}"
118
+
119
+ ps
120
+ end
121
+ end
122
+
123
+ def self.parse_stub_params(params)
124
+ api_key = Base64.decode64(params[:headers]['Authorization']).split(':').last
125
+
126
+ parsed = params.dup
127
+ begin # try to JSON decode
128
+ parsed[:body] &&= Heroku::API::OkJson.decode(parsed[:body])
129
+ rescue # else leave as is
130
+ end
131
+
132
+ [parsed, @mock_data[api_key]]
133
+ end
134
+
135
+ def self.remove_mock_app_addon(mock_data, app, addon)
136
+ addon_data = mock_data[:addons][app].detect {|addon_data| addon_data['name'] == addon}
137
+ mock_data[:addons][app].delete(addon_data)
138
+ version = mock_data[:releases][app].map {|release| release['name'][1..-1].to_i}.max || 0
139
+ env = if get_mock_app(mock_data, app)['stack'] == 'cedar'
140
+ {
141
+ 'BUNDLE_WITHOUT' => 'development:test',
142
+ 'DATABASE_URL' => 'postgres://username:password@ec2-123-123-123-123.compute-1.amazonaws.com/username',
143
+ 'LANG' => 'en_US.UTF-8',
144
+ 'RACK_ENV' => 'production',
145
+ 'SHARED_DATABASE_URL' => 'postgres://username:password@ec2-123-123-123-123.compute-1.amazonaws.com/username'
146
+ }
147
+ else
148
+ {}
149
+ end
150
+ mock_data[:releases][app] << {
151
+ 'addons' => mock_data[:addons][app].map {|addon| addon['name']},
152
+ 'commit' => nil,
153
+ 'created_at' => timestamp,
154
+ 'descr' => "Add-on remove #{addon_data['name']}",
155
+ 'env' => env,
156
+ 'name' => "v#{version + 1}",
157
+ 'pstable' => { 'web' => '' },
158
+ 'user' => 'email@example.com'
159
+ }
160
+ end
161
+
162
+ def self.with_mock_app(mock_data, app, &block)
163
+ if app_data = get_mock_app(mock_data, app)
164
+ yield(app_data)
165
+ else
166
+ APP_NOT_FOUND
167
+ end
168
+ end
169
+
170
+ def self.timestamp
171
+ Time.now.strftime("%G/%d/%m %H:%M:%S %z")
172
+ end
173
+
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,153 @@
1
+ module Heroku
2
+ class API
3
+ module Mock
4
+
5
+ # stub DELETE /apps/:app/addons/:addon
6
+ Excon.stub(:expects => 200, :method => :delete, :path => %r{^/apps/([^/]+)/addons/([^/]+)$}) do |params|
7
+ request_params, mock_data = parse_stub_params(params)
8
+ app, addon, _ = request_params[:captures][:path]
9
+ with_mock_app(mock_data, app) do
10
+ addon_names = mock_data[:addons][app].map {|data| data['name']}
11
+ if addon_data = get_mock_addon(mock_data, addon)
12
+ # addon exists
13
+ if app_addon_data = get_mock_app_addon(mock_data, app, addon)
14
+ # addon is currently installed
15
+ remove_mock_app_addon(mock_data, app, addon)
16
+ {
17
+ :body => Heroku::API::OkJson.encode({
18
+ :message => nil,
19
+ :price => get_mock_addon_price(mock_data, addon),
20
+ :status => 'Uninstalled'
21
+ }),
22
+ :status => 200
23
+ }
24
+ else
25
+ # addon is not currently installed
26
+ {
27
+ :body => Heroku::API::OkJson.encode({'error' => "The add-on #{addon} is not installed for this app. Did you mean:\n\t#{addon_names.join("\n\t")}"}),
28
+ :status => 422
29
+ }
30
+ end
31
+ else
32
+ # addon does not exist
33
+ {
34
+ :body => Heroku::API::OkJson.encode({'error' => "Could not find add-on #{addon}. Did you mean:\n\t#{addon_names.join("\n\t")}"}),
35
+ :status => 422
36
+ }
37
+ end
38
+ end
39
+ end
40
+
41
+ # stub GET /addons
42
+ Excon.stub(:expects => 200, :method => :get, :path => "/addons") do |params|
43
+ request_params, mock_data = parse_stub_params(params)
44
+ {
45
+ :body => File.read("#{File.dirname(__FILE__)}/cache/get_addons.json"),
46
+ :status => 200
47
+ }
48
+ end
49
+
50
+ # stub GET /apps/:app/addons
51
+ Excon.stub(:expects => 200, :method => :get, :path => %r{^/apps/([^/]+)/addons$}) do |params|
52
+ request_params, mock_data = parse_stub_params(params)
53
+ app, _ = request_params[:captures][:path]
54
+ with_mock_app(mock_data, app) do
55
+ {
56
+ :body => Heroku::API::OkJson.encode(mock_data[:addons][app].map {|addon| addon['configured'] = true; addon}),
57
+ :status => 200
58
+ }
59
+ end
60
+ end
61
+
62
+ # stub POST /apps/:app/addons
63
+ Excon.stub(:expects => 200, :method => :post, :path => %r{^/apps/([^/]+)/addons/([^/]+)$}) do |params|
64
+ request_params, mock_data = parse_stub_params(params)
65
+ app, addon, _ = request_params[:captures][:path]
66
+ with_mock_app(mock_data, app) do
67
+ if addon_data = get_mock_addon(mock_data, addon)
68
+ # addon exists
69
+ unless app_addon_type_data = mock_data[:addons][app].detect {|data| data['name'] =~ %r{^#{addon.split(':').first}}}
70
+ # addon of same type does not exist
71
+ unless app_addon_data = get_mock_app_addon(mock_data, app, addon)
72
+ # addon is not currently installed
73
+ add_mock_app_addon(mock_data, app, addon)
74
+ {
75
+ :body => Heroku::API::OkJson.encode({
76
+ :message => nil,
77
+ :price => get_mock_addon_price(mock_data, addon),
78
+ :status => 'Installed'
79
+ }),
80
+ :status => 200
81
+ }
82
+ else
83
+ # addon is currently installed
84
+ {
85
+ :body => Heroku::API::OkJson.encode({'error' => "Add-on already installed."}),
86
+ :status => 422
87
+ }
88
+ end
89
+ else
90
+ # addon of same type exists
91
+ {
92
+ :body => Heroku::API::OkJson.encode({'error' => "#{app_addon_type_data['name']} add-on already added.\nTo upgrade, use addons:upgrade instead.\n"}),
93
+ :status => 422
94
+ }
95
+ end
96
+ else
97
+ # addon does not exist
98
+ {
99
+ :body => Heroku::API::OkJson.encode({'error' => "Add-on not found."}),
100
+ :status => 422
101
+ }
102
+ end
103
+ end
104
+ end
105
+
106
+ # stub PUT /apps/:app/addons
107
+ Excon.stub(:expects => 200, :method => :put, :path => %r{^/apps/([^/]+)/addons/([^/]+)$}) do |params|
108
+ request_params, mock_data = parse_stub_params(params)
109
+ app, addon, _ = request_params[:captures][:path]
110
+ with_mock_app(mock_data, app) do
111
+ if addon_data = get_mock_addon(mock_data, addon)
112
+ # addon exists
113
+ if mock_data[:addons][app].detect {|data| data['name'] =~ %r{^#{addon.split(':').first}}}
114
+ # addon of same type exists
115
+ unless app_addon_data = get_mock_app_addon(mock_data, app, addon)
116
+ # addon is not currently installed
117
+ mock_data[:addons][app].delete(app_addon_data)
118
+ add_mock_app_addon(mock_data, app, addon)
119
+ {
120
+ :body => Heroku::API::OkJson.encode({
121
+ :message => nil,
122
+ :price => get_mock_addon_price(mock_data, addon),
123
+ :status => 'Updated'
124
+ }),
125
+ :status => 200
126
+ }
127
+ else
128
+ # addon is currently installed
129
+ {
130
+ :body => Heroku::API::OkJson.encode({'error' => "Add-on already installed."}),
131
+ :status => 422
132
+ }
133
+ end
134
+ else
135
+ # addon of same type not installed
136
+ {
137
+ :body => Heroku::API::OkJson.encode({'error' => "Can't upgrade, no #{addon.split(':').join(' ')} add-on has been added.\nTo add, use addons:add instead.\n"}),
138
+ :status => 422
139
+ }
140
+ end
141
+ else
142
+ # addon does not exist
143
+ {
144
+ :body => Heroku::API::OkJson.encode({'error' => "Add-on not found."}),
145
+ :status => 422
146
+ }
147
+ end
148
+ end
149
+ end
150
+
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,184 @@
1
+ module Heroku
2
+ class API
3
+ module Mock
4
+
5
+ # stub DELETE /apps/:app
6
+ Excon.stub(:expects => 200, :method => :delete, :path => %r{^/apps/([^/]+)$} ) 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
+ mock_data[:addons].delete(app)
11
+ mock_data[:apps].delete(app_data)
12
+ mock_data[:collaborators].delete(app)
13
+ mock_data[:config_vars].delete(app)
14
+ mock_data[:domains].delete(app)
15
+ mock_data[:ps].delete(app)
16
+ mock_data[:releases].delete(app)
17
+ {
18
+ :body => Heroku::API::OkJson.encode({}),
19
+ :status => 200
20
+ }
21
+ end
22
+ end
23
+
24
+ # stub GET /apps/
25
+ Excon.stub(:expects => 200, :method => :get, :path => '/apps') do |params|
26
+ request_params, mock_data = parse_stub_params(params)
27
+ {
28
+ :body => Heroku::API::OkJson.encode(mock_data[:apps]),
29
+ :status => 200
30
+ }
31
+ end
32
+
33
+ # stub GET /apps/:app
34
+ Excon.stub(:expects => 200, :method => :get, :path => %r{^/apps/([^/]+)$} ) do |params|
35
+ request_params, mock_data = parse_stub_params(params)
36
+ app, _ = request_params[:captures][:path]
37
+ with_mock_app(mock_data, app) do |app_data|
38
+ {
39
+ :body => Heroku::API::OkJson.encode(app_data),
40
+ :status => 200
41
+ }
42
+ end
43
+ end
44
+
45
+ # stub POST /apps
46
+ Excon.stub(:expects => 202, :method => :post, :path => '/apps') do |params|
47
+ request_params, mock_data = parse_stub_params(params)
48
+ app = request_params[:query].has_key?('app[name]') && request_params[:query]['app[name]'] || "generated-name-#{rand(999)}"
49
+
50
+ if get_mock_app(mock_data, app)
51
+ {
52
+ :body => Heroku::API::OkJson.encode('error' => 'Name is already taken'),
53
+ :status => 422
54
+ }
55
+ else
56
+ stack = request_params[:query].has_key?('app[stack]') && request_params[:query]['app[stack]'] || 'bamboo-mri-1.9.2'
57
+ app_data = {
58
+ 'created_at' => timestamp,
59
+ 'create_status' => 'complete',
60
+ 'id' => rand(99999),
61
+ 'name' => app,
62
+ 'owner_email' => 'email@example.com',
63
+ 'slug_size' => nil,
64
+ 'stack' => request_params[:query].has_key?('app[stack]') && request_params[:query]['app[stack]'] || 'bamboo-mri-1.9.2',
65
+ 'requested_stack' => nil,
66
+ 'git_url' => "git@heroku.com:#{app}.git",
67
+ 'repo_migrate_status' => 'complete',
68
+ 'repo_size' => nil,
69
+ 'dynos' => 0,
70
+ 'web_url' => "http://#{app}.herokuapp.com/",
71
+ 'workers' => 0
72
+ }
73
+
74
+ mock_data[:addons][app] = []
75
+ mock_data[:apps] << app_data
76
+ mock_data[:collaborators][app] = [{
77
+ 'access' => 'edit',
78
+ 'email' => 'email@example.com'
79
+ }]
80
+ mock_data[:config_vars][app] = {}
81
+ mock_data[:domains][app] = []
82
+ mock_data[:ps][app] = [{
83
+ 'action' => 'up',
84
+ 'app_name' => app,
85
+ 'attached' => false,
86
+ 'command' => nil, # set by stack below
87
+ 'elapsed' => 0,
88
+ 'pretty_state' => 'created for 0s',
89
+ 'process' => 'web.1',
90
+ 'rendezvous_url' => nil,
91
+ 'slug' => 'NONE',
92
+ 'state' => 'created',
93
+ 'transitioned_at' => app_data['created_at'],
94
+ 'type' => nil, # set by stack below
95
+ 'upid' => rand(99999999).to_s
96
+ }]
97
+ mock_data[:releases][app] = []
98
+
99
+ add_mock_app_addon(mock_data, app, 'logging:basic')
100
+ if stack == 'cedar'
101
+ add_mock_app_addon(mock_data, app, 'releases:basic')
102
+ mock_data[:ps][app].first['type'] = 'Ps'
103
+ else
104
+ add_mock_app_addon(mock_data, app, 'shared-database:5mb')
105
+ mock_data[:config_vars][app] = {
106
+ 'BUNDLE_WITHOUT' => 'development:test',
107
+ 'LANG' => 'en_US.UTF-8',
108
+ 'RACK_ENV' => 'production'
109
+ }
110
+ mock_data[:ps][app].first['command'] = 'thin -p $PORT -e $RACK_ENV -R $HEROKU_RACK start'
111
+ mock_data[:ps][app].first['type'] = 'Dyno'
112
+ end
113
+
114
+ {
115
+ :body => Heroku::API::OkJson.encode(app_data),
116
+ :status => 202
117
+ }
118
+ end
119
+ end
120
+
121
+ # stub POST /apps/:app/server/maintenance
122
+ Excon.stub(:expects => 200, :method => :post, :path => %r{^/apps/([^/]+)/server/maintenance$}) do |params|
123
+ request_params, mock_data = parse_stub_params(params)
124
+ app, _ = request_params[:captures][:path].first
125
+
126
+ with_mock_app(mock_data, app) do
127
+ case request_params[:query]['maintenance_mode']
128
+ when '0'
129
+ mock_data[:maintenance_mode] -= [app]
130
+ when '1'
131
+ mock_data[:maintenance_mode] |= [app]
132
+ end
133
+
134
+ {
135
+ :status => 200
136
+ }
137
+ end
138
+ end
139
+
140
+ # stub PUT /apps/:app
141
+ Excon.stub(:expects => 200, :method => :put, :path => %r{^/apps/([^/]+)$}) do |params|
142
+ request_params, mock_data = parse_stub_params(params)
143
+ app, _ = request_params[:captures][:path]
144
+
145
+ with_mock_app(mock_data, app) do |app_data|
146
+ if request_params[:query].has_key?('app[name]')
147
+ app_data['name'] = request_params[:query]['app[name]']
148
+ end
149
+ if request_params[:query].has_key?('app[transfer_owner]')
150
+ email = request_params[:query]['app[transfer_owner]']
151
+ if collaborator = get_mock_collaborator(mock_data, app, email)
152
+ app_data['owner_email'] = email
153
+ end
154
+ end
155
+ if email && !collaborator
156
+ {
157
+ :body => Heroku::API::OkJson.encode('error' => 'Only existing collaborators can receive ownership for an app'),
158
+ :status => 422
159
+ }
160
+ else
161
+ {
162
+ :body => Heroku::API::OkJson.encode('name' => app_data['name']),
163
+ :status => 200
164
+ }
165
+ end
166
+ end
167
+ end
168
+
169
+ # stub PUT /apps/:app/status
170
+ Excon.stub(:method => :put, :path => %r{^/apps/([^/]+)/status}) do |params|
171
+ request_params, mock_data = parse_stub_params(params)
172
+ app, _ = request_params[:captures][:path]
173
+
174
+ with_mock_app(mock_data, app) do |app_data|
175
+ {
176
+ :body => Heroku::API::OkJson.encode({}),
177
+ :status => 201
178
+ }
179
+ end
180
+ end
181
+
182
+ end
183
+ end
184
+ end