haystack_ruby 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/haystack_ruby/auth/conversation.rb +184 -0
- data/lib/haystack_ruby/config.rb +22 -0
- data/lib/haystack_ruby/error.rb +5 -0
- data/lib/haystack_ruby/object.rb +70 -0
- data/lib/haystack_ruby/point.rb +80 -0
- data/lib/haystack_ruby/project.rb +159 -0
- data/lib/haystack_ruby/railtie.rb +18 -0
- data/lib/haystack_ruby/range.rb +34 -0
- data/lib/haystack_ruby/timestamp.rb +20 -0
- data/lib/haystack_ruby/types/bin.rb +13 -0
- data/lib/haystack_ruby/types/coord.rb +15 -0
- data/lib/haystack_ruby/types/date.rb +15 -0
- data/lib/haystack_ruby/types/date_time.rb +15 -0
- data/lib/haystack_ruby/types/marker.rb +10 -0
- data/lib/haystack_ruby/types/n_a.rb +10 -0
- data/lib/haystack_ruby/types/number.rb +14 -0
- data/lib/haystack_ruby/types/ref.rb +13 -0
- data/lib/haystack_ruby/types/str.rb +12 -0
- data/lib/haystack_ruby/types/time.rb +16 -0
- data/lib/haystack_ruby/types/uri.rb +12 -0
- data/lib/haystack_ruby.rb +14 -0
- data/spec/haystack_ruby/auth/scram_spec.rb +40 -0
- data/spec/haystack_ruby/config_spec.rb +18 -0
- data/spec/haystack_ruby/point_spec.rb +144 -0
- data/spec/haystack_ruby/project_spec.rb +42 -0
- data/spec/models/point_demo.rb +10 -0
- data/spec/spec_helper.rb +22 -0
- metadata +132 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1bbe8505587fa1e720665305767e15d16a78a11b
|
4
|
+
data.tar.gz: 8ddbb550be5df6a595b1dd3740f6405c3acf9685
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 93f07240acd1a2a50c41b2a9e27789c1b8d1cff16e4a2825d9f4577e7b345cedbe7581fe8b5370dffacb4e5885b6a9f30f7e8d371ade56017788a117a6f3b6c2
|
7
|
+
data.tar.gz: bc73973de118ee8a52e9dab9efc59ba9486b048d6bba07d9656c277e19a9610dbaaad5527362f14008b1e9e49c7fcffd471604fc0e4d9923d693821d360c3236
|
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'securerandom' #consider using openssl random
|
2
|
+
require 'base64'
|
3
|
+
require 'openssl'
|
4
|
+
module HaystackRuby
|
5
|
+
module Auth
|
6
|
+
module Scram
|
7
|
+
class Conversation
|
8
|
+
attr_reader :auth_token, :nonce, :server_nonce, :server_salt
|
9
|
+
|
10
|
+
def initialize(user, url)
|
11
|
+
@user = user
|
12
|
+
@url = url
|
13
|
+
@nonce = SecureRandom.base64.tr('=','') #TODO check if problem to potentially strip =
|
14
|
+
@digest = OpenSSL::Digest::SHA256.new
|
15
|
+
@handshake_token = Base64.strict_encode64(@user.username)
|
16
|
+
end
|
17
|
+
|
18
|
+
def authorize
|
19
|
+
res = send_first_message
|
20
|
+
parse_first_response(res)
|
21
|
+
res = send_second_message
|
22
|
+
parse_second_response(res)
|
23
|
+
end
|
24
|
+
|
25
|
+
def connection
|
26
|
+
@connection ||= Faraday.new(:url => @url) do |faraday|
|
27
|
+
# faraday.response :logger # log requests to STDOUT
|
28
|
+
faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
|
29
|
+
faraday.headers['Accept'] = 'application/json' #TODO enable more formats
|
30
|
+
faraday.headers['Content-Type'] = 'text/plain'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# first message sent by client to server
|
35
|
+
def send_first_message
|
36
|
+
res = connection.get('about') do |req|
|
37
|
+
req.headers['Authorization'] = "SCRAM handshakeToken=#{@handshake_token},data=#{Base64.urlsafe_encode64(first_message).tr('=','')}"
|
38
|
+
end
|
39
|
+
res
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
# pull server data out of response to first message
|
44
|
+
def parse_first_response(response)
|
45
|
+
|
46
|
+
# parse server response to first message
|
47
|
+
response_str = response.env.response_headers['www-authenticate']
|
48
|
+
unless response_str.index('scram ') == 0
|
49
|
+
throw 'Invalid response from server'
|
50
|
+
end
|
51
|
+
response_str.slice! 'scram '
|
52
|
+
response_vars = {}
|
53
|
+
response_str.split(', ').each do |pair|
|
54
|
+
key,value = pair.split('=')
|
55
|
+
response_vars[key] = value
|
56
|
+
end
|
57
|
+
unless response_vars['hash'] == 'SHA-256'
|
58
|
+
throw "Server requested unsupported hash algorithm: #{response_vars['hash']}"
|
59
|
+
end
|
60
|
+
|
61
|
+
# todo check handshake token (should be base64 encode of username)
|
62
|
+
|
63
|
+
@server_first_msg = Base64.decode64(response_vars["data"])
|
64
|
+
server_vars = {}
|
65
|
+
@server_first_msg.split(',').each do |pair|
|
66
|
+
key,value = pair.split '='
|
67
|
+
server_vars[key] = value
|
68
|
+
end
|
69
|
+
@server_nonce = server_vars['r']
|
70
|
+
@server_salt = server_vars['s'] #Base64.decode64(server_vars['s'])
|
71
|
+
@server_iterations = server_vars['i'].to_i
|
72
|
+
end
|
73
|
+
|
74
|
+
def send_second_message
|
75
|
+
res = connection.get('about') do |req|
|
76
|
+
req.headers['Authorization'] = "SCRAM handshakeToken=#{@handshake_token},data=#{Base64.strict_encode64(client_final).tr('=','')}"
|
77
|
+
end
|
78
|
+
res
|
79
|
+
|
80
|
+
end
|
81
|
+
def parse_second_response(response)
|
82
|
+
begin
|
83
|
+
response_str = response.env.response_headers['authentication-info']
|
84
|
+
response_vars = {}
|
85
|
+
response_str.split(', ').each do |pair|
|
86
|
+
key,value = pair.split('=')
|
87
|
+
response_vars[key] = value
|
88
|
+
end
|
89
|
+
# decode data attribute to check server signature is as expected
|
90
|
+
key,val = Base64.decode64(response_vars['data']).split('=')
|
91
|
+
response_vars[key] = val
|
92
|
+
server_sig = response_vars['v']
|
93
|
+
unless server_sig == expected_server_signature
|
94
|
+
throw "invalid signature from server"
|
95
|
+
end
|
96
|
+
@auth_token = response_vars['authToken']
|
97
|
+
# rescue Exception => e
|
98
|
+
# raise
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_auth_token
|
104
|
+
res = connection.get('about') do |req|
|
105
|
+
req.headers['Authorization'] = "BEARER authToken=#{@auth_token}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
# utility methods, closely matched with SCRAM notation and algorithm overview here:
|
112
|
+
# https://tools.ietf.org/html/rfc5802#section-3
|
113
|
+
|
114
|
+
def auth_message
|
115
|
+
@auth_message ||= "#{first_message},#{@server_first_msg},#{without_proof}"
|
116
|
+
end
|
117
|
+
|
118
|
+
def client_final
|
119
|
+
@client_final ||= "#{without_proof},p=" +
|
120
|
+
client_proof(client_key, client_signature(stored_key(client_key), auth_message))
|
121
|
+
end
|
122
|
+
|
123
|
+
def client_key
|
124
|
+
@client_key ||= hmac(salted_password, 'Client Key')
|
125
|
+
end
|
126
|
+
|
127
|
+
def client_proof(key, signature)
|
128
|
+
@client_proof ||= Base64.strict_encode64(xor(key, signature))
|
129
|
+
end
|
130
|
+
|
131
|
+
def client_signature(key, message)
|
132
|
+
@client_signature ||= hmac(key, message)
|
133
|
+
end
|
134
|
+
|
135
|
+
def expected_server_key
|
136
|
+
@server_key ||= hmac(salted_password, 'Server Key')
|
137
|
+
end
|
138
|
+
|
139
|
+
def expected_server_signature
|
140
|
+
@server_signature ||= Base64.strict_encode64(hmac(expected_server_key, auth_message)).tr('=','')
|
141
|
+
end
|
142
|
+
|
143
|
+
def first_message
|
144
|
+
"n=#{@user.username},r=#{@nonce}"
|
145
|
+
end
|
146
|
+
|
147
|
+
def h(string)
|
148
|
+
@digest.digest(string)
|
149
|
+
end
|
150
|
+
|
151
|
+
def hi(data)
|
152
|
+
OpenSSL::PKCS5.pbkdf2_hmac(
|
153
|
+
data,
|
154
|
+
Base64.decode64(@server_salt),
|
155
|
+
@server_iterations,
|
156
|
+
@digest.digest_length,
|
157
|
+
@digest
|
158
|
+
)
|
159
|
+
end
|
160
|
+
|
161
|
+
def hmac(data, key)
|
162
|
+
OpenSSL::HMAC.digest(@digest,data,key)
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
def salted_password
|
167
|
+
@salted_password ||= hi(@user.password)
|
168
|
+
end
|
169
|
+
|
170
|
+
def stored_key(key)
|
171
|
+
h(key)
|
172
|
+
end
|
173
|
+
|
174
|
+
def without_proof
|
175
|
+
@without_proof ||= "c=biws,r=#{@server_nonce}"
|
176
|
+
end
|
177
|
+
|
178
|
+
def xor(first, second)
|
179
|
+
first.bytes.zip(second.bytes).map{ |(a,b)| (a ^ b).chr }.join('')
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module HaystackRuby
|
2
|
+
module Config
|
3
|
+
extend self
|
4
|
+
attr_accessor :projects
|
5
|
+
|
6
|
+
def load_configuration conf
|
7
|
+
@projects = {}
|
8
|
+
conf.each do |name, config|
|
9
|
+
p = Project.new(name, config)
|
10
|
+
@projects[name] = p if p.valid?
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# called in railtie
|
15
|
+
def load!(path, environment = nil)
|
16
|
+
require 'yaml'
|
17
|
+
environment ||= Rails.env
|
18
|
+
conf = YAML.load(File.new(path).read).with_indifferent_access[environment]
|
19
|
+
load_configuration(conf)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'haystack_ruby/types/bin'
|
2
|
+
require 'haystack_ruby/types/coord'
|
3
|
+
require 'haystack_ruby/types/date'
|
4
|
+
require 'haystack_ruby/types/date_time'
|
5
|
+
require 'haystack_ruby/types/marker'
|
6
|
+
require 'haystack_ruby/types/n_a'
|
7
|
+
require 'haystack_ruby/types/number'
|
8
|
+
require 'haystack_ruby/types/ref'
|
9
|
+
require 'haystack_ruby/types/str'
|
10
|
+
require 'haystack_ruby/types/time'
|
11
|
+
require 'haystack_ruby/types/uri'
|
12
|
+
|
13
|
+
module HaystackRuby
|
14
|
+
class Object
|
15
|
+
# Always present
|
16
|
+
attr_reader :value, :haystack_type
|
17
|
+
# May be present, depending on type
|
18
|
+
attr_reader :unit, :description
|
19
|
+
# initialize with a encoded json string
|
20
|
+
def initialize val
|
21
|
+
case val
|
22
|
+
when Array
|
23
|
+
@haystack_type = "Array" #may want to decode array components?
|
24
|
+
@value = val
|
25
|
+
when TrueClass
|
26
|
+
@haystack_type = "Boolean"
|
27
|
+
@value = val
|
28
|
+
when FalseClass
|
29
|
+
@haystack_type = "Boolean"
|
30
|
+
@value = val
|
31
|
+
when NilClass
|
32
|
+
@haystack_type = 'Null'
|
33
|
+
@value = val
|
34
|
+
when String
|
35
|
+
# Map to Haystack type per http://project-haystack.org/doc/Json
|
36
|
+
case val
|
37
|
+
when /\Am:.*/
|
38
|
+
include HaystackRuby::Types::Marker
|
39
|
+
when /\Az:.*/
|
40
|
+
include HaystackRuby::Types::NA
|
41
|
+
when /\An:.*/
|
42
|
+
extend HaystackRuby::Types::Number
|
43
|
+
when /\Ar:.*/
|
44
|
+
include HaystackRuby::Types::Ref
|
45
|
+
when /\As:.*/
|
46
|
+
include HaystackRuby::Types::Str
|
47
|
+
when /\Ad:.*/
|
48
|
+
include HaystackRuby::Types::Date
|
49
|
+
when /\Ah:.*/
|
50
|
+
include HaystackRuby::Types::Time
|
51
|
+
when /\At:.*/
|
52
|
+
include HaystackRuby::Types::DateTime
|
53
|
+
when /\Au:.*/
|
54
|
+
include HaystackRuby::Types::Uri
|
55
|
+
when /\Ab:.*/
|
56
|
+
include HaystackRuby::Types::Bin
|
57
|
+
when /\Ac:.*/
|
58
|
+
include HaystackRuby::Types::Coord
|
59
|
+
when /\Ax:.*/
|
60
|
+
raise HaystackRuby::Error, "parsing of XStr type is not supported for string val #{val}"
|
61
|
+
else
|
62
|
+
raise HaystackRuby::Error, "unrecognized type for string val #{val}"
|
63
|
+
end
|
64
|
+
else
|
65
|
+
raise HaystackRuby::Error, "unrecognized type for val #{val}"
|
66
|
+
end
|
67
|
+
set_fields val
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'date'
|
2
|
+
# require 'active_support'
|
3
|
+
module HaystackRuby
|
4
|
+
module Point
|
5
|
+
|
6
|
+
# is this Point valid for purposees of Project Haystack Integration?
|
7
|
+
def haystack_valid?
|
8
|
+
return self.haystack_project_name.present? && self.haystack_point_id.present? && self.haystack_time_zone.present?
|
9
|
+
end
|
10
|
+
|
11
|
+
def haystack_project
|
12
|
+
@project ||= HaystackRuby::Config.projects[self.haystack_project_name]
|
13
|
+
end
|
14
|
+
|
15
|
+
def connection
|
16
|
+
haystack_project.connection
|
17
|
+
end
|
18
|
+
|
19
|
+
def his_read(range)
|
20
|
+
query = ["ver:\"#{haystack_project.haystack_version}\"",'id,range',"@#{self.haystack_point_id},\"#{range}\""]
|
21
|
+
pp query.join "\n"
|
22
|
+
res = connection.post('hisRead') do |req|
|
23
|
+
req.headers['Content-Type'] = 'text/plain'
|
24
|
+
req.body = query.join("\n")
|
25
|
+
end
|
26
|
+
JSON.parse! res.body
|
27
|
+
end
|
28
|
+
|
29
|
+
def meta_data
|
30
|
+
# read request on project to load current info, including tags and timezone
|
31
|
+
res = haystack_project.read({:id => "@#{self.haystack_point_id}"})['rows'][0]
|
32
|
+
end
|
33
|
+
|
34
|
+
# data is ascending array of hashes with format: {time: epochtime, value: myvalue}
|
35
|
+
def his_write(data)
|
36
|
+
query =
|
37
|
+
["ver:\"#{haystack_project.haystack_version}\" id:@#{self.haystack_point_id}",'ts,val'] + data.map{ |d| "#{d[:time]},#{d[:value]}"}
|
38
|
+
|
39
|
+
res = connection.post('hisWrite') do |req|
|
40
|
+
req.headers['Content-Type'] = 'text/plain'
|
41
|
+
req.body = query.join("\n")
|
42
|
+
end
|
43
|
+
|
44
|
+
JSON.parse(res.body)['meta']['ok'].present?
|
45
|
+
end
|
46
|
+
|
47
|
+
def data(start, finish = nil, as_datetime = false) # as_datetime currently ignored
|
48
|
+
return unless haystack_valid? #may choose to throw exception instead
|
49
|
+
|
50
|
+
range = [start]
|
51
|
+
range << finish unless finish.nil?
|
52
|
+
# clean up the range argument before passing through to hisRead
|
53
|
+
# ----------------
|
54
|
+
r = HaystackRuby::Range.new(range, self.haystack_time_zone)
|
55
|
+
|
56
|
+
res = his_read r.to_s
|
57
|
+
reformat_timeseries(res['rows'], as_datetime)
|
58
|
+
end
|
59
|
+
|
60
|
+
def write_data(data)
|
61
|
+
# format data for his_write
|
62
|
+
data = data.map do |d|
|
63
|
+
{
|
64
|
+
time: HaystackRuby::Timestamp.convert_to_string(d[:time], self.haystack_time_zone),
|
65
|
+
value: d[:value]
|
66
|
+
}
|
67
|
+
end
|
68
|
+
his_write data
|
69
|
+
end
|
70
|
+
|
71
|
+
# map from
|
72
|
+
def reformat_timeseries data, as_datetime
|
73
|
+
data.map do |d|
|
74
|
+
time = (as_datetime) ? DateTime.parse(d['ts']) : DateTime.parse(d['ts']).to_i
|
75
|
+
val = HaystackRuby::Object.new(d['val'])
|
76
|
+
{:time => time, :value => val.value}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
module HaystackRuby
|
2
|
+
require 'json'
|
3
|
+
require 'pp'
|
4
|
+
# may consider making this a mixin instead
|
5
|
+
class Project
|
6
|
+
|
7
|
+
attr_accessor :name, :haystack_version, :url #required
|
8
|
+
def initialize(name, config)
|
9
|
+
@name = name
|
10
|
+
@url = (config['secure']) ? 'https://' : 'http://'
|
11
|
+
@url = "#{@url}#{config['base_url']}"
|
12
|
+
@haystack_version = config['haystack_version']
|
13
|
+
# expect to use basic auth
|
14
|
+
if config['credentials'].present?
|
15
|
+
@credentials = config['credentials']
|
16
|
+
#for now at least, we fake the user object
|
17
|
+
#expect to use scram
|
18
|
+
else
|
19
|
+
user = OpenStruct.new
|
20
|
+
user.username = config['username']
|
21
|
+
user.password = config['password']
|
22
|
+
# @creds_path = config['credentials_path']
|
23
|
+
# creds = YAML.load File.new(@creds_path).read
|
24
|
+
# user.username = creds['username']
|
25
|
+
# user.password = creds['password']
|
26
|
+
authorize user
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def authorize user
|
31
|
+
auth_conv = HaystackRuby::Auth::Scram::Conversation.new(user, @url)
|
32
|
+
auth_conv.authorize
|
33
|
+
@auth_token = auth_conv.auth_token
|
34
|
+
raise HaystackRuby::Error, "scram authorization failed" unless @auth_token.present?
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
# for now, setting up to have a single connection per project
|
39
|
+
def connection
|
40
|
+
# if @credentials.nil? && @auth_token.nil?
|
41
|
+
# authorize #will either set auth token or raise error
|
42
|
+
# end
|
43
|
+
@connection ||= Faraday.new(:url => @url) do |faraday|
|
44
|
+
faraday.request :url_encoded # form-encode POST params
|
45
|
+
faraday.response :logger # log requests to STDOUT
|
46
|
+
faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
|
47
|
+
faraday.headers['Authorization'] = @auth_token.present? ? "BEARER authToken=#{@auth_token}" : "Basic #@credentials"
|
48
|
+
faraday.headers['Accept'] = 'application/json' #TODO enable more formats
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def read(params)
|
53
|
+
body = ["ver:\"#{@haystack_version}\""]
|
54
|
+
body << params.keys.join(',')
|
55
|
+
body << params.values.join(',')
|
56
|
+
res = self.connection.post('read') do |req|
|
57
|
+
req.headers['Content-Type'] = 'text/plain'
|
58
|
+
req.body = body.join("\n")
|
59
|
+
end
|
60
|
+
JSON.parse! res.body
|
61
|
+
end
|
62
|
+
|
63
|
+
# return meta data for all equip with related points
|
64
|
+
def equip_point_meta
|
65
|
+
# begin
|
66
|
+
equips = read({filter: '"equip"'})['rows']
|
67
|
+
puts equips
|
68
|
+
equips.map! do |eq|
|
69
|
+
eq.delete('disMacro')
|
70
|
+
eq['description'] = eq['id'].match(/[(NWTC)|(\$siteRef)] (.*)/)[1]
|
71
|
+
eq['id'] = eq['id'].match(/:([a-z0-9\-]*)/)[1]
|
72
|
+
eq['points'] = []
|
73
|
+
read({filter: "\"point and equipRef==#{eq['id']}\""})['rows'].each do |p|
|
74
|
+
p.delete('analytics')
|
75
|
+
p.delete('disMacro')
|
76
|
+
p.delete('csvUnit')
|
77
|
+
p.delete('csvColumn')
|
78
|
+
p.delete('equipRef')
|
79
|
+
p.delete('point')
|
80
|
+
p.delete('siteRef')
|
81
|
+
|
82
|
+
p['id'] = p['id'].match(/:([a-z0-9\-]*)/)[1]
|
83
|
+
p['name'] = p['navName']
|
84
|
+
p.delete('navName')
|
85
|
+
eq['points'] << p
|
86
|
+
end
|
87
|
+
eq
|
88
|
+
end
|
89
|
+
# rescue Exception => e
|
90
|
+
puts "error: #{e}"
|
91
|
+
nil
|
92
|
+
# end
|
93
|
+
end
|
94
|
+
|
95
|
+
def ops
|
96
|
+
JSON.parse!(self.connection.get("ops").body)['rows']
|
97
|
+
end
|
98
|
+
|
99
|
+
def valid?
|
100
|
+
!(@name.nil? || @haystack_version.nil? || @url.nil?)
|
101
|
+
end
|
102
|
+
|
103
|
+
# http://www.skyfoundry.com/doc/docSkySpark/Ops#commit
|
104
|
+
# grid is array of strings
|
105
|
+
def commit grid
|
106
|
+
puts 'grid = '
|
107
|
+
pp grid.join "\n"
|
108
|
+
res = self.connection.post('commit') do |req|
|
109
|
+
req.headers['Content-Type'] = 'text/plain'
|
110
|
+
req.body = grid.join "\n"
|
111
|
+
end
|
112
|
+
JSON.parse! res.body
|
113
|
+
end
|
114
|
+
|
115
|
+
# params is array of hashes: {name: xx, type: xx, value: xx}
|
116
|
+
def add_rec params
|
117
|
+
grid = ["ver:\"#{@haystack_version}\" commit:\"add\""]
|
118
|
+
grid << params.map{|p| p[:name]}.join(',')
|
119
|
+
values = params.map do |p|
|
120
|
+
p[:value] = "\"#{p[:value]}\"" if p[:type] == 'String'
|
121
|
+
p[:value]
|
122
|
+
end
|
123
|
+
grid << values.join(',')
|
124
|
+
res = commit grid
|
125
|
+
# return id of new rec
|
126
|
+
res['rows'][0]['id']
|
127
|
+
end
|
128
|
+
|
129
|
+
# TODO fix these. weird sensitivities around mod timestamp (format and time)
|
130
|
+
# params is array of hashes: {name: xx, type: xx, value: xx}
|
131
|
+
# def update_rec id,params
|
132
|
+
# grid = ["ver:\"#{@haystack_version}\" commit:\"update\""]
|
133
|
+
# grid << 'id,mod,' + params.map{|p| p[:name]}.join(',')
|
134
|
+
# values = params.map do |p|
|
135
|
+
# p[:value] = "\"#{p[:value]}\"" if p[:type] == 'String'
|
136
|
+
# p[:value]
|
137
|
+
# end
|
138
|
+
# grid << "#{id},2017-01-09T17:21:31.197Z UTC,#{values.join(',')}"
|
139
|
+
# puts "dumping grid #{grid}"
|
140
|
+
#
|
141
|
+
# commit grid
|
142
|
+
# end
|
143
|
+
|
144
|
+
def remove_rec id
|
145
|
+
grid = ["ver:\"#{@haystack_version}\" commit:\"remove\""]
|
146
|
+
grid << 'id,mod'
|
147
|
+
grid << "#{id},#{DateTime.now}"
|
148
|
+
commit grid
|
149
|
+
end
|
150
|
+
private
|
151
|
+
|
152
|
+
# def persist_creds
|
153
|
+
# creds = {username: @username, password: @password, auth_token: @auth_token}
|
154
|
+
# File.open(@creds_path,'w') do |h|
|
155
|
+
# h.write creds.to_yaml
|
156
|
+
# end
|
157
|
+
# end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rails'
|
2
|
+
module Rails
|
3
|
+
module HaystackRuby
|
4
|
+
class Railtie < Rails::Railtie
|
5
|
+
initializer "haystack_ruby.load-config" do
|
6
|
+
config_file = Rails.root.join("config", "haystack_ruby.yml")
|
7
|
+
if config_file.file?
|
8
|
+
begin
|
9
|
+
::HaystackRuby::Config.load!(config_file)
|
10
|
+
rescue Exception => e
|
11
|
+
# TODO better error handling
|
12
|
+
puts "Error loading config for haystack_ruby: #{e}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# from haystack docs:
|
2
|
+
#
|
3
|
+
# Ranges are exclusive of start timestamp and inclusive of end timestamp. The {date} and {dateTime} options must be correctly Zinc encoded. DateTime based ranges must be in the same timezone of the entity (timezone conversion is explicitly disallowed). Date based ranges are always inferred to be from midnight of starting date to midnight of the day after ending date using the timezone of the his entity being queried.
|
4
|
+
# ----------------
|
5
|
+
|
6
|
+
|
7
|
+
module HaystackRuby
|
8
|
+
class Range
|
9
|
+
def initialize(range,time_zone)
|
10
|
+
@haystack_time_zone = time_zone
|
11
|
+
@finish = nil
|
12
|
+
if range.kind_of? Array
|
13
|
+
raise HaystackRuby::Error, 'Too many values for range' if range.count > 2
|
14
|
+
@start = Timestamp.convert_to_string(range.first, time_zone)
|
15
|
+
if range.count > 1
|
16
|
+
@finish = Timestamp.convert_to_string(range.last, time_zone)
|
17
|
+
raise HaystackRuby::Error, 'Start must be before End' if DateTime.parse(@start) >= DateTime.parse(@finish)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
@start = Timestamp.convert_to_string(range, time_zone)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
if @finish
|
26
|
+
"#{@start},#{@finish}"
|
27
|
+
elsif @start
|
28
|
+
"#{@start}"
|
29
|
+
else
|
30
|
+
''
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module HaystackRuby
|
2
|
+
class Timestamp
|
3
|
+
# convert ts to a Project Haystack compliant string.
|
4
|
+
# ts is a timestamp, tz is a Haystack timezone string
|
5
|
+
# for now assumes application timezone is set correctly
|
6
|
+
# TODO add a mapping between Ruby and Haystack timezone strings and use here
|
7
|
+
def self.convert_to_string ts, tz
|
8
|
+
if ts.kind_of? DateTime
|
9
|
+
t = ts.to_s
|
10
|
+
elsif ts.kind_of? Integer
|
11
|
+
t = Time.at(ts).to_datetime.to_s
|
12
|
+
elsif ts.kind_of? Date
|
13
|
+
t = ts.to_datetime.to_s
|
14
|
+
else
|
15
|
+
raise HaystackRuby::Error, "param must be one of Date, DateTime, Integer"
|
16
|
+
end
|
17
|
+
"#{t} #{tz}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module HaystackRuby
|
2
|
+
module Types
|
3
|
+
module Bin
|
4
|
+
def set_fields str_value
|
5
|
+
@haystack_type = 'Bin'
|
6
|
+
match = /\Ab:(.*)\z/.match str_value
|
7
|
+
raise HaystackRuby::Error, "invalid HaystackRuby::Types::Bin: #{str_value}" if match.nil?
|
8
|
+
# Value is mime type
|
9
|
+
@value = match[1]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module HaystackRuby
|
2
|
+
module Types
|
3
|
+
module Coord
|
4
|
+
def set_fields str_value
|
5
|
+
@haystack_type = 'Coord'
|
6
|
+
match = /\Ac:([0-9.-]*),([0-9.-]*)\z/.match str_value
|
7
|
+
begin
|
8
|
+
@value = [match[1].to_f, match[2].to_f] #value is array of [lat, lng]
|
9
|
+
rescue Exception=>e
|
10
|
+
raise HaystackRuby::Error, "invalid HaystackRuby::Types::Coord #{str_value}. Error #{e}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module HaystackRuby
|
2
|
+
module Types
|
3
|
+
module Date
|
4
|
+
def set_fields str_value
|
5
|
+
@haystack_type = 'Date'
|
6
|
+
match = /\Ad:([-0-9]*)\z/.match str_value
|
7
|
+
begin
|
8
|
+
@value = Date.parse match[1]
|
9
|
+
rescue Exception=>e
|
10
|
+
raise HaystackRuby::Error, "invalid HaystackRuby::Types::Date #{str_value}. Error #{e}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module HaystackRuby
|
2
|
+
module Types
|
3
|
+
module DateTime
|
4
|
+
def set_fields str_value
|
5
|
+
@haystack_type = 'DateTime'
|
6
|
+
match = /\At:(.*)\z/.match str_value
|
7
|
+
begin
|
8
|
+
@value = DateTime.parse match[1]
|
9
|
+
rescue Exception=>e
|
10
|
+
raise HaystackRuby::Error, "invalid HaystackRuby::Types::DateTime #{str_value}. Error #{e}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module HaystackRuby
|
2
|
+
module Types
|
3
|
+
module Number
|
4
|
+
def set_fields str_value
|
5
|
+
@haystack_type = 'Number'
|
6
|
+
match = /\An:([-0-9\.]*)( .*)*\z/.match str_value
|
7
|
+
raise HaystackRuby::Error, "invalid HaystackRuby::Types::Number: #{str_value}" if match.nil?
|
8
|
+
@value = match[1].to_f
|
9
|
+
# also set unit if available
|
10
|
+
@unit = match[2].strip if match[2].present?
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module HaystackRuby
|
2
|
+
module Types
|
3
|
+
module Ref
|
4
|
+
def set_fields str_value
|
5
|
+
@haystack_type = 'Ref'
|
6
|
+
match = /\Ar:([^ ]*) (.*)\z/.match str_value
|
7
|
+
raise HaystackRuby::Error, HaystackRuby::Error, "invalid HaystackRuby::Types::Ref: #{str_value}" if match.nil?
|
8
|
+
@value = match[1] #ref id
|
9
|
+
@description = match[2]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module HaystackRuby
|
2
|
+
module Types
|
3
|
+
# Since there is no Ruby Hour class, value is a string "hr:minute"
|
4
|
+
module Time
|
5
|
+
def set_fields str_value
|
6
|
+
@haystack_type = 'Time'
|
7
|
+
match = /\Ah:([0-9:]*)\z/.match str_value
|
8
|
+
begin
|
9
|
+
@value = match[1]
|
10
|
+
rescue Exception=>e
|
11
|
+
raise HaystackRuby::Error, "invalid HaystackRuby::Types::Time #{str_value}. Error #{e}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module HaystackRuby
|
2
|
+
module Types
|
3
|
+
module Uri
|
4
|
+
def set_fields str_value
|
5
|
+
@haystack_type = 'Uri'
|
6
|
+
match = /\Au:(.*)\z/.match str_value
|
7
|
+
raise HaystackRuby::Error, "invalid HaystackRuby::Types::Uri: #{str_value}" if match.nil?
|
8
|
+
@value = match[1]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'haystack_ruby/auth/conversation'
|
3
|
+
require 'haystack_ruby/config'
|
4
|
+
require 'haystack_ruby/error'
|
5
|
+
require 'haystack_ruby/object'
|
6
|
+
require 'haystack_ruby/point'
|
7
|
+
require 'haystack_ruby/project'
|
8
|
+
require 'haystack_ruby/range'
|
9
|
+
require 'haystack_ruby/timestamp'
|
10
|
+
|
11
|
+
require 'active_support/core_ext/hash'
|
12
|
+
if defined?(Rails)
|
13
|
+
require "haystack_ruby/railtie"
|
14
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HaystackRuby::Auth::Scram::Conversation do
|
4
|
+
describe 'connection' do
|
5
|
+
|
6
|
+
# it 'receives authorization challenge response to hello' do
|
7
|
+
# expect(conv.hello.status.should == 401 )
|
8
|
+
# end
|
9
|
+
before :each do
|
10
|
+
user = OpenStruct.new
|
11
|
+
user.username = CONF[PROJECT]['username']
|
12
|
+
user.password = CONF[PROJECT]['password']
|
13
|
+
url = HaystackRuby::Config.projects[PROJECT].url
|
14
|
+
@conv = HaystackRuby::Auth::Scram::Conversation.new(user, url)
|
15
|
+
end
|
16
|
+
it 'receives auth challenge response to first message' do
|
17
|
+
expect(@conv.send_first_message.status).to be == 401
|
18
|
+
end
|
19
|
+
it 'sends valid rnonce in continue message' do
|
20
|
+
res = @conv.send_first_message
|
21
|
+
@conv.parse_first_response(res)
|
22
|
+
puts "nonce #{@conv.nonce}, servernonce #{@conv.server_nonce}"
|
23
|
+
expect(@conv.nonce.length).to be > 0
|
24
|
+
expect(@conv.server_nonce.index(@conv.nonce)).to be == 0
|
25
|
+
end
|
26
|
+
it 'server responds to second client message with authorization' do
|
27
|
+
res = @conv.send_first_message
|
28
|
+
@conv.parse_first_response(res)
|
29
|
+
expect(@conv.send_second_message.status).to be == 200
|
30
|
+
end
|
31
|
+
it 'should have nonempty auth token after full authorization' do
|
32
|
+
@conv.authorize
|
33
|
+
expect(@conv.auth_token).not_to be_empty
|
34
|
+
end
|
35
|
+
it 'should load page using auth token' do
|
36
|
+
@conv.authorize
|
37
|
+
expect(@conv.test_auth_token.status).to be == 200
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
describe HaystackRuby::Config do
|
3
|
+
describe '.projects' do
|
4
|
+
context 'good config file' do
|
5
|
+
before do
|
6
|
+
@projects = HaystackRuby::Config.projects
|
7
|
+
@demo = @projects[PROJECT]
|
8
|
+
end
|
9
|
+
it 'returns a project' do
|
10
|
+
expect(@demo).to be_a_kind_of HaystackRuby::Project
|
11
|
+
end
|
12
|
+
end
|
13
|
+
context 'bad project in config file' do
|
14
|
+
end
|
15
|
+
context 'missing config file' do
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'models/point_demo'
|
3
|
+
describe HaystackRuby::Point do
|
4
|
+
before do
|
5
|
+
@point = PointDemo.new(PT_PROJ,PT_ID,PT_TZ)
|
6
|
+
end
|
7
|
+
describe '#haystack_project' do
|
8
|
+
context 'real project name' do
|
9
|
+
it 'responds to method' do
|
10
|
+
expect(@point).to respond_to :haystack_project
|
11
|
+
end
|
12
|
+
it 'returns a project' do
|
13
|
+
expect(@point.haystack_project).to be_a_kind_of HaystackRuby::Project
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
describe '#his_read' do
|
18
|
+
context 'valid id and range' do
|
19
|
+
before do
|
20
|
+
@res = @point.his_read('yesterday')
|
21
|
+
end
|
22
|
+
it 'returns 2 columns' do
|
23
|
+
expect(@res['cols'].count).to eq 2
|
24
|
+
end
|
25
|
+
it 'returns array of rows' do
|
26
|
+
expect(@res['rows']).to be_a_kind_of Array
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
describe '#his_write' do
|
31
|
+
context 'valid data' do
|
32
|
+
before do
|
33
|
+
data = [{time: "#{DateTime.now.to_s} #{@point.haystack_time_zone}", value: rand(-2000..2000).to_f / 10}, {time: "#{(DateTime.now - 1.day).to_s} #{@point.haystack_time_zone}", value: rand(-2000..2000).to_f / 10}]
|
34
|
+
@res = @point.his_write(data)
|
35
|
+
end
|
36
|
+
it 'returns true' do
|
37
|
+
expect(@res).to eq true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
describe '#write_data' do
|
42
|
+
context 'valid data' do
|
43
|
+
before do
|
44
|
+
data = [{time: DateTime.now, value: -1}]
|
45
|
+
@res = @point.write_data(data)
|
46
|
+
end
|
47
|
+
it 'returns true' do
|
48
|
+
expect(@res).to eq true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '#metadata' do
|
54
|
+
context 'valid id' do
|
55
|
+
it 'returns metadata for the point' do
|
56
|
+
|
57
|
+
expect(@point.meta_data).to be_a_kind_of Hash
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#data' do
|
63
|
+
context 'valid id and range' do
|
64
|
+
before do
|
65
|
+
@data = @point.data(Date.today.prev_day)
|
66
|
+
@d = @data.first
|
67
|
+
end
|
68
|
+
it 'returns data with expected format' do
|
69
|
+
expect(@d[:time]).to_not be_nil
|
70
|
+
expect(@d[:value]).to_not be_nil
|
71
|
+
end
|
72
|
+
it 'returns time as epoch' do
|
73
|
+
expect(@d[:time]).to be_a_kind_of Integer
|
74
|
+
end
|
75
|
+
end
|
76
|
+
context 'as_datetime true' do
|
77
|
+
before do
|
78
|
+
@d = @point.data(Date.today.prev_day, nil, true).first
|
79
|
+
end
|
80
|
+
it 'returns time in DateTime format' do
|
81
|
+
expect(@d[:time]).to be_kind_of DateTime
|
82
|
+
end
|
83
|
+
end
|
84
|
+
context 'various range formats' do
|
85
|
+
context 'Dates' do
|
86
|
+
it 'returns data when two ascending Date values' do
|
87
|
+
data = @point.data(Date.today.prev_day, Date.today)
|
88
|
+
expect(data.count).to be > 0
|
89
|
+
end
|
90
|
+
it 'throws error start is after finish' do
|
91
|
+
expect {
|
92
|
+
data = @point.data(Date.today, Date.today.prev_day)
|
93
|
+
}.to raise_error HaystackRuby::Error
|
94
|
+
end
|
95
|
+
it 'returns data when no finish' do
|
96
|
+
data = @point.data(Date.today.prev_day)
|
97
|
+
expect(data.count).to be > 0
|
98
|
+
end
|
99
|
+
end
|
100
|
+
context 'Integers' do
|
101
|
+
it 'returns data when two ascending Integer values' do
|
102
|
+
data = @point.data(Date.today.prev_day.to_time.to_i, Date.today.to_time.to_i)
|
103
|
+
expect(data.count).to be > 0
|
104
|
+
end
|
105
|
+
it 'throws error start is after finish' do
|
106
|
+
expect {
|
107
|
+
data = @point.data(Date.today.to_time.to_i, Date.today.prev_day.to_time.to_i)
|
108
|
+
}.to raise_error HaystackRuby::Error
|
109
|
+
end
|
110
|
+
it 'returns data when no finish' do
|
111
|
+
data = @point.data(Date.today.prev_day.to_time.to_i)
|
112
|
+
expect(data.count).to be > 0
|
113
|
+
end
|
114
|
+
end
|
115
|
+
context 'DateTimes' do
|
116
|
+
it 'returns data when two ascending DateTime values' do
|
117
|
+
data = @point.data(Date.today.prev_day.to_datetime, Date.today.to_datetime)
|
118
|
+
expect(data.count).to be > 0
|
119
|
+
end
|
120
|
+
it 'throws error start is after finish' do
|
121
|
+
expect {
|
122
|
+
@point.data(Date.today.to_datetime, Date.today.prev_day.to_datetime)
|
123
|
+
}.to raise_error HaystackRuby::Error
|
124
|
+
|
125
|
+
end
|
126
|
+
it 'returns data when one value' do
|
127
|
+
data = @point.data(Date.today.prev_day.to_datetime)
|
128
|
+
expect(data.count).to be > 0
|
129
|
+
end
|
130
|
+
end
|
131
|
+
it 'returns data when range is Date' do
|
132
|
+
data = @point.data(Date.today.prev_day)
|
133
|
+
expect(data.count).to be > 0
|
134
|
+
end
|
135
|
+
it 'does not accept string values for range' do
|
136
|
+
expect { data = @point.data(Date.today.prev_day.to_s) }.to raise_error HaystackRuby::Error
|
137
|
+
end
|
138
|
+
it 'returns data when range is start DateTime' do
|
139
|
+
data = @point.data(Date.today.prev_day.to_datetime)
|
140
|
+
expect(data.count).to be > 0
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
describe HaystackRuby::Project do
|
3
|
+
before do
|
4
|
+
@demo = HaystackRuby::Config.projects[PT_PROJ]
|
5
|
+
end
|
6
|
+
describe '#connection' do
|
7
|
+
context 'defined project name' do
|
8
|
+
it 'returns valid Faraday connection' do
|
9
|
+
expect(@demo.connection).to be_a_kind_of Faraday::Connection
|
10
|
+
end
|
11
|
+
it 'sets Authorization header' do
|
12
|
+
expect(@demo.connection.headers['Authorization']).to_not be_nil
|
13
|
+
end
|
14
|
+
it 'accepts json' do
|
15
|
+
expect(@demo.connection.headers['Accept']).to eq 'application/json'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
context 'undefined project name' do
|
19
|
+
end
|
20
|
+
context 'missing config file' do
|
21
|
+
end
|
22
|
+
end
|
23
|
+
describe '#read' do
|
24
|
+
context 'simple filter' do
|
25
|
+
it 'returns cols' do
|
26
|
+
expect(@demo.read({:filter => '"site"'})['cols']).to be_a_kind_of Array
|
27
|
+
end
|
28
|
+
it 'returns rows' do
|
29
|
+
expect(@demo.read({:filter => '"site"'})['rows']).to be_a_kind_of Array
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
describe '#add_rec' do
|
34
|
+
context 'point' do
|
35
|
+
it 'returns id' do
|
36
|
+
params = [{name: 'sp',type: 'Marker',value: 'M'},{name: 'dis',type: 'String',value: 'Test'},{name: 'point', type: 'Marker', value: 'M'},{name: 'equipRef',type: 'Ref',value: '@1d56759c-8f9214b6'},{name: 'siteRef', type: 'Ref', value: '@1d56758c-c15f5708'}]
|
37
|
+
res = @demo.add_rec(params )
|
38
|
+
expect(res).to be_a_kind_of String
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class PointDemo
|
2
|
+
# these fields are required on the class point is mixed into
|
3
|
+
attr_accessor :haystack_project_name, :haystack_point_id, :haystack_time_zone
|
4
|
+
include HaystackRuby::Point
|
5
|
+
def initialize(proj_name, point_id, tz)
|
6
|
+
@haystack_project_name = proj_name
|
7
|
+
@haystack_point_id = point_id
|
8
|
+
@haystack_time_zone = tz
|
9
|
+
end
|
10
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'yaml'
|
3
|
+
Bundler.setup
|
4
|
+
require 'haystack_ruby' # and any other gems you need
|
5
|
+
|
6
|
+
# configuration constants used for testing
|
7
|
+
CONFIG_PATH = 'config/example.yml' #path to the settings file, relative to directory root
|
8
|
+
CONFIG_ENV = 'test' #environment to load from settings file for testing
|
9
|
+
PROJECT = 'demov3' #name of project to use for testing. Must match name in the settings file.
|
10
|
+
PT_ID = '1d5675a8-867de4b8' #Haystack ID of an existing point that is safe for to use for testing. NOTE that this point will be written to.
|
11
|
+
PT_PROJ = PROJECT # Optionally use a different project name for the point tests
|
12
|
+
PT_TZ = 'Denver' # Timezone of your test point
|
13
|
+
|
14
|
+
|
15
|
+
Time.zone='Mountain Time (US & Canada)'
|
16
|
+
RSpec.configure do |config|
|
17
|
+
config.before(:suite) do
|
18
|
+
CONF = YAML.load(File.new(CONFIG_PATH).read).with_indifferent_access[CONFIG_ENV]
|
19
|
+
HaystackRuby::Config.load_configuration CONF
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: haystack_ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Anya Petersen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-03-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faraday
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: openssl
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Ruby adapter for Project Haystack REST API
|
70
|
+
email: anya.petersen@nrel.gov
|
71
|
+
executables: []
|
72
|
+
extensions: []
|
73
|
+
extra_rdoc_files: []
|
74
|
+
files:
|
75
|
+
- lib/haystack_ruby.rb
|
76
|
+
- lib/haystack_ruby/auth/conversation.rb
|
77
|
+
- lib/haystack_ruby/config.rb
|
78
|
+
- lib/haystack_ruby/error.rb
|
79
|
+
- lib/haystack_ruby/object.rb
|
80
|
+
- lib/haystack_ruby/point.rb
|
81
|
+
- lib/haystack_ruby/project.rb
|
82
|
+
- lib/haystack_ruby/railtie.rb
|
83
|
+
- lib/haystack_ruby/range.rb
|
84
|
+
- lib/haystack_ruby/timestamp.rb
|
85
|
+
- lib/haystack_ruby/types/bin.rb
|
86
|
+
- lib/haystack_ruby/types/coord.rb
|
87
|
+
- lib/haystack_ruby/types/date.rb
|
88
|
+
- lib/haystack_ruby/types/date_time.rb
|
89
|
+
- lib/haystack_ruby/types/marker.rb
|
90
|
+
- lib/haystack_ruby/types/n_a.rb
|
91
|
+
- lib/haystack_ruby/types/number.rb
|
92
|
+
- lib/haystack_ruby/types/ref.rb
|
93
|
+
- lib/haystack_ruby/types/str.rb
|
94
|
+
- lib/haystack_ruby/types/time.rb
|
95
|
+
- lib/haystack_ruby/types/uri.rb
|
96
|
+
- spec/haystack_ruby/auth/scram_spec.rb
|
97
|
+
- spec/haystack_ruby/config_spec.rb
|
98
|
+
- spec/haystack_ruby/point_spec.rb
|
99
|
+
- spec/haystack_ruby/project_spec.rb
|
100
|
+
- spec/models/point_demo.rb
|
101
|
+
- spec/spec_helper.rb
|
102
|
+
homepage: https://github.com/NREL/haystack_ruby
|
103
|
+
licenses:
|
104
|
+
- MIT
|
105
|
+
metadata: {}
|
106
|
+
post_install_message:
|
107
|
+
rdoc_options: []
|
108
|
+
require_paths:
|
109
|
+
- lib
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
requirements: []
|
121
|
+
rubyforge_project:
|
122
|
+
rubygems_version: 2.6.11
|
123
|
+
signing_key:
|
124
|
+
specification_version: 4
|
125
|
+
summary: Project Haystack Ruby Adapter
|
126
|
+
test_files:
|
127
|
+
- spec/haystack_ruby/auth/scram_spec.rb
|
128
|
+
- spec/haystack_ruby/config_spec.rb
|
129
|
+
- spec/haystack_ruby/point_spec.rb
|
130
|
+
- spec/haystack_ruby/project_spec.rb
|
131
|
+
- spec/models/point_demo.rb
|
132
|
+
- spec/spec_helper.rb
|