king_soa 0.0.4 → 0.0.5
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/LICENSE +1 -1
- data/README.rdoc +14 -3
- data/lib/king_soa.rb +4 -1
- data/lib/king_soa/rack/middleware.rb +11 -4
- data/lib/king_soa/service.rb +44 -27
- data/spec/king_soa/service_spec.rb +17 -9
- data/spec/server/app.rb +9 -2
- data/spec/spec_helper.rb +5 -1
- metadata +19 -4
data/LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -66,7 +66,7 @@ to provide some minimal access restriction. To make it secure you should either
|
|
66
66
|
use https in public or hide the endpoints somwhere on your farm.
|
67
67
|
|
68
68
|
Service endpoints receiving such calls need to use the provided rack middleware.
|
69
|
-
|
69
|
+
As the middleware is doing the authentication, executing the service class and
|
70
70
|
returns values or errors.
|
71
71
|
|
72
72
|
=== Local Services
|
@@ -80,6 +80,12 @@ The service is put onto a resque queue and somewhere in your cloud you should
|
|
80
80
|
have a worker looking for it. The service class should also have the resque
|
81
81
|
@queue attribute set so the job can be resceduled if it fails.
|
82
82
|
|
83
|
+
=== Gotchas
|
84
|
+
|
85
|
+
* make sure to define the service on the sender side with the appropriate url
|
86
|
+
* define the local service(called from remote) with the right auth key
|
87
|
+
* double check your auth keys(a string is not an int), to be save use "strings" esp. when loading from yml
|
88
|
+
|
83
89
|
== Integration
|
84
90
|
|
85
91
|
Just think of a buch of small sinatra or specialized rails apps, each having
|
@@ -119,13 +125,18 @@ The base is just:
|
|
119
125
|
use KingSoa::Rack::Middleware
|
120
126
|
|
121
127
|
The service definition should of course also be done, see rails example.
|
122
|
-
|
128
|
+
|
129
|
+
== ToDo
|
130
|
+
|
131
|
+
* better error logging
|
132
|
+
* a central server & clients getting definitions from it
|
133
|
+
|
123
134
|
== Note on Patches/Pull Requests
|
124
135
|
|
125
136
|
* Fork the project.
|
126
137
|
* Make your feature addition or bug fix.
|
127
138
|
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
128
|
-
* Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
139
|
+
* Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a branch or commit by itself so I can ignore when I pull)
|
129
140
|
* Send me a pull request. Bonus points for topic branches.
|
130
141
|
|
131
142
|
== Copyright
|
data/lib/king_soa.rb
CHANGED
@@ -3,6 +3,9 @@ require 'resque'
|
|
3
3
|
require 'json'
|
4
4
|
require 'typhoeus'
|
5
5
|
require 'active_support/inflector'
|
6
|
+
# Rails 3.0
|
7
|
+
#require 'active_support'
|
8
|
+
#require 'active_support/core_ext/string'
|
6
9
|
|
7
10
|
require 'king_soa/registry'
|
8
11
|
require 'king_soa/service'
|
@@ -27,7 +30,7 @@ module KingSoa
|
|
27
30
|
if service = Registry[meth]
|
28
31
|
service.perform(*args)
|
29
32
|
else
|
30
|
-
super
|
33
|
+
super(meth, *args, &blk)
|
31
34
|
end
|
32
35
|
end
|
33
36
|
end
|
@@ -1,14 +1,22 @@
|
|
1
1
|
module KingSoa::Rack
|
2
2
|
class Middleware
|
3
3
|
|
4
|
-
|
4
|
+
# === Params
|
5
|
+
# app:: Application to call next
|
6
|
+
# config<Hash{Symbol=>String}>::
|
7
|
+
# === config hash
|
8
|
+
# :endpoint_path<RegEx>:: Path which is getting all incoming soa requests.
|
9
|
+
# Defaults to /^\/soa/ => /soa
|
10
|
+
# Make sure your service url's have it set too.
|
11
|
+
def initialize(app, config={})
|
5
12
|
@app = app
|
13
|
+
@config = config
|
14
|
+
@config[:endpoint_path] ||= /^\/soa/
|
6
15
|
end
|
7
16
|
|
8
17
|
# Takes incoming soa requests and calls the passed in method with given params
|
9
18
|
def call(env)
|
10
|
-
|
11
|
-
if env["PATH_INFO"] =~ /^\/soa/
|
19
|
+
if env["PATH_INFO"] =~ @config[:endpoint_path]
|
12
20
|
begin
|
13
21
|
req = Rack::Request.new(env)
|
14
22
|
# find service
|
@@ -29,7 +37,6 @@ module KingSoa::Rack
|
|
29
37
|
]
|
30
38
|
|
31
39
|
rescue Exception => e
|
32
|
-
#Hoth::Logger.debug "e: #{e.message}"
|
33
40
|
if service
|
34
41
|
encoded_error = service.encode({"error" => e})
|
35
42
|
[500, {'Content-Type' => 'application/json', 'Content-Length' => "#{encoded_error.length}"}, [encoded_error]]
|
data/lib/king_soa/service.rb
CHANGED
@@ -1,13 +1,23 @@
|
|
1
1
|
module KingSoa
|
2
2
|
class Service
|
3
|
-
|
4
|
-
|
3
|
+
|
4
|
+
# name<String/Symbol>:: name of the service class to call
|
5
|
+
# auth<String/Int>:: password for the remote service. Used by rack middleware
|
6
|
+
# to authentify the callee
|
7
|
+
# url<String>:: Url where the service is located. If the rack middleware is
|
8
|
+
# used on the receiving side, make sure to append /soa to the url so the
|
9
|
+
# middleware can grab the incoming call. A custom endpoint path can be set in
|
10
|
+
# the middleware.
|
11
|
+
# queue<Boolean>:: turn on queueing for this service call. The incoming
|
12
|
+
# request(className+parameter) will be put onto a resque queue
|
13
|
+
# debug<Boolean>:: turn on verbose outpur for typhoeus request
|
14
|
+
attr_accessor :debug, :name, :auth, :queue, :url
|
5
15
|
|
6
16
|
def initialize(opts)
|
7
17
|
self.name = opts[:name].to_sym
|
8
|
-
|
9
|
-
|
10
|
-
|
18
|
+
[:url, :queue, :auth, :debug ].each do |opt|
|
19
|
+
self.send("#{opt}=", opts[opt]) if opts[opt]
|
20
|
+
end
|
11
21
|
end
|
12
22
|
|
13
23
|
# Call a service living somewhere in the soa universe. This is done by
|
@@ -18,9 +28,19 @@ module KingSoa
|
|
18
28
|
resp_code = request.perform
|
19
29
|
case resp_code
|
20
30
|
when 200
|
21
|
-
|
31
|
+
if request.response_header.include?('Content-Type: application/json')
|
32
|
+
#decode incoming json .. most likely from KingSoa's rack middleware
|
33
|
+
return self.decode(request.response_body)["result"]
|
34
|
+
else # return plain body
|
35
|
+
return request.response_body
|
36
|
+
end
|
22
37
|
else
|
23
|
-
|
38
|
+
if request.response_header.include?('Content-Type: application/json')
|
39
|
+
#decode incoming json .. most likely from KingSoa's rack middleware
|
40
|
+
return self.decode(request.response_body)["error"]
|
41
|
+
else # return plain body
|
42
|
+
return request.response_body
|
43
|
+
end
|
24
44
|
end
|
25
45
|
end
|
26
46
|
|
@@ -36,13 +56,17 @@ module KingSoa
|
|
36
56
|
# * local by calling perform method on a class
|
37
57
|
# * put a job onto a queue
|
38
58
|
# === Parameter
|
39
|
-
# args:: whatever arguments the service methods
|
40
|
-
#
|
59
|
+
# args:: whatever arguments the service methods receives. A local service/method
|
60
|
+
# gets thems as splatted params. For a remote service they are converted to
|
61
|
+
# json
|
62
|
+
# === Returns
|
63
|
+
# <nil> for queued services dont answer
|
64
|
+
# <mixed> Whatever the method/service
|
41
65
|
def perform(*args)
|
42
66
|
if queue
|
43
67
|
add_to_queue(*args)
|
44
68
|
return nil
|
45
|
-
else
|
69
|
+
else # call the local class if present, else got remote
|
46
70
|
result = local_class ? local_class.send(:perform, *args) : call_remote(*args)
|
47
71
|
return result
|
48
72
|
end
|
@@ -57,8 +81,9 @@ module KingSoa
|
|
57
81
|
end
|
58
82
|
end
|
59
83
|
|
60
|
-
# Return the
|
61
|
-
#
|
84
|
+
# Return the class name infered from the camelized service name.
|
85
|
+
# === Example
|
86
|
+
# save_attachment => class SaveAttachment
|
62
87
|
def local_class_name
|
63
88
|
self.name.to_s.camelize
|
64
89
|
end
|
@@ -66,7 +91,7 @@ module KingSoa
|
|
66
91
|
# Set options for the typhoeus curl request
|
67
92
|
# === Parameter
|
68
93
|
# req<Typhoeus::Easy>:: request object
|
69
|
-
# args<Array[]>::
|
94
|
+
# args<Array[]>:: arguments for the soa method, added to post body json encoded
|
70
95
|
def set_request_opts(req, args)
|
71
96
|
req.url = url
|
72
97
|
req.method = :post
|
@@ -77,20 +102,12 @@ module KingSoa
|
|
77
102
|
req.verbose = 1 if debug
|
78
103
|
end
|
79
104
|
|
80
|
-
#
|
81
|
-
#
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
@url = "#{url}/soa"
|
87
|
-
end
|
88
|
-
|
89
|
-
# The params for each soa request consist of following values:
|
90
|
-
# name => the name of the method to call
|
91
|
-
# args => the arguments for the soa class method
|
92
|
-
# auth => an authentication key. something like a api key or pass. To make
|
93
|
-
# it really secure you MUST use https or do not expose your soa endpoints
|
105
|
+
# Params for a soa request consist of following values:
|
106
|
+
# name => name of the soa class to call
|
107
|
+
# args => arguments for the soa class method -> Class.perform(args)
|
108
|
+
# auth => an authentication key. something like a api key or pass. To make
|
109
|
+
# it really secure you MUST use https or hide your soa endpoints from public
|
110
|
+
# web acces
|
94
111
|
#
|
95
112
|
# ==== Parameter
|
96
113
|
# payload<Hash|Array|String>:: will be json encoded
|
@@ -1,11 +1,14 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
2
|
|
3
|
-
describe KingSoa::Service do
|
3
|
+
describe KingSoa::Service, 'in general' do
|
4
|
+
|
4
5
|
before(:each) do
|
5
6
|
end
|
6
7
|
|
7
|
-
it "should init" do
|
8
|
-
|
8
|
+
it "should not init without name" do
|
9
|
+
lambda {
|
10
|
+
s = KingSoa::Service.new()
|
11
|
+
}.should raise_error
|
9
12
|
end
|
10
13
|
end
|
11
14
|
|
@@ -33,18 +36,23 @@ describe KingSoa::Service, 'remote request' do
|
|
33
36
|
stop_test_server
|
34
37
|
end
|
35
38
|
|
36
|
-
it "should call a service
|
37
|
-
s = KingSoa::Service.new(:name=>:soa_test_service, :url=>
|
39
|
+
it "should call a remote service" do
|
40
|
+
s = KingSoa::Service.new(:name=>:soa_test_service, :url=>test_soa_url, :auth=>'12345')
|
38
41
|
s.perform(1,2,3).should == [1,2,3]
|
39
42
|
end
|
40
43
|
|
41
|
-
it "should call a service
|
42
|
-
s = KingSoa::Service.new(:name=>:soa_test_service, :url=>
|
44
|
+
it "should call a remote service and return auth error" do
|
45
|
+
s = KingSoa::Service.new(:name=>:soa_test_service, :url=>test_soa_url, :auth=>'wrong')
|
43
46
|
s.perform(1,2,3).should == "Please provide a valid authentication key"
|
44
47
|
end
|
45
48
|
|
46
|
-
it "should call a service remote and return
|
47
|
-
s = KingSoa::Service.new(:name=>:wrong_service, :url=>
|
49
|
+
it "should call a service remote and return not found error" do
|
50
|
+
s = KingSoa::Service.new(:name=>:wrong_service, :url=>test_soa_url, :auth=>'12345')
|
48
51
|
s.perform(1,2,3).should include("The service: wrong_service could not be found")
|
49
52
|
end
|
53
|
+
|
54
|
+
it "should call a remote service without using middleware and returning plain text" do
|
55
|
+
s = KingSoa::Service.new(:name=>:non_soa_test_service, :url=> "#{test_url}/non_json_response")
|
56
|
+
s.perform().should == "<h1>hello World</h1>"
|
57
|
+
end
|
50
58
|
end
|
data/spec/server/app.rb
CHANGED
@@ -14,8 +14,14 @@ get '/die' do
|
|
14
14
|
exit!
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
#
|
17
|
+
# A non king_soa method => rack middleware is not used
|
18
|
+
# Still such methods can be called and their result is returned as plain text
|
19
|
+
post '/non_json_response' do
|
20
|
+
"<h1>hello World</h1>"
|
21
|
+
end
|
22
|
+
|
23
|
+
################################################################################
|
24
|
+
# Somewhere in you app you define a local service, receiving the incoming call
|
19
25
|
#
|
20
26
|
# setup test registry
|
21
27
|
service = KingSoa::Service.new(:name=>'soa_test_service', :auth => '12345')
|
@@ -23,6 +29,7 @@ KingSoa::Registry << service
|
|
23
29
|
|
24
30
|
# the local soa class beeing called
|
25
31
|
class SoaTestService
|
32
|
+
#simply return all given parameters
|
26
33
|
def self.perform(param1, param2, param3)
|
27
34
|
return [param1, param2, param3]
|
28
35
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -9,6 +9,10 @@ require 'rack/test'
|
|
9
9
|
# for starting the test sinatra server
|
10
10
|
require 'open3'
|
11
11
|
|
12
|
+
def test_soa_url
|
13
|
+
"#{test_url}/soa"
|
14
|
+
end
|
15
|
+
|
12
16
|
def test_url
|
13
17
|
'http://localhost:4567'
|
14
18
|
end
|
@@ -16,7 +20,7 @@ end
|
|
16
20
|
# Start a local test sinatra app receiving the real requests .. no mocking here
|
17
21
|
# the server could also be started manually with:
|
18
22
|
# ruby spec/server/app
|
19
|
-
def start_test_server(wait_time=
|
23
|
+
def start_test_server(wait_time=3)
|
20
24
|
# start sinatra test server
|
21
25
|
Dir.chdir(File.dirname(__FILE__) + '/server/') do
|
22
26
|
@in, @rackup, @err = Open3.popen3("ruby app.rb")
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: king_soa
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 21
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
8
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
9
|
+
- 5
|
10
|
+
version: 0.0.5
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Georg Leciejewski
|
@@ -14,16 +15,18 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2010-
|
18
|
+
date: 2010-06-29 00:00:00 +02:00
|
18
19
|
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
21
22
|
name: typhoeus
|
22
23
|
prerelease: false
|
23
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
24
26
|
requirements:
|
25
27
|
- - ">="
|
26
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
27
30
|
segments:
|
28
31
|
- 0
|
29
32
|
version: "0"
|
@@ -33,9 +36,11 @@ dependencies:
|
|
33
36
|
name: json
|
34
37
|
prerelease: false
|
35
38
|
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
36
40
|
requirements:
|
37
41
|
- - ">="
|
38
42
|
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
39
44
|
segments:
|
40
45
|
- 0
|
41
46
|
version: "0"
|
@@ -45,9 +50,11 @@ dependencies:
|
|
45
50
|
name: resque
|
46
51
|
prerelease: false
|
47
52
|
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
48
54
|
requirements:
|
49
55
|
- - ">="
|
50
56
|
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
51
58
|
segments:
|
52
59
|
- 0
|
53
60
|
version: "0"
|
@@ -57,9 +64,11 @@ dependencies:
|
|
57
64
|
name: rspec
|
58
65
|
prerelease: false
|
59
66
|
requirement: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
60
68
|
requirements:
|
61
69
|
- - ">="
|
62
70
|
- !ruby/object:Gem::Version
|
71
|
+
hash: 3
|
63
72
|
segments:
|
64
73
|
- 0
|
65
74
|
version: "0"
|
@@ -69,9 +78,11 @@ dependencies:
|
|
69
78
|
name: rack
|
70
79
|
prerelease: false
|
71
80
|
requirement: &id005 !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
72
82
|
requirements:
|
73
83
|
- - ">="
|
74
84
|
- !ruby/object:Gem::Version
|
85
|
+
hash: 3
|
75
86
|
segments:
|
76
87
|
- 0
|
77
88
|
version: "0"
|
@@ -111,23 +122,27 @@ rdoc_options:
|
|
111
122
|
require_paths:
|
112
123
|
- lib
|
113
124
|
required_ruby_version: !ruby/object:Gem::Requirement
|
125
|
+
none: false
|
114
126
|
requirements:
|
115
127
|
- - ">="
|
116
128
|
- !ruby/object:Gem::Version
|
129
|
+
hash: 3
|
117
130
|
segments:
|
118
131
|
- 0
|
119
132
|
version: "0"
|
120
133
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
134
|
+
none: false
|
121
135
|
requirements:
|
122
136
|
- - ">="
|
123
137
|
- !ruby/object:Gem::Version
|
138
|
+
hash: 3
|
124
139
|
segments:
|
125
140
|
- 0
|
126
141
|
version: "0"
|
127
142
|
requirements: []
|
128
143
|
|
129
144
|
rubyforge_project:
|
130
|
-
rubygems_version: 1.3.
|
145
|
+
rubygems_version: 1.3.7
|
131
146
|
signing_key:
|
132
147
|
specification_version: 3
|
133
148
|
summary: Abstraction layer for SOA-Services
|