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 +8 -0
- data/.rspec +1 -0
- data/Gemfile +10 -0
- data/InternalGemfile +19 -0
- data/README.md +35 -0
- data/Rakefile +11 -0
- data/ey_services_api.gemspec +24 -0
- data/lib/ey_services_api.rb +40 -0
- data/lib/ey_services_api/api_struct.rb +18 -0
- data/lib/ey_services_api/connection.rb +60 -0
- data/lib/ey_services_api/invoice.rb +6 -0
- data/lib/ey_services_api/message.rb +6 -0
- data/lib/ey_services_api/provisioned_service_creation.rb +35 -0
- data/lib/ey_services_api/provisioned_service_response.rb +17 -0
- data/lib/ey_services_api/service.rb +19 -0
- data/lib/ey_services_api/service_account_creation.rb +18 -0
- data/lib/ey_services_api/service_account_response.rb +17 -0
- data/lib/ey_services_api/test/tresfiestas_fake.rb +253 -0
- data/lib/ey_services_api/version.rb +5 -0
- data/spec/invoice_spec.rb +48 -0
- data/spec/message_spec.rb +70 -0
- data/spec/provisioned_service_creation_spec.rb +41 -0
- data/spec/service_account_creation_spec.rb +37 -0
- data/spec/service_spec.rb +98 -0
- data/spec/spec_helper.rb +14 -0
- metadata +109 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--format documentation --colour
|
data/Gemfile
ADDED
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,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,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,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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|