addons-client 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .*.sw?
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - 1.9.3
5
+ notifications:
6
+ email:
7
+ - csquared@heroku.com
8
+ - glenn@heroku.com
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in addons-client.gemspec
4
+ gemspec
5
+ gem "rake"
6
+
7
+ group :test do
8
+ gem 'rr'
9
+ gem 'webmock'
10
+ gem 'ruby-debug19', :platforms => [:mri_19]
11
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Chris Continanza
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,133 @@
1
+ # Addons::Client
2
+
3
+ [![Build Status](https://secure.travis-ci.org/heroku/addons-client.png?branch=master)](http://travis-ci.org/heroku/addons-client)
4
+
5
+ ## codename: Kaikei
6
+
7
+ The addons client is a Ruby library that creates the RESTful requests that are used to interact with the Add-on Platform API.
8
+
9
+ The Platform API provides 3 main functions for Add-ons: provisioning, deprovisioning, and plan change.
10
+ Historically, the heroku module "core" was responsible for sending the appropriate messages to
11
+ add-on providers and reacting correctly according to those responses. Core will still have to react
12
+ to api errors, but will no longer send messages to providers.
13
+
14
+ The Addons Client represents the first attempt at having a well-defined interface between a Platform and these
15
+ add-on related API interactions. It is an implementation of the following API: https://gist.github.com/079c98529d399bb08c7a
16
+
17
+ Also, we have provided a command line client (the real first consumer of the API) so we could issue API requests
18
+ to the add-ons app without having to fire up a console session.
19
+
20
+ ## Installation
21
+
22
+ ### make a test directory
23
+
24
+ mkdir client-test
25
+ cd client-test
26
+
27
+ ### make a Gemfile with the gem on github
28
+
29
+ echo "source :rubygems" > Gemfile
30
+ echo "gem 'addons-client', :git => 'git@github.com:heroku/addons-client.git'" >> Gemfile
31
+
32
+ ### use bundler to install the Gem from github
33
+
34
+ bundle install
35
+
36
+ ### set up ENV
37
+
38
+ export ADDONS_API_URL=https://heroku:password@localhost:3000
39
+
40
+ ### it works
41
+
42
+ bundle exec addons-client
43
+ Command must be one of: provision, deprovision, planchange
44
+
45
+ Remember to use bundle exec to run the command line commands!
46
+
47
+ ## Ruby Usage
48
+
49
+ ```ruby
50
+ client = Addons::Client.new
51
+ # Addons::UserError: ADDONS_API_URL must be set
52
+
53
+ ENV['ADDONS_API_URL']='http://heroku:password@localhost:3000'
54
+
55
+ client = Addons::Client.new
56
+ ```
57
+
58
+ ### API Methods
59
+ ```ruby
60
+ client.provision! 'memcache:5mb'
61
+
62
+ client.provision! 'foo:bar', :consumer_id => 'app123@heroku.com',
63
+ :options => { :foo => 'bar', 'baz' => 'test' }
64
+
65
+ # => {:resource_id=>"DEADBEEF",
66
+ # :config=>{"FOO_URL"=>"http://foo.com"},
67
+ # :message=>"great success",
68
+ # :provider_id=>"ABC123"}
69
+
70
+ client.plan_change! 'ABC123', 'new_plan'
71
+
72
+ client.deprovision! 'ABC123'
73
+ ```
74
+
75
+ ## Command Line Usage
76
+ export ADDONS_API_URL=http://heroku:password@localhost:3000
77
+
78
+ addons-client provision memcache:5mb --consumer-id=app123@heroku.com --options.foo=bar --options.baz=true
79
+
80
+ #### Provisioning:
81
+
82
+ addons-client provision glenntest:test
83
+ {"resource_id":"3bdb228d-a94e-4135-b19f-7a17a9f4f481","config":null,"message":null,"provider_id":null}
84
+
85
+ ### use the resource id to interact with the add-on
86
+
87
+ addons-client plan_change 0dedb8f4-2921-42b8-81b9-a7df4c551140 test
88
+
89
+ addons-client deprovision 0dedb8f4-2921-42b8-81b9-a7df4c551140
90
+ Deprovisioned 0dedb8f4-2921-42b8-81b9-a7df4c551140
91
+
92
+ ### fun with options
93
+
94
+ addons-client provision foo-bar:test --consumer_id=resource123@heroku.com --options.message='Good job'
95
+
96
+
97
+ ## enabling api requests
98
+ Notice the provider_id is null.
99
+ This is because we won't send live requests until we've enabled the switch.
100
+ And also because in a later story we will want to have this toggleable on a per-request basis.
101
+
102
+ heroku config:add PROVIDER_API_ENABLED=true --app addons-staging
103
+
104
+ ### it should now create a resource with a provider_id
105
+
106
+ addons-client provision foo-bar:test
107
+ Provisioned foo-bar:test
108
+ {"resource_id":"0dedb8f4-2921-42b8-81b9-a7df4c551140","config":{"MYADDON_URL":"http://user.yourapp.com"},"message":null,"provider_id":2}
109
+
110
+ ## Test Usage
111
+
112
+ The client supports a mocked mode that sends no requests and returns canned responses.
113
+
114
+ ```ruby
115
+ Addons::Client.mock!
116
+ Addons::Client.new.provision! 'foo:bar'
117
+ # => {:resource_id=>"DEADBEEF",
118
+ # :config=>{"FOO_URL"=>"http://foo.com"},
119
+ # :message=>"great success",
120
+ # :provider_id=>"ABC123"}
121
+
122
+ Addons::Client.unmock!
123
+ Addons::Client.new.provision! 'foo:bar'
124
+ # Addons::UserError: ADDONS_API_URL must be set
125
+ ```
126
+
127
+ ## Contributing
128
+
129
+ 1. Fork it
130
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
131
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
132
+ 4. Push to the branch (`git push origin my-new-feature`)
133
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rake/testtask"
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "test"
7
+ t.test_files = FileList['test/*_test.rb']
8
+ t.verbose = true
9
+ end
10
+
11
+ task :default => :test
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $LOAD_PATH.unshift File.dirname(File.expand_path('.', __FILE__)) + '/lib'
3
+ require "addons-client/version"
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.authors = ["Chris Continanza"]
7
+ gem.email = ["csquared@gmail.com"]
8
+ gem.description = %q{Addons Platform API client}
9
+ gem.summary = %q{Allows platfomrs to provision, deprovision, and change plans for add-on resources.}
10
+ gem.homepage = ""
11
+
12
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
13
+ gem.files = `git ls-files`.split("\n")
14
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ gem.name = "addons-client"
16
+ gem.require_paths = ["lib"]
17
+ gem.version = Addons::Client::VERSION
18
+
19
+ gem.add_dependency 'rest-client'
20
+ gem.add_dependency 'configliere'
21
+ gem.add_dependency 'json'
22
+ end
data/bin/addons-client ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require_relative '../lib/addons-client'
5
+
6
+ begin
7
+ Addons::CLI.run!
8
+ rescue Addons::UserError => e
9
+ STDERR.puts e.message and exit 1
10
+ rescue RestClient::ServiceUnavailable => e
11
+ if ! e.response.body.strip.empty?
12
+ STDERR.puts JSON.parse(e.response.body)['message']
13
+ else
14
+ STDERR.puts "Add-on is not available"
15
+ end
16
+ exit 1
17
+ end
18
+
@@ -0,0 +1,56 @@
1
+ module Addons::CLI
2
+ extend self
3
+
4
+ def run!
5
+ load_settings!
6
+ run_command!
7
+ end
8
+
9
+ def run_command!
10
+ command = Settings.rest.first
11
+ case command
12
+ when /deprovision/i
13
+ resource_id = Settings.rest[1]
14
+ raise Addons::UserError, "Must supply resource id" unless resource_id
15
+ response = client.deprovision!(resource_id)
16
+ puts "Deprovisioned #{resource_id}"
17
+ puts response
18
+ when /provision/i
19
+ slug = Settings.rest[1]
20
+ raise Addons::UserError, "Must supply add-on:plan" unless slug
21
+ response = client.provision!(slug, :options => Settings[:options],
22
+ :consumer_id => Settings[:consumer_id])
23
+ puts "Provisioned #{slug}"
24
+ puts response
25
+ when /plan-change/i
26
+ resource_id = Settings.rest[1]
27
+ raise Addons::UserError, "Must supply resource id" unless resource_id
28
+ plan = Settings.rest[2]
29
+ raise Addons::UserError, "Must supply plan after resource id" unless plan
30
+ response = client.plan_change!(resource_id, plan)
31
+ puts "Plan Changed to #{plan}"
32
+ puts response
33
+ else
34
+ if command
35
+ puts "#{command} is not a valid command"
36
+ else
37
+ puts "Command must be one of: provision, deprovision, plan-change"
38
+ end
39
+ end
40
+ end
41
+
42
+ def client
43
+ @client ||= Addons::Client
44
+ end
45
+
46
+ def puts(string)
47
+ STDOUT.puts(string)
48
+ end
49
+
50
+ def load_settings!
51
+ Settings.use :commandline
52
+ Settings.resolve!
53
+ rescue RuntimeError => e
54
+ raise Addons::UserError.new(e)
55
+ end
56
+ end
@@ -0,0 +1,89 @@
1
+ module Addons
2
+ class Client
3
+ extend Mock::Methods
4
+ extend Mock::Responses
5
+ DEFAULT_CONSUMER_ID = "api-client@localhost"
6
+
7
+ def self.api_url
8
+ @api_url = validate_api_url!
9
+ end
10
+
11
+ def self.provision!(slug, opts = {})
12
+ wrap_request do
13
+ addon_name, plan = slug.split(':')
14
+ raise UserError, "No add-on name given" unless addon_name
15
+ raise UserError, "No plan name given" unless plan
16
+
17
+ if mocked?
18
+ mocked_provision(addon_name)
19
+ else
20
+ payload = {
21
+ :addon => addon_name,
22
+ :plan => plan,
23
+ :consumer_id => opts[:consumer_id] || DEFAULT_CONSUMER_ID
24
+ }
25
+ payload.merge! :options => opts[:options] if opts[:options]
26
+ payload.merge! :rate => opts[:rate] if opts[:rate]
27
+ payload.merge! :addons_id => opts[:addons_id] if opts[:addons_id]
28
+ if start_at = opts[:start_at]
29
+ start_at.utc
30
+ payload.merge! :start_at => start_at.to_s
31
+ end
32
+ if end_at = opts[:end_at]
33
+ end_at.utc
34
+ payload.merge! :end_at => end_at.to_s
35
+ end
36
+ resource.post payload, :accept => :json
37
+ end
38
+ end
39
+ end
40
+
41
+ def self.deprovision!(resource_id)
42
+ wrap_request do
43
+ if mocked?
44
+ mocked_deprovision(resource_id)
45
+ else
46
+ resource["/#{resource_id}"].delete :accept => :json
47
+ end
48
+ end
49
+ end
50
+
51
+ def self.plan_change!(resource_id, plan)
52
+ wrap_request do
53
+ if mocked?
54
+ mocked_plan_change(resource_id, plan)
55
+ else
56
+ payload = {
57
+ :plan => plan,
58
+ }
59
+ resource["/#{resource_id}"].put payload, :accept => :json
60
+ end
61
+ end
62
+ end
63
+
64
+ def self.resource
65
+ RestClient::Resource.new(api_url.to_s)
66
+ end
67
+
68
+ protected
69
+ def self.wrap_request
70
+ response = yield
71
+ Addons::Client::Response.new(response)
72
+ rescue RestClient::ResourceNotFound
73
+ raise UserError, "Add-on not found: check addon spelling and plan name"
74
+ end
75
+
76
+ def self.validate_api_url!
77
+ api_url = nil
78
+ raise UserError, "ADDONS_API_URL must be set" unless ENV['ADDONS_API_URL']
79
+ begin
80
+ api_url = URI.join(ENV['ADDONS_API_URL'], '/api/1/resources')
81
+ rescue URI::InvalidURIError
82
+ raise UserError, "ADDONS_API_URL is an invalid url"
83
+ end
84
+ raise UserError, "No username given" unless api_url.user
85
+ raise UserError, "No password given" unless api_url.password
86
+ api_url
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,44 @@
1
+ module Addons
2
+ class Client
3
+ module Mock
4
+ module Methods
5
+ def mock!
6
+ @mock = true
7
+ end
8
+
9
+ def unmock!
10
+ @mock = false
11
+ end
12
+
13
+ def mocked?
14
+ @mock
15
+ end
16
+ end
17
+
18
+ module Responses
19
+ private
20
+ def mocked_provision(name)
21
+ {
22
+ 'resource_id' => "DEADBEEF",
23
+ 'config' => {"#{name.upcase}_URL" => 'http://foo.com'},
24
+ 'message' => "great success",
25
+ 'provider_id' => 'ABC123'
26
+ }
27
+ end
28
+
29
+ def mocked_deprovision(resource_id)
30
+ {}
31
+ end
32
+
33
+ def mocked_plan_change(resource_id, plan)
34
+ {
35
+ 'resource_id' => resource_id,
36
+ 'config' => {"#{plan.upcase}_URL" => 'http://foo.com'},
37
+ 'message' => "great success",
38
+ 'provider_id' => 'ABC123'
39
+ }
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,29 @@
1
+ module Addons
2
+ class Client
3
+ class Response
4
+ def initialize(response)
5
+ @data = case response
6
+ when String
7
+ response.strip.empty? ? {} : JSON.parse(response)
8
+ when Hash
9
+ response
10
+ end
11
+ @data
12
+ end
13
+
14
+ def to_s
15
+ @data.to_s
16
+ end
17
+
18
+ def method_missing(name, *args, &blk)
19
+ if @data.keys.include? name
20
+ @data[name]
21
+ elsif @data.keys.include? name.to_s
22
+ @data[name.to_s]
23
+ else
24
+ super
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
File without changes
@@ -0,0 +1,5 @@
1
+ module Addons
2
+ class Client
3
+ VERSION = "0.0.2"
4
+ end
5
+ end
@@ -0,0 +1,17 @@
1
+ $LOAD_PATH.unshift File.dirname(File.expand_path('..', __FILE__)) + '/lib'
2
+
3
+ require "addons-client/version"
4
+ require 'rest-client'
5
+ require 'digest'
6
+ require 'configliere'
7
+ require 'json'
8
+
9
+ module Addons
10
+ Exception = Class.new(Exception)
11
+ UserError = Class.new(RuntimeError)
12
+ end
13
+
14
+ require 'addons-client/mock'
15
+ require 'addons-client/client'
16
+ require 'addons-client/cli'
17
+ require 'addons-client/response'
@@ -0,0 +1,33 @@
1
+ require "#{File.dirname(__FILE__)}/test_helper"
2
+
3
+ class SettingsTest < Addons::Client::TestCase
4
+ def setup
5
+ stub_request(:any, /api\/1\/resources/)
6
+ ENV['ADDONS_API_URL'] = 'https://test:password@localhost:3333'
7
+ end
8
+
9
+ def test_client_uses_env_var
10
+ Addons::Client.provision! 'foo:bar'
11
+ assert_requested(:post, URI.join(ENV['ADDONS_API_URL'], '/api/1/resources').to_s)
12
+ end
13
+
14
+ def test_client_raises_error_on_bad_url
15
+ [
16
+ nil,
17
+ 'https://localhost:3333',
18
+ 'https://foo@localhost:3333',
19
+ ].each do |url|
20
+ ENV['ADDONS_API_URL'] = url
21
+ assert_raises Addons::UserError do
22
+ Addons::Client.provision! 'foo:bar'
23
+ end
24
+ end
25
+ end
26
+
27
+ def test_client_handles_404s_gracefully
28
+ stub_request(:any, /api\/1\/resources/).to_return(:status => 404)
29
+ assert_raises Addons::UserError do
30
+ Addons::Client.provision! 'foo:bar'
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,27 @@
1
+ require "#{File.dirname(__FILE__)}/test_helper"
2
+
3
+ class DeprovisionTest < Addons::Client::TestCase
4
+ def setup
5
+ ENV["ADDONS_API_URL"] = 'https://foo:bar@heroku.com'
6
+ stub_request(:any, target_url).to_return :body => " "
7
+ stub(Addons::CLI).puts
8
+ end
9
+
10
+ alias :target_url :resource_url
11
+
12
+ def test_cmd_line_requires_resource_id
13
+ assert_raises Addons::UserError, "Must supply resource id" do
14
+ addons_client! "deprovision"
15
+ end
16
+ end
17
+
18
+ def test_deprovisions_from_cmd_line
19
+ addons_client! "deprovision addons-uuid"
20
+ assert_requested(:delete, target_url)
21
+ end
22
+
23
+ def test_deprovisions_from_ruby
24
+ Addons::Client.deprovision! "addons-uuid"
25
+ assert_requested(:delete, target_url)
26
+ end
27
+ end
@@ -0,0 +1,53 @@
1
+ require "#{File.dirname(__FILE__)}/test_helper"
2
+
3
+ class PlanChangeTest < Addons::Client::TestCase
4
+ def setup
5
+ ENV["ADDONS_API_URL"] = 'https://foo:bar@heroku.com/api/1/resources'
6
+ stub_request(:any, target_url)
7
+ stub(Addons::CLI).puts
8
+ end
9
+
10
+ alias :target_url :resource_url
11
+
12
+ def test_plan_change_from_cmd_line
13
+ addons_client! "plan-change addons-uuid 5mb"
14
+ assert_requested(:put, target_url,
15
+ :body => { :plan => '5mb' })
16
+ end
17
+
18
+ def test_plan_change_from_ruby
19
+ Addons::Client.plan_change! 'addons-uuid', 'plizzan'
20
+ assert_requested(:put, target_url,
21
+ :body => { :plan => 'plizzan' })
22
+ end
23
+
24
+ def test_client_sets_plan_change_options
25
+ Addons::Client.plan_change! 'addons-uuid', 'bar'
26
+
27
+ assert_requested(:put, target_url,
28
+ :body => { :plan => 'bar' })
29
+ end
30
+
31
+ def test_cmd_line_sets_plan_change_options
32
+ addons_client! "plan-change addons-uuid bar"
33
+
34
+ assert_requested(:put, target_url,
35
+ :body => { :plan => 'bar' })
36
+ assert_received(Addons::CLI) do |cli|
37
+ cli.puts "Plan Changed to bar"
38
+ end
39
+ end
40
+
41
+ def test_cmd_line_requires_resource_id
42
+ assert_raises Addons::UserError, "Must supply resource id" do
43
+ addons_client! "provision"
44
+ end
45
+ end
46
+
47
+ def test_cmd_line_requires_plan_name
48
+ assert_raises Addons::UserError, "Must supply plan after resource id" do
49
+ addons_client! "provision ABC-123"
50
+ end
51
+ end
52
+
53
+ end
@@ -0,0 +1,69 @@
1
+ require "#{File.dirname(__FILE__)}/test_helper"
2
+
3
+ class ProvisionTest < Addons::Client::TestCase
4
+ def setup
5
+ ENV["ADDONS_API_URL"] = 'https://foo:bar@heroku.com'
6
+ stub_request(:any, target_url)
7
+ stub(Addons::CLI).puts
8
+ end
9
+
10
+ def target_url
11
+ URI.join(ENV["ADDONS_API_URL"], '/api/1/resources').to_s
12
+ end
13
+
14
+ def test_cmd_line_requires_addon_slug
15
+ assert_raises Addons::UserError, "Must supply addon:plan" do
16
+ addons_client! "provision"
17
+ end
18
+ end
19
+
20
+ def test_cmd_line_requires_plan
21
+ assert_raises Addons::UserError, "No plan name given" do
22
+ addons_client! "provision foo"
23
+ end
24
+ end
25
+
26
+ def test_provisions_from_cmd_line
27
+ addons_client! "provision memcache:5mb"
28
+ assert_requested(:post, target_url,
29
+ :body => { :addon => 'memcache', :plan => '5mb',
30
+ :consumer_id => 'api-client@localhost'})
31
+ end
32
+
33
+ def test_provisions_from_ruby
34
+ Addons::Client.provision! 'foo:plizzan'
35
+ assert_requested(:post, target_url,
36
+ :body => { :addon => 'foo', :plan => 'plizzan',
37
+ :consumer_id => 'api-client@localhost'})
38
+ end
39
+
40
+ def test_sets_consumer_id
41
+ Addons::Client.provision! 'foo:bar', :consumer_id => 'app123@heroku.com'
42
+ assert_requested(:post, target_url,
43
+ :body => { :addon => 'foo', :plan => 'bar',
44
+ :consumer_id => 'app123@heroku.com'})
45
+ end
46
+
47
+ def test_client_sets_provision_options
48
+ Addons::Client.provision! 'foo:bar',
49
+ :consumer_id => 'app123@heroku.com',
50
+ :options => { :foo => 'bar', 'baz' => 'test' }
51
+
52
+ assert_requested(:post, target_url,
53
+ :body => { :addon => 'foo', :plan => 'bar',
54
+ :consumer_id => 'app123@heroku.com',
55
+ :options => { :foo => 'bar', :baz => 'test'}})
56
+ end
57
+
58
+ def test_cmd_line_sets_provision_options
59
+ addons_client! "provision foo:bar --options.foo=bar --options.baz=test --consumer_id=app123@heroku.com"
60
+
61
+ assert_requested(:post, target_url,
62
+ :body => { :addon => 'foo', :plan => 'bar',
63
+ :consumer_id => 'app123@heroku.com',
64
+ :options => { :foo => 'bar', :baz => 'test'}})
65
+ assert_received(Addons::CLI) do |cli|
66
+ cli.puts "Provisioned foo:bar"
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,55 @@
1
+ require "#{File.dirname(__FILE__)}/test_helper"
2
+
3
+ # testing the canned responses with the mocks
4
+ # means we are both testing the mock mode and
5
+ # keeping the mocked responses in one place
6
+ class ResponseTest < Addons::Client::TestCase
7
+ def setup
8
+ super
9
+ Addons::Client.mock!
10
+ end
11
+
12
+ def teardown
13
+ super
14
+ Addons::Client.unmock!
15
+ end
16
+
17
+ def target_url
18
+ URI.join(ENV["ADDONS_API_URL"], '/api/1/resources').to_s
19
+ end
20
+
21
+ def test_response_parses_json
22
+ Addons::Client.unmock!
23
+ stub_request(:any, target_url).to_return(:body => {
24
+ 'resource_id' => '123-ABC',
25
+ 'config' => {'ADDON_URL' => 'foo'},
26
+ 'message' => 'great success',
27
+ 'provider_id' => '42'
28
+ }.to_json)
29
+
30
+ response = Addons::Client.provision! 'memcache:5mb'
31
+
32
+ assert_equal '123-ABC', response.resource_id
33
+ assert_equal 'foo', response.config['ADDON_URL']
34
+ assert_equal 'great success', response.message
35
+ assert_equal '42', response.provider_id
36
+ end
37
+
38
+ def test_response_object_wraps_provision_data
39
+ response = Addons::Client.provision! 'memcache:5mb'
40
+
41
+ assert_equal 'DEADBEEF', response.resource_id
42
+ assert_equal 'http://foo.com', response.config['MEMCACHE_URL']
43
+ assert_equal 'great success', response.message
44
+ assert_equal 'ABC123', response.provider_id
45
+ end
46
+
47
+ def test_response_object_wraps_deprovision
48
+ response = Addons::Client.deprovision! 'UUID'
49
+ end
50
+
51
+ def test_response_object_wraps_plan_change
52
+ response = Addons::Client.plan_change! 'UUID', 'plan'
53
+ end
54
+ end
55
+
@@ -0,0 +1,32 @@
1
+ require 'test/unit'
2
+ Bundler.require :test
3
+ require 'webmock/test_unit'
4
+ require "#{File.dirname(__FILE__)}/../lib/addons-client"
5
+
6
+ class Addons::Client::TestCase < Test::Unit::TestCase
7
+ include RR::Adapters::TestUnit
8
+
9
+ def setup
10
+ super
11
+ end
12
+
13
+ def resource_url
14
+ URI.join(ENV["ADDONS_API_URL"], '/api/1/resources/', 'addons-uuid').to_s
15
+ end
16
+
17
+ def teardown
18
+ super
19
+ WebMock.reset!
20
+ ::ARGV.replace []
21
+ Settings.replace Hash.new
22
+ end
23
+
24
+ def addons_client! cmd
25
+ ::ARGV.replace cmd.split
26
+ Addons::CLI.run!
27
+ end
28
+
29
+ # for ruby 1.8.7 Test::Unit compatibility
30
+ def default_test
31
+ end
32
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: addons-client
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 2
10
+ version: 0.0.2
11
+ platform: ruby
12
+ authors:
13
+ - Chris Continanza
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-04-29 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ prerelease: false
22
+ type: :runtime
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ name: rest-client
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ prerelease: false
36
+ type: :runtime
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ name: configliere
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ prerelease: false
50
+ type: :runtime
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ name: json
61
+ version_requirements: *id003
62
+ description: Addons Platform API client
63
+ email:
64
+ - csquared@gmail.com
65
+ executables:
66
+ - addons-client
67
+ extensions: []
68
+
69
+ extra_rdoc_files: []
70
+
71
+ files:
72
+ - .gitignore
73
+ - .travis.yml
74
+ - Gemfile
75
+ - LICENSE
76
+ - README.md
77
+ - Rakefile
78
+ - addons-client.gemspec
79
+ - bin/addons-client
80
+ - lib/addons-client.rb
81
+ - lib/addons-client/cli.rb
82
+ - lib/addons-client/client.rb
83
+ - lib/addons-client/mock.rb
84
+ - lib/addons-client/response.rb
85
+ - lib/addons-client/settings.rb
86
+ - lib/addons-client/version.rb
87
+ - test/client_test.rb
88
+ - test/deprovision_test.rb
89
+ - test/plan_change_test.rb
90
+ - test/provision_test.rb
91
+ - test/response_test.rb
92
+ - test/test_helper.rb
93
+ homepage: ""
94
+ licenses: []
95
+
96
+ post_install_message:
97
+ rdoc_options: []
98
+
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ hash: 3
107
+ segments:
108
+ - 0
109
+ version: "0"
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ hash: 3
116
+ segments:
117
+ - 0
118
+ version: "0"
119
+ requirements: []
120
+
121
+ rubyforge_project:
122
+ rubygems_version: 1.8.15
123
+ signing_key:
124
+ specification_version: 3
125
+ summary: Allows platfomrs to provision, deprovision, and change plans for add-on resources.
126
+ test_files:
127
+ - test/client_test.rb
128
+ - test/deprovision_test.rb
129
+ - test/plan_change_test.rb
130
+ - test/provision_test.rb
131
+ - test/response_test.rb
132
+ - test/test_helper.rb