assaydepot 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ require "openssl"
2
+
3
+ module AssayDepot
4
+ class Webhook
5
+
6
+ def self.construct_event(payload, signature_header, endpoint_secret)
7
+ sig_hash = {}
8
+
9
+ # get t=, v0=, v1= components of the signature
10
+ signature_header.split(',').each do |str|
11
+ sig_hash[str.split('=')[0]] = str.split('=')[1]
12
+ end
13
+
14
+ mac = OpenSSL::HMAC.hexdigest("SHA256", "#{sig_hash["t"]}0123456789abcdefghijklmnopqrstuvwxyz", payload)
15
+ raise AssayDepot::SignatureVerificationError.new "Event (#{Rails.env}) not properly signed." if Rails.env == 'test' || Rails.env == 'development' && mac != sig_hash["v0"]
16
+ mac = OpenSSL::HMAC.hexdigest("SHA256", "#{sig_hash["t"]}#{endpoint_secret}", payload)
17
+ raise AssayDepot::SignatureVerificationError.new if Rails.env != 'test' && Rails.env != 'development' && mac != sig_hash["v1"]
18
+ raise AssayDepot::SignatureVerificationError.new "Invalid timestamp." if sig_hash["t"].to_i < 5.minutes.ago.to_i
19
+ end
20
+ end
21
+ end
@@ -1,109 +1,214 @@
1
+ require 'forwardable'
1
2
  module AssayDepot
2
- module Model
3
+ module DatabaseModel
3
4
  module ClassMethods
5
+ def all(options = {})
6
+ self.new.all(options)
7
+ end
8
+ end
9
+
10
+ def self.included(base)
11
+ base.include AssayDepot::Model
12
+ base.extend AssayDepot::Model::ClassMethods
13
+ base.extend ClassMethods
14
+ end
15
+
16
+ def all(options = {})
17
+ result = self.clone
18
+ result.internal_options = options
19
+ result
20
+ end
21
+
22
+ # Overridden from Model
23
+ def internal_results
24
+ unless @internal_results
25
+ @internal_results = JSON.parse(Client.new(endpoint: self.class.endpoint(nil)).get(params: @internal_options))
26
+ end
27
+ @internal_results
28
+ end
29
+ end
30
+
31
+ module SearchModel
32
+ module ClassMethods
33
+ # find and where to modify params
4
34
  def find(query)
5
35
  self.new.find(query)
6
36
  end
37
+
7
38
  def where(conditions={})
8
39
  self.new.where(conditions)
9
40
  end
10
- def get(id)
11
- Client.new(:search_type => search_type).get(id)
12
- end
13
41
  end
14
42
 
15
43
  def self.included(base)
44
+ base.extend AssayDepot::Model::ClassMethods
16
45
  base.extend ClassMethods
17
- base.extend Forwardable
18
- base.def_delegators :private_results,
19
- :each,
20
- :[],
21
- :count,
22
- :collect,
23
- :map,
24
- :tap,
25
- :<=>,
26
- :compact,
27
- :each_index,
28
- :each_with_index,
29
- :empty?,
30
- :flatten,
31
- :include?,
32
- :index,
33
- :length,
34
- :first,
35
- :last,
36
- :keep_if,
37
- :reject,
38
- :reverse
46
+ base.include AssayDepot::Model
47
+ base.include AssayDepot::Pageable
39
48
 
40
49
  attr_accessor :search_query
41
50
  attr_accessor :search_facets
42
- attr_accessor :search_options
51
+ end
43
52
 
44
- def initialize(options={})
45
- @search_query = options[:search_query] || ""
46
- @search_facets = options[:search_facets] || {}
47
- @search_options = options[:search_options] || {:page => 1}
48
- end
53
+ def initialize(options={})
54
+ @internal_options = options[:search_options] || { }
55
+ @search_query = options[:search_query] || ""
56
+ @search_facets = options[:search_facets] || { }
57
+ end
49
58
 
50
- def initialize_copy(source)
51
- super
52
- @search_query = @search_query.dup
53
- @search_facets = @search_facets.dup
54
- end
59
+ def facets
60
+ internal_results["facets"]
61
+ end
55
62
 
56
- def query_time
57
- search_results["query_time"]
58
- end
59
- def total
60
- search_results["total"]
61
- end
62
- def page
63
- search_results["page"]
64
- end
65
- def per_page
66
- search_results["per_page"]
67
- end
68
- def facets
69
- search_results["facets"]
63
+ def find(query)
64
+ result = self.clone
65
+ result.search_query = query
66
+ result
67
+ end
68
+
69
+ def where(conditions={})
70
+ result = self.clone
71
+ result.search_facets = self.search_facets ? self.search_facets.merge(conditions) : conditions
72
+ result
73
+ end
74
+
75
+ # Overridden from Model
76
+ def internal_results
77
+ unless @internal_results
78
+ results = Client.new(endpoint: self.class.endpoint(nil)).search(query: search_query, facets: search_facets, params: internal_options)
79
+ @internal_results = JSON.parse(results)
70
80
  end
81
+ @internal_results
82
+ end
83
+ end
71
84
 
72
- def page(page_num)
73
- options( { "page" => page_num } )
85
+ module Model
86
+ module ClassMethods
87
+ def get_token(client_id, client_secret, site)
88
+ response = Client.new.request(AssayDepot::TokenAuth.endpoint(site), {}, {}, {:username => client_id, :password => client_secret})
89
+ response[AssayDepot::TokenAuth.ref_name]
74
90
  end
75
91
 
76
- def per_page(page_size)
77
- options( { "per_page" => page_size } )
92
+ def get_endpoint(id, endpoint, format = "json")
93
+ id = id[0] if id && id.kind_of?(Array)
94
+ id ? "#{endpoint}/#{id}.#{format}" : "#{endpoint}.#{format}"
78
95
  end
79
96
 
80
- def find(query)
81
- result = self.clone
82
- result.search_query = search_query
97
+ # HTTP request verbs
98
+ # optional "id" followed by optional hash
99
+ def get(id: nil, params: {}, format: "json")
100
+ puts "GET id #{id}, params #{params}" if ENV["DEBUG"] == "true"
101
+ result = Client.new(endpoint: endpoint(id, format)).get(params: params)
102
+ return JSON.parse(result) if format == "json"
83
103
  result
84
104
  end
85
105
 
86
- def where(conditions={})
87
- result = self.clone
88
- result.search_facets = self.search_facets ? self.search_facets.merge(conditions) : conditions
106
+ def put(id: nil, body: nil, params: {}, format: "json")
107
+ puts "PUT id #{id}, body #{body.to_s}, params #{params}" if ENV["DEBUG"] == "true"
108
+ result = Client.new(endpoint: endpoint(id, format)).put( body: body, params: params )
109
+ return JSON.parse(result) if format == "json"
89
110
  result
90
111
  end
91
112
 
92
- def options(options={})
93
- result = self.clone
94
- result.search_options = self.search_options ? self.search_options.merge(options) : options
113
+ def patch(id: nil, body: nil, params: {}, format: "json")
114
+ puts "PATCH id #{id}, body #{body.to_s}, params #{params}" if ENV["DEBUG"] == "true"
115
+ result = Client.new(endpoint: endpoint(id, format)).put( body: body, params: params )
116
+ return JSON.parse(result) if format == "json"
95
117
  result
96
118
  end
97
119
 
98
- def private_results
99
- search_results[self.class.ref_name]
120
+ def post(id: nil, body: nil, params: {}, format: "json")
121
+ puts "POST id #{id}, body #{body.to_s}, params #{params}" if ENV["DEBUG"] == "true"
122
+ result = Client.new(endpoint: endpoint(id, format)).post( body: body, params: params )
123
+ return JSON.parse(result) if format == "json"
124
+ result
100
125
  end
101
- def search_results
102
- unless @search_results
103
- @search_results = Client.new(:search_type => self.class.search_type).search(search_query, search_facets, search_options)
104
- end
105
- @search_results
126
+
127
+ def delete(id: nil, body: nil, params: {}, format: "json")
128
+ puts "DELETE id #{id}, params #{params}" if ENV["DEBUG"] == "true"
129
+ result = Client.new(endpoint: endpoint(id, format)).delete(params: params)
130
+ return JSON.parse(result) if format == "json"
131
+ result
106
132
  end
107
133
  end
134
+
135
+ def self.included(base)
136
+ base.extend ClassMethods
137
+ base.extend Forwardable
138
+ base.def_delegators :private_results,
139
+ :each,
140
+ :[],
141
+ :count,
142
+ :collect,
143
+ :map,
144
+ :tap,
145
+ :<=>,
146
+ :compact,
147
+ :each_index,
148
+ :each_with_index,
149
+ :empty?,
150
+ :flatten,
151
+ :include?,
152
+ :index,
153
+ :length,
154
+ :first,
155
+ :last,
156
+ :keep_if,
157
+ :reject,
158
+ :reverse
159
+
160
+ attr_accessor :internal_options
161
+ end
162
+
163
+ def initialize(options={})
164
+ @internal_options = options[:search_options] || { }
165
+ end
166
+
167
+ def initialize_copy(source)
168
+ super
169
+ @search_query = @search_query.dup
170
+ @search_facets = @search_facets.dup
171
+ end
172
+
173
+ def query_time
174
+ internal_results["query_time"]
175
+ end
176
+
177
+ def total
178
+ internal_results["total"]
179
+ end
180
+
181
+ def options(options={})
182
+ result = self.clone
183
+ result.internal_options = self.internal_options ? self.internal_options.merge(options) : options
184
+ result
185
+ end
186
+
187
+ def private_results
188
+ internal_results[self.class.ref_name]
189
+ end
190
+
191
+ # def internal_results
192
+ # # To be overridden
193
+ # # If I leave this method in, it gets called, possibly a problem with the order of inclusion
194
+ # end
195
+ end
196
+
197
+ module Pageable
198
+ def page
199
+ internal_results["page"]
200
+ end
201
+
202
+ def per_page
203
+ internal_results["per_page"]
204
+ end
205
+
206
+ def page(page_num)
207
+ options( { "page" => page_num } )
208
+ end
209
+
210
+ def per_page(page_size)
211
+ options( { "per_page" => page_size } )
212
+ end
108
213
  end
109
- end
214
+ end
@@ -1,3 +1,3 @@
1
1
  module AssayDepot
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -0,0 +1,30 @@
1
+ require 'assaydepot'
2
+ require 'dotenv'
3
+ Dotenv.load
4
+
5
+ describe AssayDepot do
6
+ context "scientist_api backoffice tests" do
7
+ before(:all) do
8
+ AssayDepot.configure do |config|
9
+ config.access_token = ENV['BACKOFFICE_ACCESS_TOKEN']
10
+ config.url = "#{ENV['BACKOFFICE_SITE']}/api/v2"
11
+ end
12
+ end
13
+
14
+ context "info" do
15
+ let(:info) { AssayDepot::Info.get() }
16
+
17
+ it "info version V2" do
18
+ info["api_version"].should == "V2"
19
+ end
20
+ end
21
+
22
+ context "quoted wares" do
23
+ let(:qw) { AssayDepot::QuotedWare.get() }
24
+
25
+ it "get all quoted wares" do
26
+ qw.is_a?(Array).should == true
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,124 @@
1
+ require 'assaydepot'
2
+ require 'dotenv'
3
+ Dotenv.load
4
+
5
+ describe AssayDepot do
6
+ context "when accessing the api via token client credentials" do
7
+ before(:all) do
8
+ AssayDepot.configure do |config|
9
+ config.access_token = ENV['ACCESS_TOKEN']
10
+ config.url = "#{ENV['SITE']}/api/v2"
11
+ end
12
+ end
13
+
14
+ context "and searching for wares matching \"antibody\"" do
15
+ let(:wares) { AssayDepot::Ware.find("antibody") }
16
+
17
+ it "should return a Ware object" do
18
+ wares.class.should == AssayDepot::Ware
19
+ end
20
+
21
+ it "should return some wares" do
22
+ wares.total.should > 1
23
+ end
24
+
25
+ context "and getting the details for the first ware" do
26
+ let(:ware_result) { AssayDepot::Ware.get(id: wares.first["id"]) }
27
+
28
+ it "should have a ware" do
29
+ ware_result["ware"].should_not be_nil
30
+ end
31
+
32
+ it "should have a description" do
33
+ ware_result["ware"]["description"].should_not be_nil
34
+ end
35
+ end
36
+ end
37
+
38
+ context "when searching for wares of type CustomService" do
39
+ let(:wares) { AssayDepot::Ware.where(:ware_type => "CustomService") }
40
+
41
+ it "should return facets" do
42
+ wares.facets.should_not be_empty
43
+ end
44
+
45
+ it "should include the source facet" do
46
+ wares.facets.should include("source")
47
+ end
48
+
49
+ it "should include the ware_type facet" do
50
+ wares.facets.should include("ware_type")
51
+ end
52
+
53
+ # RR: replaced "available_provider_names"
54
+ it "should include the technology facet" do
55
+ wares.facets.should include("technology")
56
+ end
57
+
58
+ it "should include the certifications facet" do
59
+ wares.facets.should include("certifications")
60
+ end
61
+
62
+ it "should include the countries facet" do
63
+ wares.facets.should include("countries")
64
+ end
65
+
66
+ it "should include the protein_type facet" do
67
+ wares.facets.should include("protein_type")
68
+ end
69
+
70
+ it "should include the clonality facet" do
71
+ wares.facets.should include("clonality")
72
+ end
73
+
74
+ it "should include the cell_source facet" do
75
+ wares.facets.should include("cell_source")
76
+ end
77
+
78
+ it "should include the species facet" do
79
+ wares.facets.should include("species")
80
+ end
81
+
82
+ it "should include the tissue facet" do
83
+ wares.facets.should include("tissue")
84
+ end
85
+ end
86
+
87
+ context "when searching for wares using a chained query" do
88
+ let(:wares) { AssayDepot::Ware.where(:ware_type => "CustomService").where(:source => "central-staging") }
89
+
90
+ it "should return wares" do
91
+ wares.total.should > 0
92
+ end
93
+
94
+ it "the ware's name should be available" do
95
+ wares.first["name"].should_not be_nil
96
+ end
97
+ end
98
+
99
+ context "and searching for providers that start with the letter a" do
100
+ let(:starts_with) {AssayDepot::Provider.get()["facets"]["starts_with"]["buckets"].first}
101
+ let(:providers) { AssayDepot::Provider.where(:starts_with => starts_with["key"]).per_page(50) }
102
+
103
+ it "should return a Provider object" do
104
+ providers.class.should == AssayDepot::Provider
105
+ end
106
+
107
+ it "should return some providers" do
108
+ providers.total.should == starts_with["doc_count"]
109
+ end
110
+
111
+ context "and getting the details for the first provider" do
112
+ let(:provider_result) { AssayDepot::Provider.get(id: providers.first["id"]) }
113
+
114
+ it "have a provider" do
115
+ provider_result["provider"].should_not be_nil
116
+ end
117
+
118
+ it "have keywords" do
119
+ expect(provider_result['provider']).to have_key('keywords')
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end