king_soa 0.0.1 → 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/lib/king_soa/rack/middleware.rb +14 -9
- data/lib/king_soa/service.rb +39 -14
- data/lib/king_soa.rb +6 -5
- data/spec/king_soa/rack/middleware_spec.rb +5 -13
- data/spec/king_soa/registry_spec.rb +8 -11
- data/spec/king_soa/service_spec.rb +21 -15
- data/spec/server/app.rb +12 -9
- data/spec/spec_helper.rb +26 -24
- metadata +5 -5
@@ -13,24 +13,28 @@ module KingSoa::Rack
|
|
13
13
|
req = Rack::Request.new(env)
|
14
14
|
# find service
|
15
15
|
service = KingSoa.find(req.params["name"])
|
16
|
+
# TODO rescue service class not found
|
17
|
+
raise "The service: #{req.params["name"]} could not be found" unless service
|
16
18
|
# authenticate
|
17
|
-
authenticated?(service, req.params["
|
18
|
-
# perform method with decoded
|
19
|
-
result = service.perform(*service.decode( req.params["
|
19
|
+
authenticated?(service, req.params["auth"])
|
20
|
+
# perform method with decoded params
|
21
|
+
result = service.perform(*service.decode( req.params["args"] ))
|
20
22
|
# encode result
|
21
23
|
encoded_result = service.encode({"result" => result})
|
22
24
|
# and return
|
23
|
-
[
|
24
|
-
|
25
|
-
|
26
|
-
[encoded_result]
|
25
|
+
[
|
26
|
+
200,
|
27
|
+
{'Content-Type' => 'application/json', 'Content-Length' => "#{encoded_result.length}"},
|
28
|
+
[encoded_result]
|
29
|
+
]
|
30
|
+
|
27
31
|
rescue Exception => e
|
28
32
|
#Hoth::Logger.debug "e: #{e.message}"
|
29
33
|
if service
|
30
34
|
encoded_error = service.encode({"error" => e})
|
31
35
|
[500, {'Content-Type' => 'application/json', 'Content-Length' => "#{encoded_error.length}"}, [encoded_error]]
|
32
36
|
else
|
33
|
-
encoded_error = {"error" => "An error occurred
|
37
|
+
encoded_error = {"error" => "An error occurred => #{e.message}"}.to_json
|
34
38
|
[500, {'Content-Type' => "application/json", 'Content-Length' => "#{encoded_error.length}"}, [encoded_error]]
|
35
39
|
end
|
36
40
|
end
|
@@ -39,8 +43,9 @@ module KingSoa::Rack
|
|
39
43
|
end
|
40
44
|
end
|
41
45
|
|
46
|
+
# TODO raise and rescue specific error
|
42
47
|
def authenticated?(service, key)
|
43
|
-
raise "Please provide a valid authentication key" unless service.
|
48
|
+
raise "Please provide a valid authentication key" unless service.auth == key
|
44
49
|
end
|
45
50
|
|
46
51
|
end
|
data/lib/king_soa/service.rb
CHANGED
@@ -1,16 +1,18 @@
|
|
1
1
|
module KingSoa
|
2
2
|
class Service
|
3
3
|
# endpoint url
|
4
|
-
attr_accessor :debug, :name, :
|
4
|
+
attr_accessor :debug, :name, :auth, :queue
|
5
5
|
attr_reader :request
|
6
6
|
|
7
7
|
def initialize(opts)
|
8
8
|
self.name = opts[:name].to_sym
|
9
9
|
self.url = opts[:url] if opts[:url]
|
10
10
|
self.queue = opts[:queue] if opts[:queue]
|
11
|
-
self.
|
11
|
+
self.auth = opts[:auth] if opts[:auth]
|
12
12
|
end
|
13
13
|
|
14
|
+
# Call a service living somewhere in the soa universe. This is done by
|
15
|
+
# making a POST request to the url
|
14
16
|
def call_remote(*args)
|
15
17
|
set_request_opts(args)
|
16
18
|
resp_code = @request.perform
|
@@ -22,20 +24,40 @@ module KingSoa
|
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
-
|
27
|
+
# A queued method MUST have an associated resque worker running and the soa
|
28
|
+
# class MUST have the @queue attribute for redis set
|
29
|
+
def add_to_queue(*args)
|
30
|
+
# use low level resque method since class might not be local available for Resque.enqueue
|
31
|
+
Resque::Job.create(queue, local_class_name, *args)
|
28
32
|
end
|
29
|
-
|
33
|
+
|
34
|
+
# Call a method: remote over http, local or put a job onto a queue
|
35
|
+
# === Parameter
|
36
|
+
# args:: whatever arguments the service methods recieves. Those are later json
|
37
|
+
# encoded for remote or queued methods
|
38
|
+
def perform(*args)
|
39
|
+
if queue
|
40
|
+
add_to_queue(*args)
|
41
|
+
return nil
|
42
|
+
else
|
43
|
+
result = local_class ? local_class.send(:perform, *args) : call_remote(*args)
|
44
|
+
return result
|
45
|
+
end
|
46
|
+
end
|
30
47
|
|
31
48
|
def local_class
|
32
49
|
@local_class ||= begin
|
33
|
-
|
50
|
+
local_class_name.constantize
|
34
51
|
rescue NameError => e # no local implementation
|
35
52
|
false
|
36
53
|
end
|
37
54
|
end
|
38
55
|
|
56
|
+
# Return the classname infered from service name
|
57
|
+
def local_class_name
|
58
|
+
self.name.to_s.camelize
|
59
|
+
end
|
60
|
+
|
39
61
|
def request
|
40
62
|
@request ||= Typhoeus::Easy.new
|
41
63
|
end
|
@@ -59,17 +81,20 @@ module KingSoa
|
|
59
81
|
@url = "#{url}/soa"
|
60
82
|
end
|
61
83
|
|
62
|
-
|
63
|
-
#
|
64
|
-
#
|
84
|
+
# The params for each soa request consist of following values:
|
85
|
+
# name => the name of the method to call
|
86
|
+
# args => the arguments for the soa class method
|
87
|
+
# auth => an authentication key. something like a api key or pass. To make
|
88
|
+
# it really secure you MUST use https or do not expose your soa endpoints
|
89
|
+
#
|
65
90
|
# ==== Parameter
|
66
|
-
#
|
91
|
+
# payload<Hash|Array|String>:: will be json encoded
|
67
92
|
# === Returns
|
68
93
|
# <Hash{String=>String}>:: params added to the POST body
|
69
94
|
def params(payload)
|
70
|
-
{ 'name'
|
71
|
-
'
|
72
|
-
'
|
95
|
+
{ 'name' => name.to_s,
|
96
|
+
'args' => encode(payload),
|
97
|
+
'auth' => auth }
|
73
98
|
end
|
74
99
|
|
75
100
|
def encode(string)
|
data/lib/king_soa.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'singleton'
|
2
|
+
require 'resque'
|
2
3
|
require 'json'
|
3
4
|
require 'typhoeus'
|
4
5
|
require 'active_support/inflector'
|
@@ -21,7 +22,8 @@ require 'king_soa/rack/middleware'
|
|
21
22
|
#
|
22
23
|
#
|
23
24
|
# after defining your services you can call each of them with
|
24
|
-
#
|
25
|
+
#
|
26
|
+
# <tt>KingSoa.service_name(args)</tt>
|
25
27
|
#
|
26
28
|
# KingSoa.sign_document(counter)
|
27
29
|
# current_number = Hoth::Services.value_of_counter(counter)
|
@@ -32,12 +34,11 @@ module KingSoa
|
|
32
34
|
class << self
|
33
35
|
|
34
36
|
def init_from_hash(services)
|
35
|
-
# create service
|
36
|
-
|
37
|
+
# create service
|
37
38
|
end
|
38
39
|
|
39
40
|
# Locate service by a given name
|
40
|
-
# ====
|
41
|
+
# ==== Parameter
|
41
42
|
# service<String|Symbol>:: the name to lookup
|
42
43
|
def find(service)
|
43
44
|
Registry[service]
|
@@ -46,7 +47,7 @@ module KingSoa
|
|
46
47
|
# this is where the services get called
|
47
48
|
def method_missing(meth, *args, &blk) # :nodoc:
|
48
49
|
if service = Registry[meth]
|
49
|
-
service.
|
50
|
+
service.perform(*args)
|
50
51
|
else
|
51
52
|
super
|
52
53
|
end
|
@@ -8,7 +8,7 @@ describe KingSoa::Rack::Middleware do
|
|
8
8
|
KingSoa::Registry << @service
|
9
9
|
end
|
10
10
|
|
11
|
-
it "should
|
11
|
+
it "should handle exceptions" do
|
12
12
|
app = stub("ApplicationStub").as_null_object
|
13
13
|
middleware = KingSoa::Rack::Middleware.new(app)
|
14
14
|
env = {"PATH_INFO" => "/soa", "name" => 'a_method'}
|
@@ -19,18 +19,10 @@ describe KingSoa::Rack::Middleware do
|
|
19
19
|
rack_response.last.first.should == "{\"error\":\"An error occurred! (Missing rack.input)\"}"
|
20
20
|
end
|
21
21
|
|
22
|
-
xit "
|
22
|
+
xit "should handle result" do
|
23
23
|
app = stub("ApplicationStub").as_null_object
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
last_response.should == 'ads'#be_ok
|
28
|
-
last_response.body.should == 'Hello World'
|
24
|
+
middleware = KingSoa::Rack::Middleware.new(app)
|
25
|
+
env = {"PATH_INFO" => "/soa"}
|
26
|
+
# KingSoa.should_receive(:find).and_return(@service)
|
29
27
|
end
|
30
|
-
|
31
|
-
# def app
|
32
|
-
# dummy_app = lambda { |env| puts "in dummy"; [200, {}, ""] }
|
33
|
-
# KingSoa::Rack::Middleware.new(dummy_app)
|
34
|
-
# end
|
35
|
-
|
36
28
|
end
|
@@ -2,26 +2,23 @@ require File.dirname(__FILE__) + '/../spec_helper.rb'
|
|
2
2
|
|
3
3
|
describe KingSoa::Registry do
|
4
4
|
before(:each) do
|
5
|
-
|
5
|
+
@reg = KingSoa::Registry
|
6
6
|
end
|
7
7
|
|
8
8
|
it "should return empty services" do
|
9
|
-
reg
|
10
|
-
reg.services.should == []
|
9
|
+
@reg.services.should == []
|
11
10
|
end
|
12
11
|
|
13
|
-
it "should add service" do
|
14
|
-
|
15
|
-
|
16
|
-
reg
|
17
|
-
reg.services.should == [s]
|
12
|
+
it "should add service" do
|
13
|
+
s = KingSoa::Service.new(:name=>'process_documents')
|
14
|
+
@reg << s
|
15
|
+
@reg.services.should == [s]
|
18
16
|
end
|
19
17
|
|
20
18
|
it "should return a service by name" do
|
21
|
-
reg = KingSoa::Registry.new
|
22
19
|
s = KingSoa::Service.new(:name=>:save_document, :url=>'http://localhost')
|
23
|
-
reg << s
|
24
|
-
reg[:save_document].should == s
|
20
|
+
@reg << s
|
21
|
+
@reg[:save_document].should == s
|
25
22
|
end
|
26
23
|
|
27
24
|
|
@@ -13,34 +13,40 @@ describe KingSoa::Service, 'local request' do
|
|
13
13
|
|
14
14
|
it "should call service" do
|
15
15
|
s = KingSoa::Service.new(:name=>:local_soa_class)
|
16
|
-
|
17
|
-
|
16
|
+
s.perform(1,2,3).should == [1,2,3]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe KingSoa::Service, 'queued' do
|
21
|
+
|
22
|
+
it "should call queue" do
|
23
|
+
s = KingSoa::Service.new(:name=>:local_soa_class, :queue=>:test_queue)
|
24
|
+
s.perform(1,2,3).should be_nil
|
18
25
|
end
|
19
26
|
end
|
20
27
|
|
21
|
-
# needs the local testserver !!!
|
22
|
-
# ruby spec/server/app
|
23
28
|
describe KingSoa::Service, 'remote request' do
|
24
29
|
|
30
|
+
before :all do
|
31
|
+
start_test_server
|
32
|
+
end
|
33
|
+
|
34
|
+
after :all do
|
35
|
+
stop_test_server
|
36
|
+
end
|
37
|
+
|
25
38
|
it "should call a service remote" do
|
26
|
-
s = KingSoa::Service.new(:name=>:soa_test_service, :url=>
|
39
|
+
s = KingSoa::Service.new(:name=>:soa_test_service, :url=>test_url, :auth=>'12345')
|
27
40
|
s.perform(1,2,3).should == [1,2,3]
|
28
41
|
end
|
29
42
|
|
30
43
|
it "should call a service remote and return auth error" do
|
31
|
-
s = KingSoa::Service.new(:name=>:soa_test_service, :url=>
|
44
|
+
s = KingSoa::Service.new(:name=>:soa_test_service, :url=>test_url, :auth=>'wrong')
|
32
45
|
s.perform(1,2,3).should == "Please provide a valid authentication key"
|
33
46
|
end
|
34
47
|
|
35
48
|
it "should call a service remote and return auth error" do
|
36
|
-
s = KingSoa::Service.new(:name=>:wrong_service, :url=>
|
37
|
-
s.perform(1,2,3).should
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
|
42
|
-
class LocalSoaClass
|
43
|
-
def self.perform(param1, param2, param3)
|
44
|
-
return [param1, param2, param3]
|
49
|
+
s = KingSoa::Service.new(:name=>:wrong_service, :url=>test_url, :auth=>'12345')
|
50
|
+
s.perform(1,2,3).should include("The service: wrong_service could not be found")
|
45
51
|
end
|
46
52
|
end
|
data/spec/server/app.rb
CHANGED
@@ -1,26 +1,29 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require 'rubygems'
|
3
3
|
require 'sinatra'
|
4
|
-
|
4
|
+
# change since we want the files in here not from installed gem
|
5
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
6
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', '..', 'lib'))
|
7
|
+
require "king_soa"
|
5
8
|
|
9
|
+
# this grabs all /soa requests
|
6
10
|
use KingSoa::Rack::Middleware
|
7
11
|
|
12
|
+
# method to kill this server instance
|
13
|
+
get '/die' do
|
14
|
+
exit!
|
15
|
+
end
|
8
16
|
|
9
|
-
###################################################
|
10
|
-
# method to kill this server instance
|
11
|
-
#'/die'
|
12
17
|
#######################################
|
13
|
-
# Somewhere in you app
|
18
|
+
# Somewhere in you app define services
|
14
19
|
#
|
15
20
|
# setup test registry
|
16
|
-
service = KingSoa::Service.new(:name=>'soa_test_service', :
|
21
|
+
service = KingSoa::Service.new(:name=>'soa_test_service', :auth => '12345')
|
17
22
|
KingSoa::Registry << service
|
18
23
|
|
19
|
-
# the class beeing called
|
24
|
+
# the local soa class beeing called
|
20
25
|
class SoaTestService
|
21
|
-
|
22
26
|
def self.perform(param1, param2, param3)
|
23
27
|
return [param1, param2, param3]
|
24
28
|
end
|
25
|
-
|
26
29
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -5,33 +5,35 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
5
5
|
require 'king_soa'
|
6
6
|
require 'spec'
|
7
7
|
require 'spec/autorun'
|
8
|
-
# for mocking web requests
|
9
|
-
require 'webmock/rspec'
|
10
8
|
require 'rack/test'
|
11
|
-
|
9
|
+
# for starting the test sinatra server
|
10
|
+
require 'open3'
|
12
11
|
|
12
|
+
def test_url
|
13
|
+
'http://localhost:4567'
|
14
|
+
end
|
13
15
|
|
16
|
+
# Start a local test sinatra app receiving the real requests .. no mocking here
|
17
|
+
# the server could also be started manually with:
|
18
|
+
# ruby spec/server/app
|
19
|
+
def start_test_server(wait_time=1)
|
20
|
+
# start sinatra test server
|
21
|
+
Dir.chdir(File.dirname(__FILE__) + '/server/') do
|
22
|
+
@in, @rackup, @err = Open3.popen3("ruby app.rb")
|
23
|
+
end
|
24
|
+
sleep wait_time # ensure the server is up
|
25
|
+
end
|
14
26
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
# run super
|
19
|
-
# end
|
27
|
+
def stop_test_server
|
28
|
+
Typhoeus::Request.get( "#{test_url}/die")
|
29
|
+
end
|
20
30
|
|
21
|
-
|
22
|
-
#
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
# Rack::Builder.new {
|
30
|
-
# # URLs starting with /account (logged in users) go to Rails
|
31
|
-
# map "/soa" do
|
32
|
-
# run KingSoa::Rack::Middleware.new
|
33
|
-
# end
|
34
|
-
# }.to_app
|
35
|
-
# end
|
36
|
-
# app.run
|
31
|
+
################################################################################
|
32
|
+
# Local soa classes called in specs
|
33
|
+
################################################################################
|
34
|
+
|
35
|
+
class LocalSoaClass
|
36
|
+
def self.perform(param1, param2, param3)
|
37
|
+
return [param1, param2, param3]
|
38
|
+
end
|
37
39
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 2
|
9
|
+
version: 0.0.2
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Georg Leciejewski
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-05-
|
17
|
+
date: 2010-05-09 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -55,7 +55,7 @@ dependencies:
|
|
55
55
|
version_requirements: *id003
|
56
56
|
description: |
|
57
57
|
Creating a SOA requires a centralized location to define all services within the
|
58
|
-
SOA.
|
58
|
+
SOA. KingSoa takes care of keeping services in a service registry and knows how to call them.
|
59
59
|
|
60
60
|
email: gl@salesking.eu
|
61
61
|
executables: []
|
@@ -106,7 +106,7 @@ rubyforge_project:
|
|
106
106
|
rubygems_version: 1.3.6
|
107
107
|
signing_key:
|
108
108
|
specification_version: 3
|
109
|
-
summary:
|
109
|
+
summary: Abstraction layer for SOA-Services
|
110
110
|
test_files:
|
111
111
|
- spec/spec_helper.rb
|
112
112
|
- spec/king_soa/registry_spec.rb
|