sapoci-connect 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md ADDED
@@ -0,0 +1,41 @@
1
+ *2011-10-15 (0.1.7)*
2
+
3
+ * Accept GET and POST on ./bin/sapoci-search
4
+
5
+ *2011-09-22 (0.1.6)*
6
+
7
+ * Fixed parsing issues with longtext field on HTML (from SAPOCI gem)
8
+
9
+ *2011-09-07 (0.1.5)*
10
+
11
+ * Raise RedirectWithoutLocation error when returning
12
+ HTTP status 302 without setting the HTTP Location header
13
+
14
+ *2011-09-06 (0.1.4)*
15
+
16
+ * Make POSTs really post as encoded name/value pairs in the body
17
+
18
+ *2011-09-06 (0.1.3)*
19
+
20
+ * Changed middleware for background search to put
21
+ `SAPOCI::Document` into `response.env[:sapoci]`
22
+ instead of `response.env[:body]`.
23
+
24
+ *2011-09-06 (0.1.2)*
25
+
26
+ * Changed semantics of search to always add HTTP method,
27
+ i.e. `:get` or `:post`
28
+ * Added test for timeout errors
29
+ * Added test for relative redirects (i.e. redirects without hostname)
30
+
31
+
32
+ *2011-08-30 (0.1.1)*
33
+
34
+ * Simplify API by separating connection initialization (Faraday) and OCI
35
+ handling via Faraday middleware.
36
+ * Added `bin/sapoci-search` script to perform, you name it, SAP OCI
37
+ background search.
38
+
39
+ *2011-08-29 (0.1.0)*
40
+
41
+ * Initial version, supporting OCI background search only.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2007-2011 Oliver Eilhard
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
data/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # SAP OCI Connect
2
+
3
+ We use this library to work with eprocurement punchout systems that
4
+ comply to the SAP OCI 4.0 specification.
5
+
6
+ ## Features
7
+
8
+ ### SAP OCI Background Search
9
+
10
+ It's as simple as this:
11
+
12
+ conn = Faraday.new("http://onlineshop.com/path", :params => {"token" => "123"}) do |builder|
13
+ builder.use SAPOCI::Connect::Middleware::FollowRedirects
14
+ builder.use SAPOCI::Connect::Middleware::PassCookies
15
+ builder.use SAPOCI::Connect::Middleware::BackgroundSearch
16
+ builder.adapter :net_http
17
+ end
18
+
19
+ conn.options[:timeout] = 5
20
+ conn.options[:open_timeout] = 10
21
+
22
+ resp = SAPOCI::Connect.search(:get, conn, "toner", "http://return.to/me")
23
+ puts resp.status # => 200
24
+ puts resp.body # => <SAPOCI::Document>
25
+ puts resp.env[:raw_body] # => "<html>...</html>"
26
+
27
+ Review [Faraday](https://github.com/technoweenie/faraday) for details on
28
+ connection initiation.
29
+
30
+ ## Testing
31
+
32
+ Here's how to test locally:
33
+
34
+ $ bundle update
35
+ $ # Start a second console
36
+ $ ruby test/live_server.rb
37
+ $ # Back in first console
38
+ $ rake
39
+
40
+ To test external servers, use the REMOTE environment variable:
41
+
42
+ $ REMOTE="http://remote-site.com/Login.aspx?u=demo&p=secret" rake
43
+
44
+ ## Credits
45
+
46
+ Standing on the shoulder of giants, where giants include:
47
+
48
+ * Rick Olson for [faraday](https://github.com/technoweenie/faraday),
49
+ * Ilya Grigorik for [em-synchrony](https://github.com/igrigorik/em-synchrony),
50
+ [em-http-request](https://github.com/igrigorik/em-http-request) and stuff,
51
+ * David Balatero and Paul Dix for [typhoeus](https://github.com/dbalatero/typhoeus)
52
+
53
+ ... and many other contributors. Thanks, guys. You rock!
54
+
data/bin/sapoci-search ADDED
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'optparse'
4
+ require 'ostruct'
5
+ require 'uri'
6
+
7
+ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + '/../lib')
8
+ require 'sapoci/connect'
9
+ require 'faraday'
10
+ require 'rack'
11
+ require 'sapoci'
12
+ require 'active_support/core_ext'
13
+
14
+ options = OpenStruct.new
15
+ parser = OptionParser.new do |opts|
16
+ opts.on("-d", "--[no-]debug", "Show raw response") do |o|
17
+ options.debug = o
18
+ end
19
+
20
+ opts.on("-p", "--post", "Use POST instead of GET") do |o|
21
+ options.http_post = o
22
+ end
23
+
24
+ opts.separator ""
25
+ opts.separator "Common options:"
26
+
27
+ opts.on_tail("-?", "-h", "--help", "Show this message") do
28
+ puts opts
29
+ exit
30
+ end
31
+ end
32
+
33
+ begin
34
+ parser.parse!(ARGV)
35
+
36
+ # Parameters
37
+ url = ARGV[0]
38
+ keywords = ARGV[1]
39
+
40
+ # Parse URL and query parameters
41
+ uri = URI.parse(url)
42
+ params = Rack::Utils.parse_query(uri.query) if uri.query
43
+ uri.query = nil
44
+
45
+ # Setup
46
+ conn = Faraday.new(uri.to_s, :ssl => {:verify => true}) do |builder|
47
+ builder.use SAPOCI::Connect::Middleware::FollowRedirects
48
+ builder.use SAPOCI::Connect::Middleware::PassCookies
49
+ builder.use SAPOCI::Connect::Middleware::BackgroundSearch
50
+ builder.use Faraday::Response::Logger if options.debug
51
+ builder.adapter :net_http
52
+ end
53
+
54
+ # Execute
55
+ method = options.http_post ? :post : :get
56
+ resp = SAPOCI::Connect.search(method, conn, keywords, "http://return.to/me", params)
57
+ if resp.status == 200
58
+ doc = resp.env[:sapoci]
59
+ $stdout.puts "%3s %-15s %-30s %s" % ["Idx", "Vendormat", "Description", "Price per unit"]
60
+ $stdout.puts "".ljust(98, '-')
61
+ doc.items.each do |item|
62
+ $stdout.puts "%3s %-15s %-30s %10.3f %-3s per %9.2f %-3s" % [item.index, item.vendormat, item.description, item.price, item.currency, item.priceunit, item.unit]
63
+ $stdout.puts " %s" % [item.longtext]
64
+ end
65
+ $stdout.puts "===> #{doc.items.size} items"
66
+ $stdout.puts resp.body.to_s if options.debug
67
+ exit 0
68
+ elsif resp.status == 404
69
+ $stdout.puts "Not found (HTTP status #{resp.status})"
70
+ $stdout.puts resp.body.to_s if options.debug
71
+ exit 1
72
+ elsif resp.status == 500
73
+ $stdout.puts "Server crashed (HTTP status #{resp.status})"
74
+ $stdout.puts resp.body.to_s if options.debug
75
+ exit 1
76
+ else
77
+ $stdout.puts "Error: HTTP status code=#{resp.status}"
78
+ $stdout.puts resp.body.to_s if options.debug
79
+ exit 1
80
+ end
81
+
82
+
83
+ rescue => e
84
+ $stderr.print "#{e.class}: " unless e.class == RuntimeError
85
+ $stderr.puts e.message
86
+ $stderr.puts e.backtrace
87
+ exit 1
88
+ end
89
+
90
+
@@ -0,0 +1,22 @@
1
+ module SAPOCI
2
+ module Connect
3
+ module Middleware
4
+ class BackgroundSearch < Faraday::Response::Middleware
5
+ def initialize(app, options = {})
6
+ @options = options
7
+ super(app)
8
+ end
9
+
10
+ def call(env)
11
+ @app.call(env).on_complete do |resp|
12
+ case env[:status]
13
+ when 200
14
+ env[:sapoci] = SAPOCI::Document.from_html(env[:body])
15
+ else
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,55 @@
1
+ module SAPOCI
2
+ module Connect
3
+ module Middleware
4
+ class RedirectLimitReached < Faraday::Error::ClientError
5
+ attr_reader :response
6
+
7
+ def initialize(response)
8
+ super "too many redirects; last one to: #{response['location']}"
9
+ @response = response
10
+ end
11
+ end
12
+
13
+ class RedirectWithoutLocation < Faraday::Error::ClientError
14
+ attr_reader :response
15
+
16
+ def initialize(response)
17
+ super "redirect without setting HTTP Location header"
18
+ @response = response
19
+ end
20
+ end
21
+
22
+ class FollowRedirects < Faraday::Middleware
23
+ REDIRECTS = [301, 302, 303]
24
+ FOLLOW_LIMIT = 5
25
+
26
+ def initialize(app, options = {})
27
+ super(app)
28
+ @options = options
29
+ @limit = options[:limit] || FOLLOW_LIMIT
30
+ end
31
+
32
+ def call(env)
33
+ process(@app.call(env), @limit)
34
+ end
35
+
36
+ def process(response, limit)
37
+ response.on_complete do |env|
38
+ if redirect?(response)
39
+ raise RedirectLimitReached, response if limit.zero?
40
+ raise RedirectWithoutLocation, response if response['location'].blank?
41
+ env[:url] += response['location']
42
+ env[:method] = :get
43
+ response = process(@app.call(env), limit - 1)
44
+ end
45
+ end
46
+ end
47
+
48
+ def redirect?(response)
49
+ REDIRECTS.include? response.status
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
@@ -0,0 +1,25 @@
1
+ module SAPOCI
2
+ module Connect
3
+ module Middleware
4
+ class PassCookies < Faraday::Response::Middleware
5
+ def initialize(app, options = {})
6
+ @options = options
7
+ @cookies = []
8
+ super(app)
9
+ end
10
+
11
+ def call(env)
12
+ unless @cookies.empty?
13
+ cookies = @cookies.compact.uniq.join("; ").squeeze(";")
14
+ env[:request_headers]['Cookie'] = cookies
15
+ end
16
+ @app.call(env).on_complete do |resp|
17
+ if cookie = resp[:response_headers]['Set-Cookie']
18
+ @cookies << cookie
19
+ end
20
+ end
21
+ end # call
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,54 @@
1
+ require 'faraday'
2
+ require 'sapoci'
3
+ require 'sapoci/connect/middleware/follow_redirects'
4
+ require 'sapoci/connect/middleware/pass_cookies'
5
+ require 'sapoci/connect/middleware/background_search'
6
+
7
+ module SAPOCI
8
+ module Connect
9
+
10
+ # Perform an OCI background search.
11
+ #
12
+ # If you need to follow redirects and pass cookies along, you should
13
+ # initialize and use Faraday with this pattern:
14
+ #
15
+ # conn = Faraday.new("http://shop.com/path", :params => {"optional" => "value"}) do |builder|
16
+ # builder.use SAPOCI::Connect::Middleware::FollowRedirects
17
+ # builder.use SAPOCI::Connect::Middleware::PassCookies
18
+ # builder.use SAPOCI::Connect::Middleware::BackgroundSearch
19
+ # builder.adapter :net_http
20
+ # end
21
+ # conn.options[:timeout] = 3
22
+ # conn.options[:open_timeout] = 5
23
+ # resp = SAPOCI::Connect.search(:get, conn, "toner", "http://return.to/me")
24
+ # puts resp.status # => 200
25
+ # puts resp.body # => <SAPOCI::Document>
26
+ #
27
+ def self.search(method, connection, keywords, hook_url, extra_params = nil)
28
+ params = {
29
+ "FUNCTION" => "BACKGROUND_SEARCH",
30
+ "SEARCHSTRING" => keywords,
31
+ "HOOK_URL" => hook_url
32
+ }
33
+ params.update(extra_params) if extra_params
34
+
35
+ unless connection.builder.handlers.include?(SAPOCI::Connect::Middleware::BackgroundSearch)
36
+ connection.use SAPOCI::Connect::Middleware::BackgroundSearch
37
+ end
38
+
39
+ case method.to_sym
40
+ when :get
41
+ connection.get do |req|
42
+ req.params = params
43
+ end
44
+ when :post
45
+ connection.post do |req|
46
+ req.body = Faraday::Utils.build_nested_query params
47
+ end
48
+ else
49
+ raise "SAPOCI::Connect.search only allows :get or :post requests"
50
+ end
51
+ end
52
+
53
+ end
54
+ end
metadata ADDED
@@ -0,0 +1,171 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sapoci-connect
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.7
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Oliver Eilhard
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-10-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: faraday
16
+ requirement: &70144890289320 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.7.4
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70144890289320
25
+ - !ruby/object:Gem::Dependency
26
+ name: em-synchrony
27
+ requirement: &70144890288740 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 1.0.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70144890288740
36
+ - !ruby/object:Gem::Dependency
37
+ name: em-http-request
38
+ requirement: &70144890288140 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 1.0.0
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70144890288140
47
+ - !ruby/object:Gem::Dependency
48
+ name: typhoeus
49
+ requirement: &70144890287460 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 0.2.4
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70144890287460
58
+ - !ruby/object:Gem::Dependency
59
+ name: rack
60
+ requirement: &70144890286820 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: 1.1.0
66
+ - - <
67
+ - !ruby/object:Gem::Version
68
+ version: '2'
69
+ type: :runtime
70
+ prerelease: false
71
+ version_requirements: *70144890286820
72
+ - !ruby/object:Gem::Dependency
73
+ name: sapoci
74
+ requirement: &70144890285840 !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: 0.1.4
80
+ type: :runtime
81
+ prerelease: false
82
+ version_requirements: *70144890285840
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: &70144890285160 !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ~>
89
+ - !ruby/object:Gem::Version
90
+ version: '1.0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: *70144890285160
94
+ - !ruby/object:Gem::Dependency
95
+ name: rdoc
96
+ requirement: &70144890284560 !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ version: '2.5'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: *70144890284560
105
+ - !ruby/object:Gem::Dependency
106
+ name: rake
107
+ requirement: &70144890283900 !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ! '>='
111
+ - !ruby/object:Gem::Version
112
+ version: 0.9.2
113
+ type: :development
114
+ prerelease: false
115
+ version_requirements: *70144890283900
116
+ - !ruby/object:Gem::Dependency
117
+ name: sinatra
118
+ requirement: &70144890283300 !ruby/object:Gem::Requirement
119
+ none: false
120
+ requirements:
121
+ - - ~>
122
+ - !ruby/object:Gem::Version
123
+ version: '1.2'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: *70144890283300
127
+ description: HTTP client library for working with SAP OCI compliant servers.
128
+ email:
129
+ - oliver.eilhard@gmail.com
130
+ executables:
131
+ - sapoci-search
132
+ extensions: []
133
+ extra_rdoc_files:
134
+ - CHANGELOG.md
135
+ - LICENSE
136
+ - README.md
137
+ files:
138
+ - bin/sapoci-search
139
+ - lib/sapoci/connect.rb
140
+ - lib/sapoci/connect/middleware/background_search.rb
141
+ - lib/sapoci/connect/middleware/follow_redirects.rb
142
+ - lib/sapoci/connect/middleware/pass_cookies.rb
143
+ - CHANGELOG.md
144
+ - LICENSE
145
+ - README.md
146
+ homepage: http://github.com/meplato/sapoci-connect
147
+ licenses: []
148
+ post_install_message:
149
+ rdoc_options:
150
+ - --charset=UTF-8
151
+ require_paths:
152
+ - lib
153
+ required_ruby_version: !ruby/object:Gem::Requirement
154
+ none: false
155
+ requirements:
156
+ - - ! '>='
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ required_rubygems_version: !ruby/object:Gem::Requirement
160
+ none: false
161
+ requirements:
162
+ - - ! '>='
163
+ - !ruby/object:Gem::Version
164
+ version: 1.3.6
165
+ requirements: []
166
+ rubyforge_project:
167
+ rubygems_version: 1.8.9
168
+ signing_key:
169
+ specification_version: 3
170
+ summary: HTTP client library for working with SAP OCI compliant servers.
171
+ test_files: []