king_soa 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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["auth_key"])
18
- # perform method with decoded params
19
- result = service.perform(*service.decode( req.params["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
- [ 200,
24
- {'Content-Type' => 'application/json',
25
- 'Content-Length' => "#{encoded_result.length}"},
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! (#{e.message})"}.to_json
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.auth_key == key
48
+ raise "Please provide a valid authentication key" unless service.auth == key
44
49
  end
45
50
 
46
51
  end
@@ -1,16 +1,18 @@
1
1
  module KingSoa
2
2
  class Service
3
3
  # endpoint url
4
- attr_accessor :debug, :name, :auth_key, :queue
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.auth_key = opts[:auth_key] if opts[:auth_key]
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
- def perform(*args)
26
- result = local_class ? local_class.send(:perform, *args) : call_remote(*args)
27
- return result
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
- "#{self.name.to_s.camelize}".constantize
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
- # The params for each soa request consisnt of two values:
63
- # name => the name of the method to call
64
- # params => the parameters for the method
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
- # params<Hash|Array|String>:: will be json encoded
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' => name.to_s,
71
- 'params' => encode(payload),
72
- 'auth_key'=> auth_key }
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
- # <tt>Hoth::Services.service_name(params)</tt>
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
- # ==== Params
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.execute(*args)
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 be able to handle exceptions" do
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 "says hello" do
22
+ xit "should handle result" do
23
23
  app = stub("ApplicationStub").as_null_object
24
- middleware = Hoth::Providers::RackProvider.new(app)
25
-
26
- get '/soa', :name=>'a_method', :params=> "#{{:number=>1}.to_json}"
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 = KingSoa::Registry.new
10
- reg.services.should == []
9
+ @reg.services.should == []
11
10
  end
12
11
 
13
- it "should add service" do
14
- reg = KingSoa::Registry.new
15
- s = KingSoa::Service.new(:url=>'http://localhost')
16
- reg << s
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
- res = s.perform(1,2,3)
17
- res.should == [1,2,3]
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=>'http://localhost:4567', :auth_key=>'12345')
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=>'http://localhost:4567', :auth_key=>'wrong')
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=>'http://localhost:4567', :auth_key=>'12345')
37
- s.perform(1,2,3).should == "An error occurred! (undefined method `auth_key' for nil:NilClass)"
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
- require "#{File.dirname(__FILE__)}/../../lib/king_soa"
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', :auth_key=>'12345')
21
+ service = KingSoa::Service.new(:name=>'soa_test_service', :auth => '12345')
17
22
  KingSoa::Registry << service
18
23
 
19
- # the class beeing called localy
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
- include WebMock
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 local_service
16
- # Rack::Builder.app do
17
- # use KingSoa::Rack::Middleware
18
- # run super
19
- # end
27
+ def stop_test_server
28
+ Typhoeus::Request.get( "#{test_url}/die")
29
+ end
20
30
 
21
- # Rack::Builder.new do
22
- # use KingSoa::Rack::Middleware
23
- # app = proc do |env|
24
- # [ 200, {'Content-Type' => 'text/plain'}, "b" ]
25
- # end
26
- # run app
27
- # end.to_app
28
- # def app
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
- - 1
9
- version: 0.0.1
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-08 00:00:00 +02:00
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. Furthermore you want to know where to deploy those services.
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: Registry and deployment description abstraction for SOA-Services by SalesKing
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