psc 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +13 -0
- data/.rvmrc +2 -0
- data/.yardopts +4 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +20 -0
- data/LICENSE +20 -0
- data/README.md +163 -0
- data/Rakefile +69 -0
- data/cucumber.yml +13 -0
- data/features/step_definitions/eval_steps.rb +14 -0
- data/features/step_definitions/setup_steps.rb +7 -0
- data/features/studies.feature +21 -0
- data/features/support/env.rb +33 -0
- data/features/support/int_psc.rb +165 -0
- data/int-psc/README-int-psc.md +57 -0
- data/int-psc/hsqldb/baseline.log +197 -0
- data/int-psc/hsqldb/baseline.properties +17 -0
- data/int-psc/hsqldb/baseline.script +295 -0
- data/int-psc/hsqldb/datasource.properties +18 -0
- data/int-psc/hsqldb/datasource.script +2205 -0
- data/int-psc/jetty/jetty-runner-7.4.0.v20110414.jar +0 -0
- data/int-psc/state/ABC 1200.xml +115 -0
- data/int-psc/state/int-psc-state.xml +64 -0
- data/lib/psc.rb +47 -0
- data/lib/psc/client.rb +35 -0
- data/lib/psc/connection.rb +96 -0
- data/lib/psc/faraday.rb +11 -0
- data/lib/psc/faraday/http_basic.rb +35 -0
- data/lib/psc/faraday/psc_token.rb +42 -0
- data/lib/psc/faraday/string_is_xml.rb +25 -0
- data/lib/psc/version.rb +3 -0
- data/meta.rakefile +30 -0
- data/psc.gemspec +33 -0
- data/spec/fixtures/studies-json.http +37 -0
- data/spec/middleware_helper.rb +18 -0
- data/spec/psc/client_spec.rb +39 -0
- data/spec/psc/connection_spec.rb +156 -0
- data/spec/psc/faraday/http_basic_spec.rb +15 -0
- data/spec/psc/faraday/psc_token_spec.rb +38 -0
- data/spec/psc/faraday/string_is_xml_spec.rb +49 -0
- data/spec/psc/version_spec.rb +11 -0
- data/spec/psc_spec.rb +16 -0
- data/spec/spec_helper.rb +15 -0
- data/tasks/int-psc.rake +84 -0
- data/tasks/psc/TODO +3 -0
- data/tasks/psc/state.rb +393 -0
- 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
|
data/lib/psc/version.rb
ADDED
data/meta.rakefile
ADDED
@@ -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'
|
data/psc.gemspec
ADDED
@@ -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
|