ey_services_api 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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