smarts_api 0.0.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.
- data/.gitignore +16 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +46 -0
- data/LICENSE +22 -0
- data/README.md +7 -0
- data/Rakefile +2 -0
- data/lib/smarts_api/error.rb +3 -0
- data/lib/smarts_api/message/connect_message.rb +45 -0
- data/lib/smarts_api/message/disconnect_message.rb +39 -0
- data/lib/smarts_api/message/evaluate_message.rb +51 -0
- data/lib/smarts_api/message.rb +81 -0
- data/lib/smarts_api/version.rb +5 -0
- data/lib/smarts_api.rb +17 -0
- data/smarts_api.gemspec +24 -0
- data/spec/smarts_api/message/connect_message_spec.rb +54 -0
- data/spec/smarts_api/message/disconnect_message_spec.rb +49 -0
- data/spec/smarts_api/message/evaluate_message_spec.rb +86 -0
- data/spec/smarts_api/message_spec.rb +80 -0
- data/spec/smarts_api_spec.rb +58 -0
- data/spec/spec_helper.rb +3 -0
- metadata +151 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
smarts_api (0.0.1)
|
5
|
+
active_support
|
6
|
+
typhoeus
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
active_support (3.0.0)
|
12
|
+
activesupport (= 3.0.0)
|
13
|
+
activesupport (3.0.0)
|
14
|
+
addressable (2.3.5)
|
15
|
+
crack (0.4.1)
|
16
|
+
safe_yaml (~> 0.9.0)
|
17
|
+
diff-lcs (1.2.4)
|
18
|
+
ethon (0.6.1)
|
19
|
+
ffi (>= 1.3.0)
|
20
|
+
mime-types (~> 1.18)
|
21
|
+
ffi (1.9.0)
|
22
|
+
mime-types (1.25)
|
23
|
+
rspec (2.14.1)
|
24
|
+
rspec-core (~> 2.14.0)
|
25
|
+
rspec-expectations (~> 2.14.0)
|
26
|
+
rspec-mocks (~> 2.14.0)
|
27
|
+
rspec-core (2.14.5)
|
28
|
+
rspec-expectations (2.14.2)
|
29
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
30
|
+
rspec-mocks (2.14.3)
|
31
|
+
safe_yaml (0.9.5)
|
32
|
+
timecop (0.6.3)
|
33
|
+
typhoeus (0.6.5)
|
34
|
+
ethon (~> 0.6.1)
|
35
|
+
webmock (1.13.0)
|
36
|
+
addressable (>= 2.2.7)
|
37
|
+
crack (>= 0.3.2)
|
38
|
+
|
39
|
+
PLATFORMS
|
40
|
+
ruby
|
41
|
+
|
42
|
+
DEPENDENCIES
|
43
|
+
rspec (~> 2.14.1)
|
44
|
+
smarts_api!
|
45
|
+
timecop
|
46
|
+
webmock
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Steve Mitchell
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
class SmartsApi::ConnectMessage < SmartsApi::Message
|
2
|
+
|
3
|
+
def send
|
4
|
+
logger.info "Connecting to #{uri}" if logger.respond_to?(:info)
|
5
|
+
response = Typhoeus::Request.post(uri,
|
6
|
+
:method => method,
|
7
|
+
:headers => {:Accept => "text/json"},
|
8
|
+
:body => make_form(request_params)
|
9
|
+
)
|
10
|
+
raise SmartsApi::Error, "Service connection failed. Recieved empty reply" if response.nil? || response.body.blank?
|
11
|
+
reply = JSON.parse(response.body)
|
12
|
+
|
13
|
+
raise SmartsApi::Error, "Connection failed. Received malformed response." unless reply["Header"] && reply["Header"]["SessionId"]
|
14
|
+
|
15
|
+
session = reply["Header"]["SessionId"]
|
16
|
+
raise SmartsApi::Error, "Connection failed. Did not receive session ID" if session == "00000000-0000-0000-0000-000000000000"
|
17
|
+
|
18
|
+
return session
|
19
|
+
end
|
20
|
+
|
21
|
+
def request_document
|
22
|
+
{:OperationId =>1 , :Header => {:DeploymentId => project_id}}.to_json
|
23
|
+
end
|
24
|
+
|
25
|
+
def request_params
|
26
|
+
params = {
|
27
|
+
:appId => app_id,
|
28
|
+
:pwd => pwd,
|
29
|
+
:reqData => request_document,
|
30
|
+
:reqTime => self.timestamp,
|
31
|
+
:userId => user_id,
|
32
|
+
:workspaceId => workspace_id
|
33
|
+
}
|
34
|
+
signature = {
|
35
|
+
:sign => sign_request(params)
|
36
|
+
}
|
37
|
+
return params.merge(signature)
|
38
|
+
end
|
39
|
+
|
40
|
+
def uri
|
41
|
+
"#{base_uri}connect"
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class SmartsApi::DisconnectMessage < SmartsApi::Message
|
2
|
+
|
3
|
+
|
4
|
+
def send(session)
|
5
|
+
body = make_form(request_params(session))
|
6
|
+
logger.info "Disconnecting" if logger.respond_to?(:info)
|
7
|
+
response = Typhoeus::Request.post(uri,
|
8
|
+
:method => method,
|
9
|
+
:headers => {:Accept => "text/json"},
|
10
|
+
:body => body
|
11
|
+
)
|
12
|
+
raise SmartsApi::Error, "Service connection failed. Recieved empty reply" if response.nil? || response.body.blank?
|
13
|
+
reply = JSON.parse(response.body)
|
14
|
+
|
15
|
+
raise SmartsApi::Error, "Connection failed. Received malformed response." unless reply["Header"] && reply["Header"]["SessionId"]
|
16
|
+
|
17
|
+
session = reply["Header"]["SessionId"]
|
18
|
+
raise SmartsApi::Error, "Connection failed. Did not receive session ID" if session == "00000000-0000-0000-0000-000000000000"
|
19
|
+
return session
|
20
|
+
end
|
21
|
+
|
22
|
+
def request_params(session)
|
23
|
+
params = {
|
24
|
+
:appId => app_id,
|
25
|
+
:reqTime => timestamp,
|
26
|
+
:session => session
|
27
|
+
}
|
28
|
+
signature = {
|
29
|
+
:sign => sign_request(params)
|
30
|
+
}
|
31
|
+
return params.merge(signature)
|
32
|
+
end
|
33
|
+
|
34
|
+
def uri
|
35
|
+
"#{base_uri}disconnect"
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
class SmartsApi::EvaluateMessage < SmartsApi::Message
|
2
|
+
|
3
|
+
def send(session, member, decision)
|
4
|
+
body = make_form(request_params(session, member, decision))
|
5
|
+
logger.info "Evaluating" if logger.respond_to?(:info)
|
6
|
+
response = Typhoeus::Request.post(uri,
|
7
|
+
:method => method,
|
8
|
+
:headers => {:Accept => "text/json"},
|
9
|
+
:body => body
|
10
|
+
)
|
11
|
+
raise SmartsApi::Error, "Rules Evaluation failed failed. Recieved empty reply" if response.nil? || response.body.blank?
|
12
|
+
reply = JSON.parse(response.body)
|
13
|
+
|
14
|
+
raise SmartsApi::Error, "Rules Evaluation failed failed. Received malformed response." unless reply["Header"] && ["Body"]
|
15
|
+
|
16
|
+
body = reply["Body"]
|
17
|
+
|
18
|
+
if body.blank?
|
19
|
+
logger.info "Rules Engine Evaluation failed. \n\n #{body} \n\n #{response.body}" if logger.respond_to?(:info)
|
20
|
+
|
21
|
+
raise SmartsApi::Error, "Rules Evaluation failed. Returned JSON is blank."
|
22
|
+
end
|
23
|
+
|
24
|
+
logger.info "Updating issues" if logger.respond_to?(:info)
|
25
|
+
member.process_smarts_response body
|
26
|
+
return body
|
27
|
+
end
|
28
|
+
|
29
|
+
def request_params(session, member, decision)
|
30
|
+
params = {
|
31
|
+
:appId => app_id,
|
32
|
+
:reqData => request_document(session, member, decision),
|
33
|
+
:reqTime => timestamp,
|
34
|
+
:session => session
|
35
|
+
}
|
36
|
+
signature = {
|
37
|
+
:sign => sign_request(params)
|
38
|
+
}
|
39
|
+
return params.merge(signature)
|
40
|
+
end
|
41
|
+
|
42
|
+
def request_document(session, member, decision)
|
43
|
+
doc = {:OperationId =>1 , :Header => {:SessionId => session, :DecisionId => decision}, :Body => {:Documents => []}}
|
44
|
+
doc[:Body][:Documents] = member.smarts_document
|
45
|
+
return doc.to_json
|
46
|
+
end
|
47
|
+
|
48
|
+
def uri
|
49
|
+
"#{base_uri}evaluate"
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'typhoeus'
|
2
|
+
require 'json'
|
3
|
+
require 'active_support/core_ext/class'
|
4
|
+
require 'active_support/core_ext/array'
|
5
|
+
include ERB::Util
|
6
|
+
require 'openssl'
|
7
|
+
require 'base64'
|
8
|
+
|
9
|
+
class SmartsApi::Message
|
10
|
+
attr_reader :logger
|
11
|
+
cattr_accessor :user_id, :pwd, :app_id, :workspace_id, :access_key, :base_uri, :project_id
|
12
|
+
|
13
|
+
|
14
|
+
def initialize(logger=nil)
|
15
|
+
@logger = logger
|
16
|
+
end
|
17
|
+
|
18
|
+
def method
|
19
|
+
:post
|
20
|
+
end
|
21
|
+
|
22
|
+
def uri
|
23
|
+
base_uri
|
24
|
+
end
|
25
|
+
|
26
|
+
def sign_request(params)
|
27
|
+
#Below you will find the reverse-engineered ruby implementation of Sparkling Logic's security signing algorithm.
|
28
|
+
#this was translated from a javascript library on their documentation site, then
|
29
|
+
|
30
|
+
#The params object include the following parameters, in this order: AppId, pwd, reqData, reqTime, userID, and workspaceID.
|
31
|
+
#naming of the parameters is significant, and case-sensitive.
|
32
|
+
|
33
|
+
#combine the parameters into a long encoded string (again, order of elements is critical)
|
34
|
+
encoded_parts = []
|
35
|
+
http = URI.parse(uri)
|
36
|
+
encoded_parts[0] = method.to_s.upcase
|
37
|
+
encoded_parts[1] = http.host
|
38
|
+
encoded_parts[2] = http.request_uri
|
39
|
+
encoded_parts[3] = encode_hash(params)
|
40
|
+
|
41
|
+
encoded = encoded_parts.join("\n") #Join parts with newline character
|
42
|
+
|
43
|
+
#5 Digest the encode string with SHA256, using the pre-shared AccessKey as the digest key.
|
44
|
+
digest = OpenSSL::Digest::Digest.new('sha256')
|
45
|
+
hash = OpenSSL::HMAC.hexdigest(digest, access_key, encoded)
|
46
|
+
|
47
|
+
#6 OpenSSL::Digest correctly translates the Hash to a string of bytes. Sparkling Logic's algorithm ...unexpectedle converts the hex value to the corresponding ascii code.
|
48
|
+
#So we need to iterate each pair in our byte string and convert to ascii. this is a little ugly, but necessary
|
49
|
+
#7 AND THEN we encode that string to base64. Not sure why, exactly...
|
50
|
+
signature = Base64.encode64(hex_string_to_ascii(hash))
|
51
|
+
#8 re-URL_encode the string and remove line-endings. Again, for absolutely no logical reason. I think the javascript version did this automatically.
|
52
|
+
signature = url_encode(signature)
|
53
|
+
signature = signature.gsub("%0A", "" )
|
54
|
+
return signature
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
def hex_string_to_ascii hex_str
|
60
|
+
#we need to iterate our hex-string and convert each pair to a hex-number. Then evaluate that as ascii code and replace with the corresponding ascii character.
|
61
|
+
ascii_str = ''
|
62
|
+
hex_str.split('').in_groups_of(2){|c| ascii_str << (c[0]+c[1]).hex.chr }
|
63
|
+
ascii_str
|
64
|
+
end
|
65
|
+
|
66
|
+
def timestamp
|
67
|
+
Time.now.utc.iso8601.to_s # => "2012-06-21T18:15:09Z"
|
68
|
+
end
|
69
|
+
|
70
|
+
def encode_hash(hash)
|
71
|
+
new = []
|
72
|
+
hash.each do |k,v|
|
73
|
+
new << k.to_s+"="+url_encode(v)
|
74
|
+
end
|
75
|
+
return new.join("&")
|
76
|
+
end
|
77
|
+
|
78
|
+
def make_form(request_params)
|
79
|
+
request_params.map{|k,v| "#{k}=#{v}"}.join("&")
|
80
|
+
end
|
81
|
+
end
|
data/lib/smarts_api.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
module SmartsApi
|
3
|
+
Dir[File.expand_path('../../lib/smarts_api/*.rb', __FILE__)].each {|f| require f}
|
4
|
+
Dir[File.expand_path('../../lib/smarts_api/*/*.rb', __FILE__)].each {|f| require f}
|
5
|
+
|
6
|
+
def self.evaluate(decision, obj, logger = nil)
|
7
|
+
raise SmartsApi::Error.new("Object to be evaluated must define a method 'smarts_document'") unless obj.respond_to?(:smarts_document)
|
8
|
+
logger.info "processing request for #{obj}" if logger.respond_to?(:info)
|
9
|
+
|
10
|
+
session = SmartsApi::ConnectMessage.new(logger).send
|
11
|
+
response = SmartsApi::EvaluateMessage.new(logger).
|
12
|
+
send(session, obj, decision)
|
13
|
+
|
14
|
+
#SmartsApi::DisconnectMessage.new(logger).disconnect(session)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
data/smarts_api.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/smarts_api/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Steve Mitchell"]
|
6
|
+
gem.email = ["theSteveMitchell@gmail.com"]
|
7
|
+
gem.description = "smarts_api-#{SmartsApi::Version::STRING}"
|
8
|
+
gem.summary = "api gem for Sparkling Logic's SMARTS business logic platform"
|
9
|
+
gem.homepage = "http://github.com/theSteveMitchell/smarts_api"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "smarts_api"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = SmartsApi::Version::STRING
|
17
|
+
|
18
|
+
gem.add_dependency('typhoeus')
|
19
|
+
gem.add_dependency('activesupport')
|
20
|
+
|
21
|
+
gem.add_development_dependency('rspec', '~> 2.14.1')
|
22
|
+
gem.add_development_dependency('webmock')
|
23
|
+
gem.add_development_dependency('timecop')
|
24
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SmartsApi::ConnectMessage do
|
4
|
+
before (:all) do
|
5
|
+
SmartsApi::Message.base_uri = "http://smarts.dev.thismashine.com/"
|
6
|
+
SmartsApi::Message.access_key = "sshhhh...Secret!"
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should return session ID if successful' do
|
10
|
+
|
11
|
+
stub_http_request(:post, "#{SmartsApi::Message.base_uri}connect")
|
12
|
+
.to_return(:status => 200, :body => "{\"Header\":{\"SessionId\":\"487d2c44-43fe-44d3-988f-ea462af03169\"},\"Body\":null,\"ErrorInfo\":null,\"Metrics\":null,\"Success\":true,\"OperationException\":null}")
|
13
|
+
|
14
|
+
|
15
|
+
SmartsApi::ConnectMessage.new().send.should == "487d2c44-43fe-44d3-988f-ea462af03169"
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should throw an error if the connection returns an error' do
|
20
|
+
|
21
|
+
stub_http_request(:post, "#{SmartsApi::Message.base_uri}connect")
|
22
|
+
.to_return(:status => 200, :body => "{\"OperationId\":0,\"Header\":{\"SessionId\":\"00000000-0000-0000-0000-000000000000\",\"TransactionTime\":\"2012-06-22T21:02:16.642625Z\",\"Workspace\":null,\"DeploymentId\":null,\"DecisionId\":null},\"Body\":null,\"ErrorInfo\":{\"ErrorCode\":\"ServerException\",\"ErrorMessage\":\"Exception during connection\",\"Details\":[\"Invalid API access\"]},\"Metrics\":null,\"Success\":false,\"OperationException\":{\"IsRestException\":true,\"ErrorType\":\"DocApiAccessDeniedException\",\"CompleteStackTrace\":\"Type: DocApiAccessDeniedException\\r\\nMessage: Invalid API access\\r\\nStack Trace:\\r\\n at Splog.Rest.Base.DocRestHttpHandler.VerifyHmacSignature(String method, IEnumerable`1 keys, String signature, String[] paramaters)\\r\\n at Splog.Rest.Decisions.DocRestDecisionService.Connect(String appId, String reqTime, String userId, String pwd, String workspaceId, String reqData, String sign)\\r\\n\\r\\n\",\"ExtraInfo\":null,\"Message\":\"[DecisionServer] Exception connecting to the decision server for session 72950db0-9639-477b-b824-b82ad5122b56\",\"Data\":{}}}")
|
23
|
+
|
24
|
+
expect{SmartsApi::ConnectMessage.new().send}.to raise_error(SmartsApi::Error)
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should throw an error if the returned sessionID is empty' do
|
29
|
+
|
30
|
+
stub_http_request(:post, "#{SmartsApi::Message.base_uri}connect")
|
31
|
+
.to_return(:status => 200, :body => "{\"OperationId\":0,\"Header\":{\"SessionId\":\"00000000-0000-0000-0000-000000000000\",\"TransactionTime\":\"2012-06-22T21:02:16.642625Z\",\"Workspace\":null,\"DeploymentId\":null,\"DecisionId\":null},\"Body\":null,\"ErrorInfo\":{\"ErrorCode\":\"ServerException\",\"ErrorMessage\":\"Exception during connection\",\"Details\":[\"Invalid API access\"]},\"Metrics\":null,\"Success\":false,\"OperationException\":{\"IsRestException\":true,\"ErrorType\":\"DocApiAccessDeniedException\",\"CompleteStackTrace\":\"Type: DocApiAccessDeniedException\\r\\nMessage: Invalid API access\\r\\nStack Trace:\\r\\n at Splog.Rest.Base.DocRestHttpHandler.VerifyHmacSignature(String method, IEnumerable`1 keys, String signature, String[] paramaters)\\r\\n at Splog.Rest.Decisions.DocRestDecisionService.Connect(String appId, String reqTime, String userId, String pwd, String workspaceId, String reqData, String sign)\\r\\n\\r\\n\",\"ExtraInfo\":null,\"Message\":\"[DecisionServer] Exception connecting to the decision server for session 72950db0-9639-477b-b824-b82ad5122b56\",\"Data\":{}}}")
|
32
|
+
|
33
|
+
expect{SmartsApi::ConnectMessage.new().send}.to raise_error(SmartsApi::Error)
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should throw an error if no body is returned' do
|
38
|
+
|
39
|
+
stub_http_request(:post, "#{SmartsApi::Message.base_uri}connect")
|
40
|
+
.to_return(:status => 200)
|
41
|
+
expect{SmartsApi::ConnectMessage.new().send}.to raise_error(SmartsApi::Error)
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should throw an error if status code is bad' do
|
46
|
+
|
47
|
+
stub_http_request(:post, "#{SmartsApi::Message.base_uri}connect")
|
48
|
+
.to_return(:status => 500)
|
49
|
+
expect{SmartsApi::ConnectMessage.new().send}.to raise_error(SmartsApi::Error)
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
describe SmartsApi::DisconnectMessage do
|
5
|
+
it 'should return session ID if successful' do
|
6
|
+
|
7
|
+
stub_http_request(:post, "#{SmartsApi::Message.base_uri}disconnect")
|
8
|
+
.to_return(:status => 200, :body => "{\"Header\":{\"SessionId\":\"487d2c44-43fe-44d3-988f-ea462af03169\"},\"Body\":null,\"ErrorInfo\":null,\"Metrics\":null,\"Success\":true,\"OperationException\":null}")
|
9
|
+
|
10
|
+
|
11
|
+
SmartsApi::DisconnectMessage.new().send("487d2c44-43fe-44d3-988f-ea462af03169").should == "487d2c44-43fe-44d3-988f-ea462af03169"
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should throw an error if the connection returns an error' do
|
16
|
+
|
17
|
+
stub_http_request(:post, "#{SmartsApi::Message.base_uri}disconnect")
|
18
|
+
.to_return(:status => 200, :body => "{\"OperationId\":0,\"Header\":{\"SessionId\":\"00000000-0000-0000-0000-000000000000\",\"TransactionTime\":\"2012-06-22T21:02:16.642625Z\",\"Workspace\":null,\"DeploymentId\":null,\"DecisionId\":null},\"Body\":null,\"ErrorInfo\":{\"ErrorCode\":\"ServerException\",\"ErrorMessage\":\"Exception during connection\",\"Details\":[\"Invalid API access\"]},\"Metrics\":null,\"Success\":false,\"OperationException\":{\"IsRestException\":true,\"ErrorType\":\"DocApiAccessDeniedException\",\"CompleteStackTrace\":\"Type: DocApiAccessDeniedException\\r\\nMessage: Invalid API access\\r\\nStack Trace:\\r\\n at Splog.Rest.Base.DocRestHttpHandler.VerifyHmacSignature(String method, IEnumerable`1 keys, String signature, String[] paramaters)\\r\\n at Splog.Rest.Decisions.DocRestDecisionService.Connect(String appId, String reqTime, String userId, String pwd, String workspaceId, String reqData, String sign)\\r\\n\\r\\n\",\"ExtraInfo\":null,\"Message\":\"[DecisionServer] Exception connecting to the decision server for session 72950db0-9639-477b-b824-b82ad5122b56\",\"Data\":{}}}")
|
19
|
+
|
20
|
+
expect{SmartsApi::DisconnectMessage.new().send("487d2c44-43fe-44d3-988f-ea462af03169")}.to raise_error(SmartsApi::Error)
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should throw an error if the returned sessionID is empty returns an error' do
|
25
|
+
|
26
|
+
stub_http_request(:post, "#{SmartsApi::Message.base_uri}disconnect")
|
27
|
+
.to_return(:status => 200, :body => "{\"OperationId\":0,\"Header\":{\"SessionId\":\"00000000-0000-0000-0000-000000000000\",\"TransactionTime\":\"2012-06-22T21:02:16.642625Z\",\"Workspace\":null,\"DeploymentId\":null,\"DecisionId\":null},\"Body\":null,\"ErrorInfo\":{\"ErrorCode\":\"ServerException\",\"ErrorMessage\":\"Exception during connection\",\"Details\":[\"Invalid API access\"]},\"Metrics\":null,\"Success\":false,\"OperationException\":{\"IsRestException\":true,\"ErrorType\":\"DocApiAccessDeniedException\",\"CompleteStackTrace\":\"Type: DocApiAccessDeniedException\\r\\nMessage: Invalid API access\\r\\nStack Trace:\\r\\n at Splog.Rest.Base.DocRestHttpHandler.VerifyHmacSignature(String method, IEnumerable`1 keys, String signature, String[] paramaters)\\r\\n at Splog.Rest.Decisions.DocRestDecisionService.Connect(String appId, String reqTime, String userId, String pwd, String workspaceId, String reqData, String sign)\\r\\n\\r\\n\",\"ExtraInfo\":null,\"Message\":\"[DecisionServer] Exception connecting to the decision server for session 72950db0-9639-477b-b824-b82ad5122b56\",\"Data\":{}}}")
|
28
|
+
|
29
|
+
expect{SmartsApi::DisconnectMessage.new().send("487d2c44-43fe-44d3-988f-ea462af03169")}.to raise_error(SmartsApi::Error)
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should throw an error if no body is returned' do
|
34
|
+
|
35
|
+
stub_http_request(:post, "#{SmartsApi::Message.base_uri}disconnect")
|
36
|
+
.to_return(:status => 200)
|
37
|
+
expect{SmartsApi::DisconnectMessage.new().send("487d2c44-43fe-44d3-988f-ea462af03169")}.to raise_error(SmartsApi::Error)
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should throw an error if status code is bad' do
|
42
|
+
stub_http_request(:post, "#{SmartsApi::Message.base_uri}disconnect")
|
43
|
+
.to_return(:status => 500)
|
44
|
+
expect{SmartsApi::DisconnectMessage.new().send("487d2c44-43fe-44d3-988f-ea462af03169")}.to raise_error(SmartsApi::Error)
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SmartsApi::EvaluateMessage do
|
4
|
+
|
5
|
+
before (:all) do
|
6
|
+
SmartsApi::Message.base_uri = "http://smarts.dev.thismashine.com/"
|
7
|
+
SmartsApi::Message.access_key = "sshhhh...Secret!"
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:eval_class) {
|
11
|
+
Class.new() do
|
12
|
+
|
13
|
+
def smarts_document
|
14
|
+
"123"
|
15
|
+
end
|
16
|
+
|
17
|
+
def process_smarts_response(body)
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
}
|
22
|
+
|
23
|
+
let(:eval_object) {
|
24
|
+
eval_class.new()
|
25
|
+
}
|
26
|
+
|
27
|
+
it 'should return session ID if successful' do
|
28
|
+
|
29
|
+
body = "{\"OperationId\":1,\"Header\":{\"SessionId\":\"e3e2b012-e9b6-45e7-a96a-a009ebf0a07a\"},\"Body\":{\"Documents\":[{\"identification\":{\"actor_group\":\"root\",\"actor_id\":3,\"gender\":\"Male\",\"birth_date\":\"1984-12-08T06:00:00Z\"},\"historical_properties\":[{\"weight\":\"150\"}]}]},\"ErrorInfo\":null,\"Metrics\":null,\"Success\":true,\"OperationException\":null}"
|
30
|
+
|
31
|
+
stub_http_request(:post, "#{SmartsApi::Message.base_uri}evaluate")
|
32
|
+
.to_return(:status => 200, :body => body)
|
33
|
+
SmartsApi::EvaluateMessage.new().send("487d2c44-43fe-44d3-988f-ea462af03169", eval_object, "Issues Analysis Decision").should == JSON.parse(body)["Body"]
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should throw an error if the connection returns an error' do
|
38
|
+
stub_http_request(:post, "#{SmartsApi::Message.base_uri}evaluate")
|
39
|
+
.to_return(:status => 200, :body => "{\"OperationId\":0,\"Header\":{\"SessionId\":\"00000000-0000-0000-0000-000000000000\",\"TransactionTime\":\"2012-06-22T21:02:16.642625Z\",\"Workspace\":null,\"DeploymentId\":null,\"DecisionId\":null},\"Body\":null,\"ErrorInfo\":{\"ErrorCode\":\"ServerException\",\"ErrorMessage\":\"Exception during connection\",\"Details\":[\"Invalid API access\"]},\"Metrics\":null,\"Success\":false,\"OperationException\":{\"IsRestException\":true,\"ErrorType\":\"DocApiAccessDeniedException\",\"CompleteStackTrace\":\"Type: DocApiAccessDeniedException\\r\\nMessage: Invalid API access\\r\\nStack Trace:\\r\\n at Splog.Rest.Base.DocRestHttpHandler.VerifyHmacSignature(String method, IEnumerable`1 keys, String signature, String[] paramaters)\\r\\n at Splog.Rest.Decisions.DocRestDecisionService.Connect(String appId, String reqTime, String userId, String pwd, String workspaceId, String reqData, String sign)\\r\\n\\r\\n\",\"ExtraInfo\":null,\"Message\":\"[DecisionServer] Exception connecting to the decision server for session 72950db0-9639-477b-b824-b82ad5122b56\",\"Data\":{}}}")
|
40
|
+
expect{SmartsApi::EvaluateMessage.new().send("487d2c44-43fe-44d3-988f-ea462af03169",eval_object, "Issues Analysis Decision")}.to raise_error(SmartsApi::Error)
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should throw an error if the returned sessionID is empty returns an error' do
|
45
|
+
|
46
|
+
stub_http_request(:post, "#{SmartsApi::Message.base_uri}evaluate")
|
47
|
+
.to_return(:status => 200, :body => "{\"OperationId\":0,\"Header\":{\"SessionId\":\"00000000-0000-0000-0000-000000000000\",\"TransactionTime\":\"2012-06-22T21:02:16.642625Z\",\"Workspace\":null,\"DeploymentId\":null,\"DecisionId\":null},\"Body\":null,\"ErrorInfo\":{\"ErrorCode\":\"ServerException\",\"ErrorMessage\":\"Exception during connection\",\"Details\":[\"Invalid API access\"]},\"Metrics\":null,\"Success\":false,\"OperationException\":{\"IsRestException\":true,\"ErrorType\":\"DocApiAccessDeniedException\",\"CompleteStackTrace\":\"Type: DocApiAccessDeniedException\\r\\nMessage: Invalid API access\\r\\nStack Trace:\\r\\n at Splog.Rest.Base.DocRestHttpHandler.VerifyHmacSignature(String method, IEnumerable`1 keys, String signature, String[] paramaters)\\r\\n at Splog.Rest.Decisions.DocRestDecisionService.Connect(String appId, String reqTime, String userId, String pwd, String workspaceId, String reqData, String sign)\\r\\n\\r\\n\",\"ExtraInfo\":null,\"Message\":\"[DecisionServer] Exception connecting to the decision server for session 72950db0-9639-477b-b824-b82ad5122b56\",\"Data\":{}}}")
|
48
|
+
expect{SmartsApi::EvaluateMessage.new().send("487d2c44-43fe-44d3-988f-ea462af03169",eval_object, "Issues Analysis Decision")}.to raise_error(SmartsApi::Error)
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should throw an error if no body is returned' do
|
53
|
+
|
54
|
+
stub_http_request(:post, "#{SmartsApi::Message.base_uri}evaluate")
|
55
|
+
.to_return(:status => 200)
|
56
|
+
expect{SmartsApi::EvaluateMessage.new().send("487d2c44-43fe-44d3-988f-ea462af03169",eval_object, "Issues Analysis Decision")}.to raise_error(SmartsApi::Error)
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should throw an error if status code is bad' do
|
61
|
+
|
62
|
+
stub_http_request(:post, "#{SmartsApi::Message.base_uri}evaluate")
|
63
|
+
.to_return(:status => 500)
|
64
|
+
expect{SmartsApi::EvaluateMessage.new().send("487d2c44-43fe-44d3-988f-ea462af03169",eval_object, "Issues Analysis Decision")}.to raise_error(SmartsApi::Error)
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should call the document method on the member object' do
|
69
|
+
eval_object.should_receive(:smarts_document)
|
70
|
+
body = "{\"OperationId\":1,\"Header\":{\"SessionId\":\"e3e2b012-e9b6-45e7-a96a-a009ebf0a07a\"},\"Body\":{\"Documents\":[{\"identification\":{\"actor_group\":\"root\",\"actor_id\":3,\"gender\":\"Male\",\"birth_date\":\"1984-12-08T06:00:00Z\"},\"historical_properties\":[{\"weight\":\"150\"}]}]},\"ErrorInfo\":null,\"Metrics\":null,\"Success\":true,\"OperationException\":null}"
|
71
|
+
|
72
|
+
stub_http_request(:post, "#{SmartsApi::Message.base_uri}evaluate")
|
73
|
+
.to_return(:status => 200, :body => body)
|
74
|
+
SmartsApi::EvaluateMessage.new().send("487d2c44-43fe-44d3-988f-ea462af03169", eval_object, "Issues Analysis Decision").should == JSON.parse(body)["Body"]
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should pass the returned message body to the evaluated object for processing" do
|
78
|
+
|
79
|
+
body = "{\"OperationId\":1,\"Header\":{\"SessionId\":\"e3e2b012-e9b6-45e7-a96a-a009ebf0a07a\"},\"Body\":{\"Documents\":[{\"identification\":{\"actor_group\":\"root\",\"actor_id\":3,\"gender\":\"Male\",\"birth_date\":\"1984-12-08T06:00:00Z\"},\"historical_properties\":[{\"weight\":\"150\"}]}]},\"ErrorInfo\":null,\"Metrics\":null,\"Success\":true,\"OperationException\":null}"
|
80
|
+
eval_object.should_receive(:process_smarts_response).with(JSON.parse(body)["Body"])
|
81
|
+
stub_http_request(:post, "#{SmartsApi::Message.base_uri}evaluate")
|
82
|
+
.to_return(:status => 200, :body => body)
|
83
|
+
SmartsApi::EvaluateMessage.new().send("487d2c44-43fe-44d3-988f-ea462af03169", eval_object, "Issues Analysis Decision").should == JSON.parse(body)["Body"]
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SmartsApi::Message do
|
4
|
+
|
5
|
+
before (:all) do
|
6
|
+
SmartsApi::Message.base_uri = "http://www.versign.com/request/doSomething.aspc"
|
7
|
+
SmartsApi::Message.access_key = "secretKey"
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'initializer' do
|
11
|
+
it "should keep the logger for logging purposes" do
|
12
|
+
logger = Logger.new(STDOUT)
|
13
|
+
message = SmartsApi::Message.new(logger)
|
14
|
+
|
15
|
+
message.logger.should == logger
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'timestamp' do
|
21
|
+
it "should be formatted correctly" do
|
22
|
+
Timecop.freeze(Time.utc(2012, 6, 21, 12, 34, 56))
|
23
|
+
SmartsApi::Message.new().timestamp.should == "2012-06-21T12:34:56Z"
|
24
|
+
Timecop.return
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'hash encoding' do
|
30
|
+
it 'should convert a hash into a url_encoded string' do
|
31
|
+
hash = {:a => "123", :b => "aber=/3sd8:&++\\", :c => "hellpo"}
|
32
|
+
|
33
|
+
SmartsApi::Message.new().encode_hash(hash).should == "a=123&b=aber%3D%2F3sd8%3A%26%2B%2B%5C&c=hellpo"
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'ascii conversion' do
|
40
|
+
it "should convert a hex string to ascii in the way the service expects" do
|
41
|
+
#even if it's wrong...'
|
42
|
+
|
43
|
+
SmartsApi::Message.new().hex_string_to_ascii("3D").should == "="
|
44
|
+
SmartsApi::Message.new().hex_string_to_ascii("0B").should == "\v"
|
45
|
+
SmartsApi::Message.new().hex_string_to_ascii("52").should == "R"
|
46
|
+
|
47
|
+
hex = "52410baf20302e9e2c31b5ecea597348aa3df3067c58cf35cb5bd94273dc1de0"
|
48
|
+
SmartsApi::Message.new().hex_string_to_ascii(hex)
|
49
|
+
.should == "RA\v\xAF 0.\x9E,1\xB5\xEC\xEAYsH\xAA=\xF3\x06|X\xCF5\xCB[\xD9Bs\xDC\x1D\xE0"
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
#Notes on signing: The signing algorithm for Sparkling Logic is very brittle and depends on ALL of the
|
56
|
+
#values in sparkling_logic.yml, as well as timestamp. The below tests will fail if any of those settings change.
|
57
|
+
#so if the signature does not match the expected, and one of those settings did change, we can update the expected sig.
|
58
|
+
describe 'signing' do
|
59
|
+
it "should create a correct signature when no params supplied" do
|
60
|
+
Timecop.freeze(Time.utc(2012, 6, 21, 12, 34, 56))
|
61
|
+
|
62
|
+
SmartsApi::Message.new().sign_request({})
|
63
|
+
.should == "dOqhdTRLwMSUBSO7HZDRTsD63fVVA%2FKnffCn3DuaRnE%3D"
|
64
|
+
Timecop.return
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should create a correct signature when params supplied" do
|
68
|
+
Timecop.freeze(Time.utc(2012, 6, 21, 12, 34, 56))
|
69
|
+
params = {:param1 => "nothing", :param2 => "nothing More"}
|
70
|
+
|
71
|
+
SmartsApi::Message.new().sign_request(params)
|
72
|
+
.should == "EIvlfFrm7FpotCVp6MIt76P6bz%2BUKWbVaO7pAIclyd8%3D"
|
73
|
+
Timecop.return
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SmartsApi do
|
4
|
+
|
5
|
+
describe "evaluate" do
|
6
|
+
|
7
|
+
before (:all) do
|
8
|
+
SmartsApi::Message.base_uri = "http://smarts.dev.thismashine.com/"
|
9
|
+
SmartsApi::Message.access_key = "sshhhh...Secret!"
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "expectations on eval object" do
|
13
|
+
|
14
|
+
it 'should raise error and not call connect if obj is nil' do
|
15
|
+
expect{SmartsApi.evaluate(
|
16
|
+
"string", nil
|
17
|
+
)}.to raise_error(SmartsApi::Error, "Object to be evaluated must define a method 'smarts_document'")
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should raise error and not call connect if obj does not define the document method' do
|
21
|
+
RegularClass = Class.new() {}
|
22
|
+
instance = RegularClass.new
|
23
|
+
instance.should_not be_nil
|
24
|
+
expect{SmartsApi.evaluate(
|
25
|
+
"string", instance
|
26
|
+
)}.to raise_error(SmartsApi::Error, "Object to be evaluated must define a method 'smarts_document'")
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should accept any object that defines the document method' do
|
30
|
+
EvalClass= Class.new() do
|
31
|
+
def smarts_document
|
32
|
+
"EvalClass.Instance 1"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
instance = EvalClass.new
|
37
|
+
instance.should_not be_nil
|
38
|
+
|
39
|
+
SmartsApi::ConnectMessage.any_instance.should_receive(:send).and_return("session 334")
|
40
|
+
SmartsApi::EvaluateMessage.any_instance.should_receive(:send)
|
41
|
+
SmartsApi.evaluate("string", instance)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "specifying decision string" do
|
46
|
+
it "should include the decision name and session when sending the evaluate message" do
|
47
|
+
|
48
|
+
instance = EvalClass.new
|
49
|
+
instance.should_not be_nil
|
50
|
+
SmartsApi::ConnectMessage.any_instance.should_receive(:send).and_return("session 3339")
|
51
|
+
SmartsApi::EvaluateMessage.any_instance.should_receive(:send).with("session 3339", instance, "Chosen_decision")
|
52
|
+
SmartsApi.evaluate("Chosen_decision", instance)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: smarts_api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Steve Mitchell
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-09-23 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: typhoeus
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: activesupport
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 2.14.1
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.14.1
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: webmock
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: timecop
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
description: smarts_api-0.0.1
|
95
|
+
email:
|
96
|
+
- theSteveMitchell@gmail.com
|
97
|
+
executables: []
|
98
|
+
extensions: []
|
99
|
+
extra_rdoc_files: []
|
100
|
+
files:
|
101
|
+
- .gitignore
|
102
|
+
- Gemfile
|
103
|
+
- Gemfile.lock
|
104
|
+
- LICENSE
|
105
|
+
- README.md
|
106
|
+
- Rakefile
|
107
|
+
- lib/smarts_api.rb
|
108
|
+
- lib/smarts_api/error.rb
|
109
|
+
- lib/smarts_api/message.rb
|
110
|
+
- lib/smarts_api/message/connect_message.rb
|
111
|
+
- lib/smarts_api/message/disconnect_message.rb
|
112
|
+
- lib/smarts_api/message/evaluate_message.rb
|
113
|
+
- lib/smarts_api/version.rb
|
114
|
+
- smarts_api.gemspec
|
115
|
+
- spec/smarts_api/message/connect_message_spec.rb
|
116
|
+
- spec/smarts_api/message/disconnect_message_spec.rb
|
117
|
+
- spec/smarts_api/message/evaluate_message_spec.rb
|
118
|
+
- spec/smarts_api/message_spec.rb
|
119
|
+
- spec/smarts_api_spec.rb
|
120
|
+
- spec/spec_helper.rb
|
121
|
+
homepage: http://github.com/theSteveMitchell/smarts_api
|
122
|
+
licenses: []
|
123
|
+
post_install_message:
|
124
|
+
rdoc_options: []
|
125
|
+
require_paths:
|
126
|
+
- lib
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
128
|
+
none: false
|
129
|
+
requirements:
|
130
|
+
- - ! '>='
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
134
|
+
none: false
|
135
|
+
requirements:
|
136
|
+
- - ! '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
requirements: []
|
140
|
+
rubyforge_project:
|
141
|
+
rubygems_version: 1.8.24
|
142
|
+
signing_key:
|
143
|
+
specification_version: 3
|
144
|
+
summary: api gem for Sparkling Logic's SMARTS business logic platform
|
145
|
+
test_files:
|
146
|
+
- spec/smarts_api/message/connect_message_spec.rb
|
147
|
+
- spec/smarts_api/message/disconnect_message_spec.rb
|
148
|
+
- spec/smarts_api/message/evaluate_message_spec.rb
|
149
|
+
- spec/smarts_api/message_spec.rb
|
150
|
+
- spec/smarts_api_spec.rb
|
151
|
+
- spec/spec_helper.rb
|