king_soa 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009 Dirk Breuer
1
+ Copyright (c) 2010 Georg Leciejewski
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
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
- The middleware is doing the authentication, executing the service class and
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
- def initialize(app)
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
- # Hoth::Logger.debug "env: #{env.inspect}"
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]]
@@ -1,13 +1,23 @@
1
1
  module KingSoa
2
2
  class Service
3
- # endpoint url
4
- attr_accessor :debug, :name, :auth, :queue
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
- self.url = opts[:url] if opts[:url]
9
- self.queue = opts[:queue] if opts[:queue]
10
- self.auth = opts[:auth] if opts[:auth]
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
- return self.decode(request.response_body)["result"]
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
- return self.decode(request.response_body)["error"]
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 recieves. Those are later json
40
- # encoded for remote or queued methods
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 classname infered from the camelized service name.
61
- # A service named: save_attachment => class SaveAttachment
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[]>:: the arguments for the soa method, will be json encoded and added to post body
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
- # Url receiving the request
81
- # TODO. if not present try to grab from endpoint
82
- def url
83
- @url
84
- end
85
- def url=(url)
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 remote" do
37
- s = KingSoa::Service.new(:name=>:soa_test_service, :url=>test_url, :auth=>'12345')
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 remote and return auth error" do
42
- s = KingSoa::Service.new(:name=>:soa_test_service, :url=>test_url, :auth=>'wrong')
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 auth error" do
47
- s = KingSoa::Service.new(:name=>:wrong_service, :url=>test_url, :auth=>'12345')
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
- # Somewhere in you app define services
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=1)
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
- - 4
9
- version: 0.0.4
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-05-09 00:00:00 +02:00
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.6
145
+ rubygems_version: 1.3.7
131
146
  signing_key:
132
147
  specification_version: 3
133
148
  summary: Abstraction layer for SOA-Services