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 +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
|