psc 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +13 -0
  2. data/.rvmrc +2 -0
  3. data/.yardopts +4 -0
  4. data/CHANGELOG.md +4 -0
  5. data/Gemfile +20 -0
  6. data/LICENSE +20 -0
  7. data/README.md +163 -0
  8. data/Rakefile +69 -0
  9. data/cucumber.yml +13 -0
  10. data/features/step_definitions/eval_steps.rb +14 -0
  11. data/features/step_definitions/setup_steps.rb +7 -0
  12. data/features/studies.feature +21 -0
  13. data/features/support/env.rb +33 -0
  14. data/features/support/int_psc.rb +165 -0
  15. data/int-psc/README-int-psc.md +57 -0
  16. data/int-psc/hsqldb/baseline.log +197 -0
  17. data/int-psc/hsqldb/baseline.properties +17 -0
  18. data/int-psc/hsqldb/baseline.script +295 -0
  19. data/int-psc/hsqldb/datasource.properties +18 -0
  20. data/int-psc/hsqldb/datasource.script +2205 -0
  21. data/int-psc/jetty/jetty-runner-7.4.0.v20110414.jar +0 -0
  22. data/int-psc/state/ABC 1200.xml +115 -0
  23. data/int-psc/state/int-psc-state.xml +64 -0
  24. data/lib/psc.rb +47 -0
  25. data/lib/psc/client.rb +35 -0
  26. data/lib/psc/connection.rb +96 -0
  27. data/lib/psc/faraday.rb +11 -0
  28. data/lib/psc/faraday/http_basic.rb +35 -0
  29. data/lib/psc/faraday/psc_token.rb +42 -0
  30. data/lib/psc/faraday/string_is_xml.rb +25 -0
  31. data/lib/psc/version.rb +3 -0
  32. data/meta.rakefile +30 -0
  33. data/psc.gemspec +33 -0
  34. data/spec/fixtures/studies-json.http +37 -0
  35. data/spec/middleware_helper.rb +18 -0
  36. data/spec/psc/client_spec.rb +39 -0
  37. data/spec/psc/connection_spec.rb +156 -0
  38. data/spec/psc/faraday/http_basic_spec.rb +15 -0
  39. data/spec/psc/faraday/psc_token_spec.rb +38 -0
  40. data/spec/psc/faraday/string_is_xml_spec.rb +49 -0
  41. data/spec/psc/version_spec.rb +11 -0
  42. data/spec/psc_spec.rb +16 -0
  43. data/spec/spec_helper.rb +15 -0
  44. data/tasks/int-psc.rake +84 -0
  45. data/tasks/psc/TODO +3 -0
  46. data/tasks/psc/state.rb +393 -0
  47. metadata +311 -0
@@ -0,0 +1,25 @@
1
+ require 'psc/faraday'
2
+
3
+ module Psc
4
+ module Faraday
5
+ ##
6
+ # Middleware which sets the request content type to `text/xml` if
7
+ # the provided body is a `String` and the content type isn't
8
+ # already set.
9
+ class StringIsXml
10
+ def initialize(app)
11
+ @app = app
12
+ end
13
+
14
+ ##
15
+ # Sets the content type request header if appropriate
16
+ def call(env)
17
+ if String === env[:body]
18
+ env[:request_headers]['Content-Type'] ||= 'text/xml'
19
+ end
20
+
21
+ @app.call(env)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module Psc
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,30 @@
1
+ PLATFORMS = {
2
+ :ruby18 => 'ruby-1.8.7-p334',
3
+ :jruby => 'jruby-1.6.1',
4
+ :ruby19 => 'ruby-1.9.2-p180'
5
+ }
6
+
7
+ BUNDLER_VERSION = '1.0.13'
8
+
9
+ def ci_env
10
+ (ENV["PSC_RB_ENV"] || :ruby18).to_sym
11
+ end
12
+
13
+ namespace :ci do
14
+ task :rvmrc do
15
+ puts "#{PLATFORMS[ci_env]}@psc-rb"
16
+ end
17
+
18
+ task :ensure_bundler_available do
19
+ unless Gem.available?('bundler', BUNDLER_VERSION)
20
+ puts "Installing bundler"
21
+ sh "gem install bundler -v '#{BUNDLER_VERSION}'"
22
+ end
23
+ end
24
+
25
+ task :setup => [:ensure_bundler_available] do
26
+ sh "bundle _#{BUNDLER_VERSION}_ update"
27
+ end
28
+ end
29
+
30
+ task :default => 'ci:setup'
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "psc/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "psc"
7
+ s.version = Psc::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Rhett Sutphin"]
10
+ s.email = ["r-sutphin@northwestern.edu"]
11
+ s.homepage = "https://github.com/NUBIC/psc.rb"
12
+ s.summary = "A lightweight ruby client for Patient Study Calendar's RESTful HTTP API"
13
+ s.description = "A lightweight ruby client for Patient Study Calendar's RESTful HTTP API"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_dependency 'faraday', '~> 0.7.0'
21
+ s.add_dependency 'builder', '>= 2.1.2'
22
+ s.add_dependency 'faraday-stack', '~> 0.1.1'
23
+ s.add_dependency 'nokogiri', '~> 1.4'
24
+ s.add_dependency 'activesupport', '>= 2.3' # for the JSON adapter
25
+
26
+ s.add_development_dependency 'rspec', '~> 2.6'
27
+ s.add_development_dependency 'ci_reporter', '~> 1.6'
28
+ s.add_development_dependency 'cucumber', '~> 0.10.2'
29
+ s.add_development_dependency 'childprocess', '~> 0.1'
30
+ s.add_development_dependency 'highline'
31
+ s.add_development_dependency 'webmock', '~> 1.6'
32
+ s.add_development_dependency 'yard', '~> 0.6.8'
33
+ end
@@ -0,0 +1,37 @@
1
+ HTTP/1.1 200 OK
2
+ Date: Mon, 16 May 2011 21:47:14 GMT
3
+ Server: Restlet-Framework/2.0.3
4
+ Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
5
+ Pragma: no-cache
6
+ Accept-Ranges: bytes
7
+ Vary: Accept-Charset,Accept-Encoding,Accept-Language,Accept
8
+ Content-Type: application/json
9
+ Connection: close
10
+ Transfer-Encoding: chunked
11
+
12
+ {
13
+ "studies": [
14
+ {
15
+ "privileges": [
16
+ "develop",
17
+ "see-development",
18
+ "set-managing-sites",
19
+ "release",
20
+ "assign-identifiers",
21
+ "purge"
22
+ ],
23
+ "assigned_identifier": "NU 1404"
24
+ },
25
+ {
26
+ "privileges": [
27
+ "develop",
28
+ "see-development",
29
+ "set-managing-sites",
30
+ "release",
31
+ "assign-identifiers",
32
+ "purge"
33
+ ],
34
+ "assigned_identifier": "ECOG 5607"
35
+ }
36
+ ]
37
+ }
@@ -0,0 +1,18 @@
1
+ shared_context 'middleware' do
2
+ let(:app) { mock('app') }
3
+ let(:env) { { :request_headers => ::Faraday::Utils::Headers.new } }
4
+ let(:headers) { env[:request_headers] }
5
+
6
+ before { app.stub!(:call) }
7
+
8
+ def do_call
9
+ subject.call(env)
10
+ end
11
+ end
12
+
13
+ shared_examples 'unconditional middleware' do
14
+ it 'continues the chain' do
15
+ app.should_receive(:call)
16
+ subject.call(env)
17
+ end
18
+ end
@@ -0,0 +1,39 @@
1
+ require File.expand_path("../../spec_helper.rb", __FILE__)
2
+
3
+ module Psc
4
+ describe Client do
5
+ let(:options) { { :authenticator => { :basic => %w(superuser superuser) } } }
6
+ let(:url) { 'http://psc.example.org/' }
7
+ let(:client) { Psc::Client.new(url, options) }
8
+
9
+ def mockable_uri(path)
10
+ URI.join(url.sub(%r{//}, '//superuser:superuser@'), "api/v1/#{path}").to_s
11
+ end
12
+
13
+ describe '.new' do
14
+ it 'creates a connection from a string and options' do
15
+ client.connection.should be_a(Psc::Connection)
16
+ end
17
+
18
+ it 'yields a connection builder if given a block' do
19
+ client = Psc::Client.new(url, options) do |builder|
20
+ builder.response :logger
21
+ end
22
+ client.connection.builder[-2].should == ::Faraday::Response::Logger
23
+ end
24
+ end
25
+
26
+ describe '#studies' do
27
+ before do
28
+ stub_request(:get, mockable_uri('studies.json')).
29
+ to_return(http_fixture('studies-json'))
30
+ end
31
+
32
+ let(:actual) { client.studies }
33
+
34
+ it "has the data from the response" do
35
+ actual.first['assigned_identifier'].should == 'NU 1404'
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,156 @@
1
+ require File.expand_path("../../spec_helper.rb", __FILE__)
2
+
3
+ module Psc
4
+ describe Connection do
5
+ let(:conn) {
6
+ Psc::Connection.new('http://psc.example.org/', options)
7
+ }
8
+
9
+ let(:options) {
10
+ { :authenticator => { :basic => ['foo', 'bar'] } }
11
+ }
12
+
13
+ describe 'appending the API path' do
14
+ def actual_path_prefix(url)
15
+ Psc::Connection.new(url, options).path_prefix
16
+ end
17
+
18
+ it 'is appended correctly if the URL ends with a slash' do
19
+ actual_path_prefix('http://psc.example.org/').should == '/api/v1'
20
+ end
21
+
22
+ it 'is appended correctly if the URL does not end with a slash' do
23
+ actual_path_prefix('http://psc.example.org').should == '/api/v1'
24
+ end
25
+
26
+ it 'is appended correctly if the URL has path info that ends with a slash' do
27
+ actual_path_prefix('http://www.example.net/psc/').should == '/psc/api/v1'
28
+ end
29
+
30
+ it 'is appended correctly if the URL has path info that does not end with a slash' do
31
+ actual_path_prefix('http://www.example.net/psc').should == '/psc/api/v1'
32
+ end
33
+
34
+ it 'is not appended if it is already there and it ends with a slash' do
35
+ actual_path_prefix('http://www.example.net/api/v1').should == '/api/v1'
36
+ end
37
+
38
+ it 'is not appended if it is already there and it does not end with a slash' do
39
+ actual_path_prefix('http://www.example.net/api/v1/').should == '/api/v1'
40
+ end
41
+ end
42
+
43
+ DEFAULT_MIDDLEWARE_COUNT = 6
44
+
45
+ describe 'the default middleware' do
46
+ it 'has the authentication middleware first' do
47
+ conn.builder[0].should == Psc::Faraday::HttpBasic
48
+ end
49
+
50
+ it 'has the string-is-xml middleware next' do
51
+ conn.builder[1].should == Psc::Faraday::StringIsXml
52
+ end
53
+
54
+ it 'has the Faraday JSON request middleware next' do
55
+ conn.builder[2].should == ::Faraday::Request::JSON
56
+ end
57
+
58
+ it 'has the Faraday URL-encoded request middleware next' do
59
+ conn.builder[3].should == ::Faraday::Request::UrlEncoded
60
+ end
61
+
62
+ it 'has the FaradayStack XML parser middleware next' do
63
+ conn.builder[4].should == ::FaradayStack::ResponseXML
64
+ end
65
+
66
+ it 'has the FaradayStack JSON parser middleware next' do
67
+ conn.builder[5].should == ::FaradayStack::ResponseJSON
68
+ end
69
+ end
70
+
71
+ describe 'user-specified additional middleware' do
72
+ let(:custom_conn) {
73
+ Psc::Connection.new('http://psc.example.org/', options) do |builder|
74
+ builder.use ::Faraday::Response::Logger
75
+ end
76
+ }
77
+
78
+ it 'comes after the default middleware' do
79
+ custom_conn.builder[DEFAULT_MIDDLEWARE_COUNT].should == ::Faraday::Response::Logger
80
+ end
81
+
82
+ it 'comes before the adapter' do
83
+ custom_conn.builder.handlers.last.should == ::Faraday::Adapter::NetHttp
84
+ end
85
+ end
86
+
87
+ describe 'selecting authentication middleware' do
88
+ let(:auth_middleware) { conn.builder[0] } # this is a Faraday::Builder::Handler
89
+ let(:auth_mw_args) { conn.builder[0].instance_eval { @args } }
90
+
91
+ context 'when :basic' do
92
+ before do
93
+ options[:authenticator] = { :basic => %w(foo bar) }
94
+ end
95
+
96
+ it 'uses the HttpBasic middleware' do
97
+ auth_middleware.klass.should == Psc::Faraday::HttpBasic
98
+ end
99
+
100
+ it 'provides the username and password to the middleware' do
101
+ auth_mw_args.should == %w(foo bar)
102
+ end
103
+ end
104
+
105
+ context 'when :token' do
106
+ before do
107
+ options[:authenticator] = { :token => lambda { 'foo' } }
108
+ end
109
+
110
+ it 'uses the PscToken middleware' do
111
+ auth_middleware.klass.should == Psc::Faraday::PscToken
112
+ end
113
+
114
+ it 'provides the token (or creator) to the middleware' do
115
+ auth_mw_args.first.call.should == 'foo'
116
+ end
117
+ end
118
+
119
+ context 'when an unknown kind' do
120
+ before do
121
+ options[:authenticator] = { :quuxor => 7 }
122
+ end
123
+
124
+ it 'raises an error' do
125
+ lambda { conn }.should raise_error('Unsupported authentication method :quuxor.')
126
+ end
127
+ end
128
+
129
+ context 'when :authenticator not specified' do
130
+ before do
131
+ options.delete :authenticator
132
+ end
133
+
134
+ it 'raises an error' do
135
+ lambda { conn }.should raise_error(
136
+ 'No authentication method specified. Please include the :authenticator option.')
137
+ end
138
+ end
139
+ end
140
+
141
+ describe 'selecting an adapter' do
142
+ let(:adapter) { conn.builder.handlers.last }
143
+
144
+ it 'defaults to net/http' do
145
+ adapter.klass.should == ::Faraday::Adapter::NetHttp
146
+ end
147
+
148
+ it 'uses the one the user provides if any' do
149
+ custom_conn = Connection.new('dc', options) do |builder|
150
+ builder.adapter :excon
151
+ end
152
+ custom_conn.builder.handlers.last.should == ::Faraday::Adapter::Excon
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,15 @@
1
+ require File.expand_path("../../../spec_helper", __FILE__)
2
+
3
+ module Psc::Faraday
4
+ describe HttpBasic do
5
+ include_context "middleware"
6
+ it_behaves_like "unconditional middleware"
7
+
8
+ subject { HttpBasic.new(app, 'jo', 'basil') }
9
+
10
+ it 'adds the appropriate Authorization header' do
11
+ do_call
12
+ headers['Authorization'].should == 'Basic am86YmFzaWw='
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,38 @@
1
+ require File.expand_path("../../../spec_helper", __FILE__)
2
+
3
+ module Psc::Faraday
4
+ describe PscToken do
5
+ include_context "middleware"
6
+
7
+ describe "with a static token" do
8
+ it_behaves_like "unconditional middleware"
9
+
10
+ subject { PscToken.new(app, 'jo-9') }
11
+
12
+ it 'adds the appropriate Authorization header' do
13
+ do_call
14
+ headers['Authorization'].should == 'psc_token jo-9'
15
+ end
16
+ end
17
+
18
+ describe "with a dynamic token" do
19
+ it_behaves_like "unconditional middleware"
20
+
21
+ subject do
22
+ i = 7
23
+ PscToken.new(app, lambda { i += 1; i ** 2 })
24
+ end
25
+
26
+ it 'adds the appropriate Authorization header' do
27
+ do_call
28
+ headers['Authorization'].should == 'psc_token 64'
29
+ end
30
+
31
+ it 'invokes the lambda for each call' do
32
+ do_call
33
+ do_call
34
+ headers['Authorization'].should == 'psc_token 81'
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,49 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ module Psc::Faraday
4
+ describe StringIsXml do
5
+ include_context 'middleware'
6
+
7
+ subject { StringIsXml.new(app) }
8
+
9
+ before do
10
+ env[:body] = '<foo/>'
11
+ end
12
+
13
+ it 'treats a string body as text/xml' do
14
+ do_call
15
+ headers['Content-Type'].should == 'text/xml'
16
+ end
17
+
18
+ it 'leaves a Hash body alone' do
19
+ env[:body] = { 'foo' => 'bar' }
20
+ do_call
21
+ headers['Content-Type'].should be_nil
22
+ end
23
+
24
+ it 'does nothing if the content type is already set' do
25
+ headers['Content-Type'] = 'application/vnd.sun.wadl+xml'
26
+ do_call
27
+ headers['Content-Type'].should == 'application/vnd.sun.wadl+xml'
28
+ end
29
+
30
+ context 'in the same stack as Faraday::Request::JSON' do
31
+ let(:conn) do
32
+ ::Faraday::Connection.new('http://example.org/') do |builder|
33
+ builder.use StringIsXml
34
+ builder.use ::Faraday::Request::JSON
35
+ end
36
+ end
37
+
38
+ it 'sets the content type for a string body' do
39
+ response = conn.put('dc', '<foo/>')
40
+ response.env[:request_headers]['Content-Type'].should == 'text/xml'
41
+ end
42
+
43
+ it 'does not interfere with the JSON middleware for a Hash body' do
44
+ response = conn.put('dc', { 'bar' => 'baz' })
45
+ response.env[:request_headers]['Content-Type'].should == 'application/json'
46
+ end
47
+ end
48
+ end
49
+ end