ey_services_api 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,8 @@
1
+ .DS_Store
2
+ *.gem
3
+ .bundle
4
+ Gemfile.lock
5
+ InternalGemfile.lock
6
+ pkg/*
7
+ .rvmrc
8
+ coverage
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --format documentation --colour
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in ey_services_api.gemspec
4
+ gemspec
5
+
6
+ group :test, :development do
7
+ gem 'rake'
8
+ gem 'sinatra'
9
+ gem 'rcov'
10
+ end
data/InternalGemfile ADDED
@@ -0,0 +1,19 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in ey_services_api.gemspec
4
+ gemspec
5
+
6
+ group :test, :development do
7
+ gem 'rake'
8
+ gem 'ey_sso', :git => "git@github.com:engineyard/ey_sso.git"
9
+
10
+ # gem 'tresfiestas', :git => "git@github.com:engineyard/tresfiestas.git"
11
+ gem 'tresfiestas', :path => "../tresfiestas"
12
+
13
+ #TODO: this should just be a dep of tresfiestas
14
+ gem 'lisonja', :path => "../lisonja"
15
+ #TODO: this should just be a dep of lisonja
16
+ gem 'ey_services_api', :path => "../ey_services_api"
17
+
18
+ gem 'sinatra'
19
+ end
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # EY Services API
2
+
3
+ This gem provides basic ability to interact with Engine Yard services. (http://services.engineyard.com/)
4
+
5
+ All operations happen on the connection. First it must be setup. For example:
6
+
7
+ ```ruby
8
+ EY::ServicesAPI.setup(:auth_id => "...", :auth_key => "...")
9
+ ```
10
+
11
+ Then you can do things like register a new service. For example:
12
+
13
+ ```ruby
14
+ EY::ServicesAPI.connection.register_service(
15
+ "http://services.engineyard.com/api/1/partners/1/services", {
16
+ :name => "My Service",
17
+ :description => "my service does things",
18
+ :service_accounts_url => "http://my-service.example.com/api/1/customers/fancy",
19
+ :home_url => "http://my-service.example.com/",
20
+ :vars => ["MY_SERVICE_API_KEY"] })
21
+ ```
22
+
23
+ ## To run the tests
24
+
25
+ To run specs mocked:
26
+
27
+ * rvm use 1.8.7
28
+ * bundle
29
+ * bundle exec rake
30
+
31
+ To run against tresfiestas codebase: (internal only)
32
+
33
+ * rvm use 1.9.2
34
+ * BUNDLE_GEMFILE=InternalGemfile bundle
35
+ * BUNDLE_GEMFILE=InternalGemfile bundle exec rake
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc 'Default: run specs.'
5
+ task :default => :spec
6
+
7
+ desc "Run specs"
8
+ RSpec::Core::RakeTask.new do |t|
9
+ # Put spec opts in a file named .rspec in root
10
+ t.rcov = true
11
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "ey_services_api/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "ey_services_api"
7
+ s.version = EY::ServicesAPI::VERSION
8
+ s.authors = ["Jacob Burkhart & Thorben Schröder & David Calavera & Michael Brodhead & Others"]
9
+ s.email = ["jacob@engineyard.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{API for Partner Services (talks to services.engineyard.com)}
12
+ s.description = %q{API for Partner Services (talks to services.engineyard.com)}
13
+
14
+ s.rubyforge_project = "ey_services_api"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency 'rspec'
22
+ s.add_dependency 'json'
23
+ s.add_dependency 'ey_api_hmac'
24
+ end
@@ -0,0 +1,40 @@
1
+ require "ey_services_api/version"
2
+ require "ey_services_api/api_struct"
3
+ require "ey_services_api/connection"
4
+ require "ey_services_api/service"
5
+ require "ey_services_api/message"
6
+ require "ey_services_api/invoice"
7
+ require "ey_services_api/service_account_creation"
8
+ require "ey_services_api/service_account_response"
9
+ require "ey_services_api/provisioned_service_creation"
10
+ require "ey_services_api/provisioned_service_response"
11
+
12
+ module EY
13
+ module ServicesAPI
14
+
15
+ def self.setup!(opts)
16
+ @connection = Connection.new(opts[:auth_id], opts[:auth_key])
17
+ end
18
+
19
+ def self.connection
20
+ @connection or raise "Not setup!"
21
+ end
22
+
23
+ def self.enable_mock!(provider = nil)
24
+ unless @mock_backend
25
+ unless provider
26
+ require "ey_services_api/test/tresfiestas_fake"
27
+ provider = TresfiestasFake
28
+ end
29
+ @mock_backend = provider.setup!
30
+ end
31
+ @mock_backend.reset!
32
+ @mock_backend.initialize_api_connection
33
+ end
34
+
35
+ def self.mock_backend
36
+ @mock_backend
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,18 @@
1
+ class APIStruct < Struct
2
+ def initialize(atts = {})
3
+ #converting all keys of atts to Symbols
4
+ atts = Hash[atts.map {|k,v| [k.to_sym, v]}]
5
+ super(*atts.values_at(*self.members.map(&:to_sym)))
6
+ end
7
+
8
+ def to_hash
9
+ Hash[members.map(&:to_sym).zip(entries)]
10
+ end
11
+
12
+ protected
13
+ def update_from_hash(atts)
14
+ atts.each do |k, v|
15
+ self.send("#{k}=", v)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,60 @@
1
+ require 'rack/client'
2
+ require 'json'
3
+ require 'ey_api_hmac'
4
+
5
+ module EY
6
+ module ServicesAPI
7
+ class Connection < EY::ApiHMAC::BaseConnection
8
+
9
+ def default_user_agent
10
+ "EY-ServicesAPI/#{VERSION}"
11
+ end
12
+
13
+ def list_services(url)
14
+ response = get(url) do |json_body, response_location|
15
+ json_body.map do |json_item|
16
+ service = Service.new(json_item["service"])
17
+ service.connection = self
18
+ service.url = url
19
+ service
20
+ end
21
+ end
22
+ end
23
+
24
+ def register_service(registration_url, params)
25
+ post(registration_url, :service => params) do |json_body, response_location|
26
+ service = Service.new(params)
27
+ service.connection = self
28
+ service.url = response_location
29
+ service
30
+ end
31
+ end
32
+
33
+ def get_service(url)
34
+ response = get(url) do |json_body, response_location|
35
+ service = Service.new(json_body["service"])
36
+ service.connection = self
37
+ service.url = url
38
+ service
39
+ end
40
+ end
41
+
42
+ def update_service(url, params)
43
+ put(url, :service => params)
44
+ end
45
+
46
+ def destroy_service(url)
47
+ delete(url)
48
+ end
49
+
50
+ def send_message(url, message)
51
+ post(url, :message => message.to_hash)
52
+ end
53
+
54
+ def send_invoice(invoices_url, invoice)
55
+ post(invoices_url, :invoice => invoice.to_hash)
56
+ end
57
+
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,6 @@
1
+ module EY
2
+ module ServicesAPI
3
+ class Invoice < APIStruct.new(:total_amount_cents, :line_item_description)
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module EY
2
+ module ServicesAPI
3
+ class Message < APIStruct.new(:message_type, :subject, :body)
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,35 @@
1
+ module EY
2
+ module ServicesAPI
3
+ class ProvisionedServiceCreation < APIStruct.new(:url, :environment, :app, :messages_url)
4
+
5
+ def initialize(*args)
6
+ super(*args)
7
+ self.environment = Environment.new(self.environment)
8
+ self.app = App.new(self.app)
9
+ end
10
+
11
+ def self.from_request(request)
12
+ json = JSON.parse(request)
13
+ new(json)
14
+ end
15
+
16
+ class App < APIStruct.new(:id, :name)
17
+ end
18
+
19
+ class Environment < APIStruct.new(:id, :name, :framework_env)
20
+ end
21
+
22
+ # def environment
23
+ # debugger
24
+ # Environment.new(@environment)
25
+ # end
26
+
27
+ def creation_response_hash
28
+ response_presenter = ProvisionedServiceResponse.new
29
+ yield response_presenter
30
+ response_presenter.to_hash
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,17 @@
1
+ module EY
2
+ module ServicesAPI
3
+ class ProvisionedServiceResponse < Struct.new(:configuration_required, :configuration_url, :message, :vars, :url)
4
+ def to_hash
5
+ {
6
+ :provisioned_service => {
7
+ :url => self.url,
8
+ :configuration_required => self.configuration_required,
9
+ :configuration_url => self.configuration_url,
10
+ :vars => self.vars,
11
+ },
12
+ :message => self.message.to_hash,
13
+ }
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ module EY
2
+ module ServicesAPI
3
+ class Service < APIStruct.new(:name, :description, :home_url, :service_accounts_url, :terms_and_conditions_url, :vars)
4
+ attr_accessor :connection
5
+ attr_accessor :url
6
+
7
+ def update(atts)
8
+ new_atts = self.to_hash.merge(atts)
9
+ connection.update_service(self.url, new_atts)
10
+ update_from_hash(new_atts)
11
+ end
12
+
13
+ def destroy
14
+ connection.destroy_service(self.url)
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ module EY
2
+ module ServicesAPI
3
+ class ServiceAccountCreation < APIStruct.new(:name, :url, :messages_url, :invoices_url)
4
+
5
+ def self.from_request(request)
6
+ json = JSON.parse(request)
7
+ new(json)
8
+ end
9
+
10
+ def creation_response_hash
11
+ response_presenter = ServiceAccountResponse.new
12
+ yield response_presenter
13
+ response_presenter.to_hash
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ module EY
2
+ module ServicesAPI
3
+ class ServiceAccountResponse < Struct.new(:configuration_required, :configuration_url, :message, :provisioned_services_url, :url)
4
+ def to_hash
5
+ {
6
+ :service_account => {
7
+ :url => self.url,
8
+ :configuration_required => self.configuration_required,
9
+ :configuration_url => self.configuration_url,
10
+ :provisioned_services_url => self.provisioned_services_url
11
+ },
12
+ :message => self.message.to_hash
13
+ }
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,253 @@
1
+ class TresfiestasFake
2
+ BASE_URL = "http://mockservice.test"
3
+
4
+ def self.setup!
5
+ @mock_helper = MockHelper.new
6
+ end
7
+ def self.reset!
8
+ @services = {}
9
+ @invoices = []
10
+ @status_messages = []
11
+ end
12
+ def self.services
13
+ @services ||= {}
14
+ end
15
+ def self.invoices
16
+ @invoices ||= []
17
+ end
18
+ def self.status_messages
19
+ @status_messages ||= []
20
+ end
21
+
22
+ def self.mock_helper
23
+ MockHelper.new
24
+ end
25
+
26
+ class MockHelper
27
+ def reset!
28
+ end
29
+ def initialize_api_connection
30
+ EY::ServicesAPI.setup!(:auth_id => "123", :auth_key => "456")
31
+ EY::ServicesAPI.connection.backend = TresfiestasFake::RackApp
32
+ end
33
+
34
+ def partner
35
+ {
36
+ :registration_url => "#{BASE_URL}/api/1/register_a_new_service",
37
+ :auth_id => "123",
38
+ :auth_key => "456",
39
+ }
40
+ end
41
+
42
+ #TODO: make an equivalent in tresfiestas!
43
+ def connection_to_partner
44
+ @connection_to_partner ||= EY::ApiHMAC::BaseConnection.new(partner[:auth_id], partner[:auth_key])
45
+ end
46
+
47
+ def service_registration_params
48
+ {
49
+ :name => "Mocking Bird",
50
+ :description => "a mock service",
51
+ :service_accounts_url => "#{BASE_URL}/api/1/customers/regular",
52
+ :home_url => "#{BASE_URL}/",
53
+ :terms_and_conditions_url => "#{BASE_URL}/terms",
54
+ :vars => [
55
+ "MOCK_API_KEY"
56
+ ]
57
+ }
58
+ end
59
+
60
+ def service_account
61
+ {:invoices_url => "#{BASE_URL}/api/1/invoices/12",
62
+ :messages_url => "#{BASE_URL}/api/1/messages/12",
63
+ :id => 12}
64
+ end
65
+
66
+ #TODO: test this!, put in tresfiestas too!
67
+ def create_service_account
68
+ service = TresfiestasFake.services.values.first
69
+ service_accounts_url = service['service_accounts_url']
70
+ post_params = {
71
+ :name => "my-account",
72
+ # :url => "TODO url",
73
+ :messages_url => "#{BASE_URL}/api/1/messages/???service_account_id???",
74
+ :invoices_url => "#{BASE_URL}/api/1/invoices/???service_account_id???",
75
+ }
76
+ connection_to_partner.post(service_accounts_url, post_params) do |json_body, location|
77
+ service["service_accounts"] ||= {}
78
+ service["service_accounts"][location] = json_body
79
+ end
80
+ end
81
+ #TODO: test this!, put in tresfiestas too!
82
+ def destroy_service_account
83
+ service = TresfiestasFake.services.values.first
84
+ service_account_url = service["service_accounts"].keys.first
85
+ connection_to_partner.delete(service_account_url)
86
+ end
87
+ #TODO: test this!, put in tresfiestas too!
88
+ def create_provisioned_service
89
+ service = TresfiestasFake.services.values.first
90
+ service_account = service["service_accounts"].values.first["service_account"]
91
+ provisioned_services_url = service_account["provisioned_services_url"]
92
+ post_params = {
93
+ # :url => "TODO: url",
94
+ :messages_url => "#{BASE_URL}/api/1/provisioned_service_messages/???provisioned_service_id???",
95
+ :environment => {:id => 5, :name => 'myenv'},
96
+ :app => {:id => 6, :name => "myapp"},
97
+ }
98
+ connection_to_partner.post(provisioned_services_url, post_params) do |json_body, location|
99
+ service_account["provisioned_services"] ||= {}
100
+ service_account["provisioned_services"][location] = json_body
101
+ end
102
+ end
103
+ #TODO: test this!, put in tresfiestas too!
104
+ def destroy_provisioned_service
105
+ service = TresfiestasFake.services.values.first
106
+ service_account = service["service_accounts"].values.first["service_account"]
107
+ provisioned_service_url = service_account["provisioned_services"].keys.first
108
+ connection_to_partner.delete(provisioned_service_url)
109
+ end
110
+ #TODO: tests this, put in tresfiestas too???!
111
+ def created_provisioned_service
112
+ service = TresfiestasFake.services.values.first
113
+ service_account = service["service_accounts"].values.first["service_account"]
114
+ service_account["provisioned_services"].values.first["provisioned_service"]
115
+ end
116
+
117
+ def service_account_creation_request(service_account_hash)
118
+ {}
119
+ end
120
+
121
+ def provisioned_service_creation_request(service_account_hash)
122
+ {:environment => {}, :app => {}}
123
+ end
124
+
125
+ def latest_invoice
126
+ invoice = TresfiestasFake.invoices.last
127
+ {
128
+ :total_amount_cents => invoice['total_amount_cents'],
129
+ :line_item_description => invoice['line_item_description'],
130
+ :service_account_id => invoice['service_account_id'],
131
+ }
132
+ end
133
+
134
+ def latest_status_message
135
+ message = TresfiestasFake.status_messages.last
136
+ to_return = {
137
+ :subject => message["subject"],
138
+ :body => message["body"],
139
+ }
140
+ if message['provisioned_service_id']
141
+ to_return.merge!(:provisioned_service_id => message['provisioned_service_id'])
142
+ end
143
+ to_return
144
+ end
145
+
146
+ def provisioned_service
147
+ {:messages_url => "#{BASE_URL}/api/1/provisioned_service_messages/64",
148
+ :id => 64}
149
+ end
150
+ end
151
+
152
+ class RackApp < Sinatra::Base
153
+ enable :raise_errors
154
+ disable :dump_errors
155
+ disable :show_exceptions
156
+
157
+ #TODO: auth!
158
+ post '/api/1/register_a_new_service' do
159
+ service_id = TresfiestasFake.services.size + 100
160
+ service = JSON.parse(request.body.read)["service"]
161
+ if service["name"].to_s.empty?
162
+ status 400
163
+ {:error_messages => ["Name can't be blank"]}.to_json
164
+ else
165
+ TresfiestasFake.services[service_id.to_s] = service
166
+ status 201
167
+ headers 'Location' => "#{BASE_URL}/api/1/services/#{service_id}"
168
+ {}.to_json
169
+ end
170
+ end
171
+
172
+ get '/api/1/services/:service_id' do |service_id|
173
+ if service = TresfiestasFake.services[service_id.to_s]
174
+ {"service" => service}.to_json
175
+ else
176
+ status 404
177
+ {}.to_json
178
+ end
179
+ end
180
+
181
+ put '/api/1/services/:service_id' do |service_id|
182
+ service = TresfiestasFake.services[service_id.to_s]
183
+ update_params = JSON.parse(request.body.read)["service"]
184
+ if update_params.key?("name") && update_params["name"].to_s.empty?
185
+ status 400
186
+ {:error_messages => ["Name can't be blank"]}.to_json
187
+ else
188
+ service.merge!(update_params)
189
+ {}.to_json
190
+ end
191
+ end
192
+
193
+ delete '/api/1/services/:service_id' do |service_id|
194
+ TresfiestasFake.services.delete(service_id.to_s)
195
+ {}.to_json
196
+ end
197
+
198
+ post '/api/1/invoices/:service_account_id' do |service_account_id|
199
+ invoice_params = JSON.parse(request.body.read)["invoice"]
200
+ unless invoice_params['total_amount_cents'].is_a?(Fixnum)
201
+ status 400
202
+ return {:error_messages => ["Total Amount Cents must be an integer"]}.to_json
203
+ end
204
+ if invoice_params["line_item_description"].to_s.empty?
205
+ status 400
206
+ return {:error_messages => ["Line item description can't be blank"]}.to_json
207
+ end
208
+ if invoice_params['total_amount_cents'] < 0
209
+ status 400
210
+ return {:error_messages => ["Total amount cents must be greater than or equal to 0"]}.to_json
211
+ end
212
+ TresfiestasFake.invoices << invoice_params.merge('service_account_id' => service_account_id.to_i)
213
+ {}.to_json
214
+ end
215
+
216
+ post '/api/1/messages/:service_account_id' do |service_account_id|
217
+ message_params = JSON.parse(request.body.read)["message"]
218
+
219
+ if message_params['subject'].to_s.empty?
220
+ status 400
221
+ return {:error_messages => ["Subject can't be blank."]}.to_json
222
+ end
223
+
224
+ unless ['status', 'notification', 'alert'].include? message_params['message_type']
225
+ status 400
226
+ return {:error_messages => ['Message type must be one of: status, notification or alert']}.to_json
227
+ end
228
+
229
+ TresfiestasFake.status_messages << message_params
230
+ {}.to_json
231
+ end
232
+
233
+
234
+ post '/api/1/provisioned_service_messages/:provisioned_service_id' do |provisioned_service_id|
235
+ message_params = JSON.parse(request.body.read)["message"]
236
+
237
+ if message_params['subject'].to_s.empty?
238
+ status 400
239
+ return {:error_messages => ["Subject can't be blank."]}.to_json
240
+ end
241
+
242
+ unless ['status', 'notification', 'alert'].include? message_params['message_type']
243
+ status 400
244
+ return {:error_messages => ['Message type must be one of: status, notification or alert']}.to_json
245
+ end
246
+
247
+ TresfiestasFake.status_messages << message_params.merge('provisioned_service_id' => provisioned_service_id.to_i)
248
+ {}.to_json
249
+ end
250
+
251
+ end
252
+
253
+ end
@@ -0,0 +1,5 @@
1
+ module EY
2
+ module ServicesAPI
3
+ VERSION = "0.0.2"
4
+ end
5
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+ require 'sinatra'
3
+
4
+ describe EY::ServicesAPI::Invoice do
5
+ before do
6
+ @service_account = @tresfiestas.service_account
7
+ @invoices_url = @service_account[:invoices_url]
8
+ @connection = EY::ServicesAPI.connection
9
+ end
10
+
11
+ it "can send an invoice" do
12
+ invoice = EY::ServicesAPI::Invoice.new(:total_amount_cents => 500, :line_item_description => "good stuff")
13
+ @connection.send_invoice(@invoices_url, invoice)
14
+ latest_invoice = @tresfiestas.latest_invoice
15
+ latest_invoice[:total_amount_cents].should eq 500
16
+ latest_invoice[:line_item_description].should eq "good stuff"
17
+ latest_invoice[:service_account_id].should eq @service_account[:id]
18
+ end
19
+
20
+ it "returns an error for fractional ammounts for total_amount_cents" do
21
+ lambda{
22
+ invoice = EY::ServicesAPI::Invoice.new(:total_amount_cents => 4.50, :line_item_description => "fractional stuff")
23
+ @connection.send_invoice(@invoices_url, invoice)
24
+ }.should raise_error(EY::ServicesAPI::Connection::ValidationError, /Total Amount Cents must be an integer/)
25
+ end
26
+
27
+ it "allows invoices for zero cents" do
28
+ lambda{
29
+ invoice = EY::ServicesAPI::Invoice.new(:total_amount_cents => 0, :line_item_description => "free stuff")
30
+ @connection.send_invoice(@invoices_url, invoice)
31
+ }.should_not raise_error
32
+ end
33
+
34
+ it "returns an error for negative ammounts for total_amount_cents" do
35
+ lambda{
36
+ invoice = EY::ServicesAPI::Invoice.new(:total_amount_cents => -1, :line_item_description => "bad stuff")
37
+ @connection.send_invoice(@invoices_url, invoice)
38
+ }.should raise_error(EY::ServicesAPI::Connection::ValidationError, /Total amount cents must be greater than or equal to 0/)
39
+ end
40
+
41
+ it "returns an error for blank descriptions" do
42
+ lambda{
43
+ invoice = EY::ServicesAPI::Invoice.new(:total_amount_cents => 10, :line_item_description => "")
44
+ @connection.send_invoice(@invoices_url, invoice)
45
+ }.should raise_error(EY::ServicesAPI::Connection::ValidationError, /Line item description can't be blank/)
46
+ end
47
+
48
+ end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+ require 'sinatra'
3
+
4
+ #TODO: support a generic message class too?
5
+ describe EY::ServicesAPI::Message do
6
+ describe "#send_message" do
7
+ describe "with a service account" do
8
+ before do
9
+ @service_account = @tresfiestas.service_account
10
+ @messages_url = @service_account[:messages_url]
11
+ @connection = EY::ServicesAPI.connection
12
+ end
13
+
14
+ it "POSTs to the message callback URL to send a message" do
15
+ message = EY::ServicesAPI::Message.new(:message_type => "status", :subject => "Subjecty", :body => "Whee")
16
+ @connection.send_message(@messages_url, message)
17
+
18
+ latest = @tresfiestas.latest_status_message
19
+ latest.should_not be_empty
20
+ end
21
+
22
+ it "returns an error when the message is not valid" do
23
+ lambda{
24
+ @connection.send_message(@messages_url, EY::ServicesAPI::Message.new(:subject => "", :body => ""))
25
+ }.should raise_error(EY::ServicesAPI::Connection::ValidationError, /Subject can't be blank/)
26
+ end
27
+
28
+ it "returns an error when the message_type is not valid" do
29
+ lambda{
30
+ @connection.send_message(@messages_url, EY::ServicesAPI::Message.new(:message_type => "urgent_reminder", :subject => "valid"))
31
+ }.should raise_error(EY::ServicesAPI::Connection::ValidationError, /Message type must be one of: status, notification or alert/)
32
+ end
33
+
34
+ end
35
+
36
+ describe "with a provisioned service" do
37
+ before do
38
+ @provisioned_service = @tresfiestas.provisioned_service
39
+ @messages_url = @provisioned_service[:messages_url]
40
+ @connection = EY::ServicesAPI.connection
41
+ end
42
+
43
+ it "POSTs to the message callback URL to send a message" do
44
+ message = EY::ServicesAPI::Message.new(:message_type => "status", :subject => "Subjectish", :body => "Bodily")
45
+ @connection.send_message(@messages_url, message)
46
+
47
+ latest = @tresfiestas.latest_status_message
48
+ latest.should_not be_empty
49
+ latest[:provisioned_service_id].should == @provisioned_service[:id]
50
+
51
+ latest[:subject].should === "Subjectish"
52
+ latest[:body].should === "Bodily"
53
+ end
54
+
55
+ it "returns an error when the message is not valid" do
56
+ lambda{
57
+ @connection.send_message(@messages_url, EY::ServicesAPI::Message.new(:message_type => "status", :subject => "", :body => ""))
58
+ }.should raise_error(EY::ServicesAPI::Connection::ValidationError, /Subject can't be blank/)
59
+ end
60
+
61
+ it "returns an error when the message_type is not valid" do
62
+ lambda{
63
+ @connection.send_message(@messages_url, EY::ServicesAPI::Message.new(:message_type => "urgent_reminder", :subject => "valid"))
64
+ }.should raise_error(EY::ServicesAPI::Connection::ValidationError, /Message type must be one of: status, notification or alert/)
65
+ end
66
+
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+ require 'sinatra'
3
+
4
+ describe EY::ServicesAPI::ProvisionedServiceCreation do
5
+ describe "with a service account" do
6
+ before do
7
+ @service_account_hash = @tresfiestas.service_account
8
+ @creation_request = @tresfiestas.provisioned_service_creation_request(@service_account_hash)
9
+ @provisioned_service = EY::ServicesAPI::ProvisionedServiceCreation.from_request(@creation_request.to_json)
10
+ end
11
+
12
+ it "can handle a provisioned service creation request" do
13
+ @provisioned_service.url.should eq @creation_request[:url]
14
+ @provisioned_service.messages_url.should eq @creation_request[:messages_url]
15
+ @provisioned_service.environment.id.should eq @creation_request[:environment][:id]
16
+ @provisioned_service.environment.name.should eq @creation_request[:environment][:name]
17
+ @provisioned_service.environment.framework_env.should eq @creation_request[:environment][:framework_env]
18
+ @provisioned_service.app.id.should eq @creation_request[:app][:id]
19
+ @provisioned_service.app.name.should eq @creation_request[:app][:name]
20
+ end
21
+
22
+ it "can produce a response body hash for provisioned service creation requests" do
23
+ response_hash = @provisioned_service.creation_response_hash do |presenter|
24
+ # presenter.provisioned_services_url = "some provision url"
25
+ presenter.url = "some resource url"
26
+ presenter.vars = {"SOME_ENV_VAR" => "value", "OTHER_VAR" => "blah"}
27
+ presenter.configuration_required = true
28
+ presenter.configuration_url = "some config url" #doesn't even have to be valid here!
29
+ presenter.message = EY::ServicesAPI::Message.new(:message_type => "status", :subject => "some messages")
30
+ end
31
+
32
+ provisioned_service_response = response_hash[:provisioned_service]
33
+ provisioned_service_response[:configuration_required].should be true
34
+ provisioned_service_response[:configuration_url].should eq "some config url"
35
+ provisioned_service_response[:vars].should eq({"SOME_ENV_VAR" => "value", "OTHER_VAR" => "blah"})
36
+ provisioned_service_response[:url].should eq "some resource url"
37
+ response_hash[:message].should eq({:message_type => 'status', :subject => "some messages", :body => nil})
38
+ end
39
+ end
40
+
41
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+ require 'sinatra'
3
+
4
+ describe EY::ServicesAPI::ServiceAccountCreation do
5
+ describe "with a service account" do
6
+ before do
7
+ @service_account_hash = @tresfiestas.service_account
8
+ @creation_request = @tresfiestas.service_account_creation_request(@service_account_hash)
9
+ @service_account = EY::ServicesAPI::ServiceAccountCreation.from_request(@creation_request.to_json)
10
+ end
11
+
12
+ it "can handle a service account creation request" do
13
+ @service_account.url.should eq @creation_request[:url]
14
+ @service_account.messages_url.should eq @creation_request[:messages_url]
15
+ @service_account.invoices_url.should eq @creation_request[:invoices_url]
16
+ @service_account.name.should eq @creation_request[:name]
17
+ end
18
+
19
+ it "can produce a response body hash for service account creation requests" do
20
+ response_hash = @service_account.creation_response_hash do |presenter|
21
+ presenter.provisioned_services_url = "some provision url"
22
+ presenter.url = "some resource url"
23
+ presenter.configuration_required = true
24
+ presenter.configuration_url = "some config url" #doesn't even have to be valid here!
25
+ presenter.message = EY::ServicesAPI::Message.new(:message_type => "status", :subject => "some messages")
26
+ end
27
+
28
+ service_account_response = response_hash[:service_account]
29
+ service_account_response[:configuration_required].should be true
30
+ service_account_response[:configuration_url].should eq "some config url"
31
+ service_account_response[:provisioned_services_url].should eq "some provision url"
32
+ service_account_response[:url].should eq "some resource url"
33
+ response_hash[:message].should eq({:message_type => 'status', :subject => "some messages", :body => nil})
34
+ end
35
+ end
36
+
37
+ end
@@ -0,0 +1,98 @@
1
+ require 'spec_helper'
2
+ require 'sinatra'
3
+
4
+ describe EY::ServicesAPI::Service do
5
+ before do
6
+ @valid_params = @tresfiestas.service_registration_params
7
+ @service = EY::ServicesAPI::Service.new(@valid_params)
8
+ end
9
+
10
+ it "can be initialized with a hash" do
11
+ @service.should be_a EY::ServicesAPI::Service
12
+ @service.name.should eq @valid_params[:name]
13
+ @service.description.should eq @valid_params[:description]
14
+ @service.service_accounts_url.should eq @valid_params[:service_accounts_url]
15
+ @service.home_url.should eq @valid_params[:home_url]
16
+ @service.terms_and_conditions_url.should eq @valid_params[:terms_and_conditions_url]
17
+ @service.vars.should eq @valid_params[:vars]
18
+ end
19
+
20
+ describe "#register_service" do
21
+
22
+ describe "with a registration_url" do
23
+ before do
24
+ partner = @tresfiestas.partner
25
+
26
+ @registration_url = partner[:registration_url]
27
+
28
+ @registration_params = @tresfiestas.service_registration_params
29
+
30
+ @connection = EY::ServicesAPI.connection
31
+ end
32
+
33
+ it "can register a service" do
34
+ service = @connection.register_service(@registration_url, @registration_params)
35
+ service.should be_a EY::ServicesAPI::Service
36
+ service.url.should_not be_nil
37
+ end
38
+
39
+ it "can list services" do
40
+ services = @connection.list_services(@registration_url)
41
+ services.should eq []
42
+ end
43
+
44
+ it "can handle errors on registration" do
45
+ lambda{
46
+ @connection.register_service(@registration_url, @registration_params.merge(:name => nil))
47
+ }.should raise_error(EY::ServicesAPI::Connection::ValidationError, /Name can't be blank/)
48
+ end
49
+
50
+ describe "with a registered service" do
51
+ before do
52
+ @service = @connection.register_service(@registration_url, @registration_params)
53
+ end
54
+
55
+ it "can list services" do
56
+ services = @connection.list_services(@registration_url)
57
+ services.should eq [@service]
58
+ end
59
+
60
+ it "can fetch your service" do
61
+ fetched_service = @connection.get_service(@service.url)
62
+ fetched_service.should eq @service
63
+ end
64
+
65
+ it "can update your service" do
66
+ new_name = "New and Improved: #{@service.name}"
67
+ @service.update(:name => new_name)
68
+ @service.name.should eq new_name
69
+ fetched_service = @connection.get_service(@service.url)
70
+ fetched_service.name.should eq new_name
71
+ end
72
+
73
+ it "can handle errors when updating your service" do
74
+ old_name = @service.name
75
+ lambda {
76
+ @service.update(:name => nil)
77
+ }.should raise_error(EY::ServicesAPI::Connection::ValidationError, /Name can't be blank/)
78
+ @service.name.should eq old_name
79
+ fetched_service = @connection.get_service(@service.url)
80
+ fetched_service.name.should eq old_name
81
+ end
82
+
83
+ it "can delete your service" do
84
+ @service.destroy
85
+ lambda {
86
+ @connection.get_service(@service.url)
87
+ }.should raise_error EY::ServicesAPI::Connection::NotFound
88
+ end
89
+
90
+ describe "with an AWSM account id" do
91
+ before do
92
+
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,14 @@
1
+ require 'ey_services_api'
2
+ require 'rspec'
3
+
4
+ RSpec.configure do |config|
5
+ config.before(:each) do
6
+ if ENV["BUNDLE_GEMFILE"] == "InternalGemfile"
7
+ require 'tresfiestas/gem_integration_test'
8
+ EY::ServicesAPI.enable_mock!(Tresfiestas::GemIntegrationTest)
9
+ else
10
+ EY::ServicesAPI.enable_mock!
11
+ end
12
+ @tresfiestas = EY::ServicesAPI.mock_backend
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ey_services_api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jacob Burkhart & Thorben Schröder & David Calavera & Michael Brodhead & Others
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-09-20 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &2170284860 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *2170284860
25
+ - !ruby/object:Gem::Dependency
26
+ name: json
27
+ requirement: &2170284400 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *2170284400
36
+ - !ruby/object:Gem::Dependency
37
+ name: ey_api_hmac
38
+ requirement: &2170283960 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *2170283960
47
+ description: API for Partner Services (talks to services.engineyard.com)
48
+ email:
49
+ - jacob@engineyard.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - .rspec
56
+ - Gemfile
57
+ - InternalGemfile
58
+ - README.md
59
+ - Rakefile
60
+ - ey_services_api.gemspec
61
+ - lib/ey_services_api.rb
62
+ - lib/ey_services_api/api_struct.rb
63
+ - lib/ey_services_api/connection.rb
64
+ - lib/ey_services_api/invoice.rb
65
+ - lib/ey_services_api/message.rb
66
+ - lib/ey_services_api/provisioned_service_creation.rb
67
+ - lib/ey_services_api/provisioned_service_response.rb
68
+ - lib/ey_services_api/service.rb
69
+ - lib/ey_services_api/service_account_creation.rb
70
+ - lib/ey_services_api/service_account_response.rb
71
+ - lib/ey_services_api/test/tresfiestas_fake.rb
72
+ - lib/ey_services_api/version.rb
73
+ - spec/invoice_spec.rb
74
+ - spec/message_spec.rb
75
+ - spec/provisioned_service_creation_spec.rb
76
+ - spec/service_account_creation_spec.rb
77
+ - spec/service_spec.rb
78
+ - spec/spec_helper.rb
79
+ homepage: ''
80
+ licenses: []
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ! '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubyforge_project: ey_services_api
99
+ rubygems_version: 1.8.10
100
+ signing_key:
101
+ specification_version: 3
102
+ summary: API for Partner Services (talks to services.engineyard.com)
103
+ test_files:
104
+ - spec/invoice_spec.rb
105
+ - spec/message_spec.rb
106
+ - spec/provisioned_service_creation_spec.rb
107
+ - spec/service_account_creation_spec.rb
108
+ - spec/service_spec.rb
109
+ - spec/spec_helper.rb