MrMurano 1.8.1 → 1.9.0
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.
- checksums.yaml +4 -4
- data/Gemfile +4 -0
- data/README.markdown +9 -0
- data/Rakefile +12 -3
- data/TODO.taskpaper +6 -1
- data/bin/mr +2 -0
- data/lib/MrMurano/Config.rb +1 -0
- data/lib/MrMurano/Product-1P-Device.rb +129 -0
- data/lib/MrMurano/Product-Resources.rb +1 -29
- data/lib/MrMurano/Product.rb +46 -0
- data/lib/MrMurano/Solution-Endpoint.rb +2 -1
- data/lib/MrMurano/commands/config.rb +2 -2
- data/lib/MrMurano/commands/content.rb +1 -1
- data/lib/MrMurano/commands/domain.rb +1 -1
- data/lib/MrMurano/commands/exportImport.rb +89 -2
- data/lib/MrMurano/commands/init.rb +117 -0
- data/lib/MrMurano/commands/productDevice.rb +67 -0
- data/lib/MrMurano/commands/productWrite.rb +3 -3
- data/lib/MrMurano/commands/tsdb.rb +19 -1
- data/lib/MrMurano/commands.rb +2 -0
- data/lib/MrMurano/dir.rb +47 -0
- data/lib/MrMurano/version.rb +1 -1
- data/lib/MrMurano.rb +1 -0
- data/spec/ProductResources_spec.rb +0 -60
- data/spec/Product_1P_Device_spec.rb +136 -0
- data/spec/Product_1P_RPC_spec.rb +173 -0
- metadata +10 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c1ab13a9e2ec31727024cb8776e466d1764e592f
|
|
4
|
+
data.tar.gz: 7a1d7d1f1b6d135b89af5053d0277fa2917c62ea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8b51b475707e85b63490e8f547674651cd47666ddb1cade28ade86f3b08724dba7a6df65e850975f9a630161cb1bc0b7b3277ea8c2262a8f5f8315b47fb03e1b
|
|
7
|
+
data.tar.gz: 0c61906ec034c36d5551ac790042e4b3df6127c4f8075e5f9f70167f85eb11f6c6925e89d19497ca2d2186fd1f38d41ab19728b6dd16587efa083cc01bfb3cbe
|
data/Gemfile
CHANGED
data/README.markdown
CHANGED
|
@@ -212,3 +212,12 @@ MrMurano uses git flow for managing branches.
|
|
|
212
212
|
|
|
213
213
|
MrMurano also uses [bunder](http://bundler.io).
|
|
214
214
|
|
|
215
|
+
## Windows
|
|
216
|
+
|
|
217
|
+
The MrMurano gem will install on Windows.
|
|
218
|
+
|
|
219
|
+
You can install Ruby on Windows with [RubyInstaller](http://rubyinstaller.org).
|
|
220
|
+
You might run into a [known SSL cert issue](http://guides.rubygems.org/ssl-certificate-update/).
|
|
221
|
+
If so follow the steps there to update the certs.
|
|
222
|
+
|
|
223
|
+
|
data/Rakefile
CHANGED
|
@@ -25,9 +25,18 @@ task :gitpush do
|
|
|
25
25
|
sh %{git push --tags}
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
task :gempush do
|
|
29
|
+
sh %{gem push pkg/MrMurano-#{Bundler::GemHelper.gemspec.version}.gem}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
task :gemit do
|
|
33
|
+
mrt=Bundler::GemHelper.gemspec.version
|
|
34
|
+
sh %{git checkout v#{mrt}}
|
|
35
|
+
Rake::Task[:build].invoke
|
|
36
|
+
Rake::Task[:bob].invoke
|
|
37
|
+
Rake::Task[:gempush].invoke
|
|
38
|
+
sh %{git checkout develop}
|
|
39
|
+
end
|
|
31
40
|
|
|
32
41
|
desc "Prints a cmd to test this in another directory"
|
|
33
42
|
task :testwith do
|
data/TODO.taskpaper
CHANGED
|
@@ -3,7 +3,7 @@ Readme:
|
|
|
3
3
|
- Look into using VCR for testing. @pri(low)
|
|
4
4
|
|
|
5
5
|
Commands:
|
|
6
|
-
- Init command.
|
|
6
|
+
- Init command. @done(2016-11-28)
|
|
7
7
|
- Empty sub-commands should return help. @done(2016-11-21)
|
|
8
8
|
There are a bunch of empty sub-commands that prefix another layer. Such as
|
|
9
9
|
assign, content, product, and others. Those should be impemented as a ‘help’
|
|
@@ -23,6 +23,7 @@ Account:
|
|
|
23
23
|
- Netrc library (or the netrc format) doesn't allow '#' in passwords. @done(2016-08-10)
|
|
24
24
|
|
|
25
25
|
Endpoints:
|
|
26
|
+
- In fetch(); add content_type to script header if not application/json
|
|
26
27
|
- Add support for multiple endpoints in one file @pri(high) @done(2016-11-18)
|
|
27
28
|
- Add directory support like in modules @done(2016-07-26)
|
|
28
29
|
|
|
@@ -54,6 +55,7 @@ Timeseries:
|
|
|
54
55
|
- Add CSV output option. @done(2016-09-09)
|
|
55
56
|
|
|
56
57
|
Product:
|
|
58
|
+
- Add option for progress bar when uploading content files.
|
|
57
59
|
- Support multiple products.
|
|
58
60
|
Think about how this would work. There is the syncing of the resoruces, and then
|
|
59
61
|
some of the commands that use product.id.
|
|
@@ -68,6 +70,9 @@ Service Device:
|
|
|
68
70
|
Config:
|
|
69
71
|
- Add config tool.default_sync to set which things sync{up,down} by default
|
|
70
72
|
It is internally hardcoded to be -s, -a, -m, -e right now.
|
|
73
|
+
- Store passwords in system Keychain on system that have a Keychain.
|
|
74
|
+
mac OS does, various Linux desktops have a couple differnet ones. Not sure about
|
|
75
|
+
Windows.
|
|
71
76
|
- Add ENV['MR_CONFIGFILE'] path to file to load like --configfile @done(2016-09-22)
|
|
72
77
|
- Maybe add dotenv support. @done(2016-09-22)
|
|
73
78
|
- Think about adding dev,staging,prod system; how would that work? @done(2016-09-16)
|
data/bin/mr
CHANGED
data/lib/MrMurano/Config.rb
CHANGED
|
@@ -74,6 +74,7 @@ module MrMurano
|
|
|
74
74
|
set('tool.verbose', false, :defaults)
|
|
75
75
|
set('tool.debug', false, :defaults)
|
|
76
76
|
set('tool.dry', false, :defaults)
|
|
77
|
+
set('tool.fullerror', false, :defaults)
|
|
77
78
|
set('tool.outformat', 'best', :defaults)
|
|
78
79
|
|
|
79
80
|
set('net.host', 'bizapi.hosted.exosite.io', :defaults)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
require 'MrMurano/Product'
|
|
2
|
+
|
|
3
|
+
module MrMurano
|
|
4
|
+
class Product1PDevice < ProductBase
|
|
5
|
+
include ProductOnePlatformRpcShim
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
super
|
|
9
|
+
@uriparts << :proxy
|
|
10
|
+
@uriparts << 'onep:v1'
|
|
11
|
+
@uriparts << :rpc
|
|
12
|
+
@uriparts << :process
|
|
13
|
+
@model_rid = nil
|
|
14
|
+
@sn_rid = nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
## Get the internal protocol identifier for a device
|
|
18
|
+
# +sn+:: Identifier for a device
|
|
19
|
+
def sn_rid(sn)
|
|
20
|
+
return @sn_rid unless @sn_rid.nil?
|
|
21
|
+
prd = Product.new()
|
|
22
|
+
found = []
|
|
23
|
+
|
|
24
|
+
offset = 0
|
|
25
|
+
loop do
|
|
26
|
+
listing = prd.list(offset)
|
|
27
|
+
break if listing.empty?
|
|
28
|
+
found = listing.select{|item| item[:sn] == sn}
|
|
29
|
+
break unless found.empty?
|
|
30
|
+
|
|
31
|
+
offset += 50
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
@sn_rid = found.first[:rid]
|
|
35
|
+
@sn_rid
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
## Get information about a device
|
|
39
|
+
# +sn+:: Identifier for a device
|
|
40
|
+
def info(sn)
|
|
41
|
+
do_rpc({:id=>1,
|
|
42
|
+
:procedure=>:info,
|
|
43
|
+
:arguments=>[sn_rid(sn), {}]
|
|
44
|
+
}, sn_rid(sn))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
## List resources on a device
|
|
48
|
+
# +sn+:: Identifier for a device
|
|
49
|
+
def list(sn)
|
|
50
|
+
data = info(sn)
|
|
51
|
+
dt = {}
|
|
52
|
+
data[:aliases].each{|k,v| v.each{|a| dt[a] = k.to_s}}
|
|
53
|
+
dt
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def listing(sn)
|
|
57
|
+
do_rpc({:id=>1,
|
|
58
|
+
:procedure=>:listing,
|
|
59
|
+
:arguments=>[sn_rid(sn), [:dataport], {}]
|
|
60
|
+
}, sn_rid(sn))
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
## Read the last value for resources on a device
|
|
64
|
+
# +sn+:: Identifier for a device
|
|
65
|
+
# +aliases+:: Array of resource names
|
|
66
|
+
def read(sn, aliases)
|
|
67
|
+
aliases = [aliases] unless aliases.kind_of? Array
|
|
68
|
+
calls = aliases.map do |a|
|
|
69
|
+
{
|
|
70
|
+
:procedure=>:read,
|
|
71
|
+
:arguments=>[ {:alias=>a}, {} ]
|
|
72
|
+
}
|
|
73
|
+
end
|
|
74
|
+
do_mrpc(calls, sn_rid(sn)).map do |i|
|
|
75
|
+
if i.has_key?(:result) and i[:result].count > 0 and i[:result][0].count > 1 then
|
|
76
|
+
i[:result][0][1]
|
|
77
|
+
else
|
|
78
|
+
nil
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
## Get a tree of info for a device and its resources.
|
|
84
|
+
# +sn+:: Identifier for a device
|
|
85
|
+
def twee(sn)
|
|
86
|
+
inf = info(sn)
|
|
87
|
+
|
|
88
|
+
aliases = inf[:aliases].keys
|
|
89
|
+
# information for all
|
|
90
|
+
info_calls = aliases.map do |rid|
|
|
91
|
+
{:procedure=>:info, :arguments=>[rid, {}]}
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
limitkeys = [:basic, :description, :usage, :children, :storage]
|
|
95
|
+
|
|
96
|
+
isubs = do_mrpc(info_calls, sn_rid(sn))
|
|
97
|
+
children = isubs.map{|i| i[:result].select{|k,v| limitkeys.include? k} }
|
|
98
|
+
|
|
99
|
+
# most current value
|
|
100
|
+
read_calls = aliases.map do |rid|
|
|
101
|
+
{:procedure=>:read, :arguments=>[rid, {}]}
|
|
102
|
+
end
|
|
103
|
+
ivalues = do_mrpc(read_calls, sn_rid(sn))
|
|
104
|
+
|
|
105
|
+
rez = aliases.zip(children, ivalues).map do |d|
|
|
106
|
+
dinf = d[1]
|
|
107
|
+
dinf[:rid] = d[0]
|
|
108
|
+
dinf[:alias] = inf[:aliases][d[0]].first
|
|
109
|
+
|
|
110
|
+
iv = d[2]
|
|
111
|
+
if iv.has_key?(:result) and iv[:result].count > 0 and iv[:result][0].count > 1 then
|
|
112
|
+
dinf[:value] = iv[:result][0][1]
|
|
113
|
+
else
|
|
114
|
+
dinf[:value] = nil
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
dinf
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
inf[:children] = rez
|
|
121
|
+
inf.select!{|k,v| limitkeys.include? k }
|
|
122
|
+
inf
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# vim: set ai et sw=2 ts=2 :
|
|
@@ -9,6 +9,7 @@ module MrMurano
|
|
|
9
9
|
# Or better stated, that's all okami-shim is.
|
|
10
10
|
class ProductResources < ProductBase
|
|
11
11
|
include SyncUpDown
|
|
12
|
+
include ProductOnePlatformRpcShim
|
|
12
13
|
|
|
13
14
|
def initialize
|
|
14
15
|
super
|
|
@@ -42,35 +43,6 @@ module MrMurano
|
|
|
42
43
|
name
|
|
43
44
|
end
|
|
44
45
|
|
|
45
|
-
## The model RID for this product.
|
|
46
|
-
def model_rid
|
|
47
|
-
return @model_rid unless @model_rid.nil?
|
|
48
|
-
prd = Product.new
|
|
49
|
-
data = prd.info
|
|
50
|
-
if data.kind_of?(Hash) and data.has_key?(:modelrid) then
|
|
51
|
-
@model_rid = data[:modelrid]
|
|
52
|
-
else
|
|
53
|
-
raise "Bad info; #{data}"
|
|
54
|
-
end
|
|
55
|
-
@model_rid
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
## Do a 1P RPC call
|
|
59
|
-
#
|
|
60
|
-
# While this will take an array of calls, don't. Only pass one.
|
|
61
|
-
def do_rpc(calls)
|
|
62
|
-
calls = [calls] unless calls.kind_of?(Array)
|
|
63
|
-
r = post('', {
|
|
64
|
-
:auth=>{:client_id=>model_rid},
|
|
65
|
-
:calls=>calls
|
|
66
|
-
})
|
|
67
|
-
return r if not r.kind_of?(Array) or r.count < 1
|
|
68
|
-
r = r[0]
|
|
69
|
-
return r if not r.kind_of?(Hash) or r[:status] != 'ok'
|
|
70
|
-
r[:result]
|
|
71
|
-
end
|
|
72
|
-
private :do_rpc
|
|
73
|
-
|
|
74
46
|
## Get 1P info about the prodcut
|
|
75
47
|
def info
|
|
76
48
|
do_rpc({:id=>1,
|
data/lib/MrMurano/Product.rb
CHANGED
|
@@ -29,6 +29,52 @@ module MrMurano
|
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
module ProductOnePlatformRpcShim
|
|
33
|
+
## The model RID for this product.
|
|
34
|
+
def model_rid
|
|
35
|
+
return @model_rid unless @model_rid.nil?
|
|
36
|
+
prd = Product.new
|
|
37
|
+
data = prd.info
|
|
38
|
+
if data.kind_of?(Hash) and data.has_key?(:modelrid) then
|
|
39
|
+
@model_rid = data[:modelrid]
|
|
40
|
+
else
|
|
41
|
+
raise "Bad info; #{data}"
|
|
42
|
+
end
|
|
43
|
+
@model_rid
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
## Do a 1P RPC call
|
|
47
|
+
#
|
|
48
|
+
# While this will take an array of calls, don't. Only pass one.
|
|
49
|
+
# This only returns the result of the first call. Results from other calls are
|
|
50
|
+
# dropped.
|
|
51
|
+
def do_rpc(calls, cid=model_rid)
|
|
52
|
+
calls = [calls] unless calls.kind_of?(Array)
|
|
53
|
+
r = post('', {
|
|
54
|
+
:auth=>{:client_id=>cid},
|
|
55
|
+
:calls=>calls
|
|
56
|
+
})
|
|
57
|
+
return r if not r.kind_of?(Array) or r.count < 1
|
|
58
|
+
r = r[0]
|
|
59
|
+
return r if not r.kind_of?(Hash) or r[:status] != 'ok'
|
|
60
|
+
r[:result]
|
|
61
|
+
end
|
|
62
|
+
private :do_rpc
|
|
63
|
+
|
|
64
|
+
## Do many 1P RPC calls
|
|
65
|
+
def do_mrpc(calls, cid=model_rid)
|
|
66
|
+
calls = [calls] unless calls.kind_of?(Array)
|
|
67
|
+
maxid = ((calls.max_by{|c| c[:id] or 0 }[:id]) or 0)
|
|
68
|
+
calls.map!{|c| c[:id] = (maxid += 1) unless c.has_key?(:id); c}
|
|
69
|
+
post('', {
|
|
70
|
+
:auth=>{:client_id=>cid},
|
|
71
|
+
:calls=>calls
|
|
72
|
+
})
|
|
73
|
+
end
|
|
74
|
+
private :do_mrpc
|
|
75
|
+
|
|
76
|
+
end
|
|
77
|
+
|
|
32
78
|
class Product < ProductBase
|
|
33
79
|
## Get info about the product
|
|
34
80
|
def info
|
|
@@ -25,9 +25,10 @@ module MrMurano
|
|
|
25
25
|
def fetch(id)
|
|
26
26
|
ret = get('/' + id.to_s)
|
|
27
27
|
ret[:content_type] = 'application/json' if ret[:content_type].empty?
|
|
28
|
+
# TODO: add content_type to header if not application/json
|
|
28
29
|
aheader = (ret[:script].lines.first or "").chomp
|
|
29
30
|
dheader = /^--#ENDPOINT (?i:#{ret[:method]}) #{ret[:path]}$/
|
|
30
|
-
rheader = %{--#ENDPOINT #{ret[:method]} #{ret[:path]}\n}
|
|
31
|
+
rheader = %{--#ENDPOINT #{ret[:method].upcase} #{ret[:path]}\n}
|
|
31
32
|
if block_given? then
|
|
32
33
|
yield rheader unless dheader =~ aheader
|
|
33
34
|
yield ret[:script]
|
|
@@ -32,7 +32,7 @@ command :config do |c|
|
|
|
32
32
|
elsif args.count == 0 then
|
|
33
33
|
say_error "Need a config key"
|
|
34
34
|
elsif args.count == 1 and not options.unset then
|
|
35
|
-
options.
|
|
35
|
+
options.default :system=>false, :user=>false, :project=>false,
|
|
36
36
|
:specified=>false, :env=>false
|
|
37
37
|
|
|
38
38
|
# For read, if no scopes, than all. Otherwise just those specified
|
|
@@ -47,7 +47,7 @@ command :config do |c|
|
|
|
47
47
|
say $cfg.get(args[0], scopes)
|
|
48
48
|
else
|
|
49
49
|
|
|
50
|
-
options.
|
|
50
|
+
options.default :system=>false, :user=>false, :project=>true,
|
|
51
51
|
:specified=>false, :env=>false
|
|
52
52
|
# For write, if scope is specified, only write to that scope.
|
|
53
53
|
scope = :project
|
|
@@ -75,7 +75,7 @@ command 'content upload' do |c|
|
|
|
75
75
|
c.option '--meta STRING', %{Add extra meta info to the content item}
|
|
76
76
|
|
|
77
77
|
c.action do |args, options|
|
|
78
|
-
options.
|
|
78
|
+
options.default :meta=>' '
|
|
79
79
|
prd = MrMurano::ProductContent.new
|
|
80
80
|
|
|
81
81
|
if args[0].nil? then
|
|
@@ -4,7 +4,7 @@ command :domain do |c|
|
|
|
4
4
|
c.summary = %{Print the domain for this solution}
|
|
5
5
|
c.option '--[no-]raw', %{Don't add scheme}
|
|
6
6
|
c.action do |args,options|
|
|
7
|
-
options.
|
|
7
|
+
options.default :raw=>true
|
|
8
8
|
sol = MrMurano::Solution.new
|
|
9
9
|
ret = sol.info()
|
|
10
10
|
if options.raw then
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
require 'pathname'
|
|
2
2
|
require 'json'
|
|
3
|
+
require 'yaml'
|
|
3
4
|
require 'fileutils'
|
|
5
|
+
require 'MrMurano/dir'
|
|
6
|
+
require 'MrMurano/Account'
|
|
4
7
|
|
|
5
8
|
command 'config export' do |c|
|
|
6
9
|
c.syntax = %{mr config export}
|
|
@@ -17,7 +20,7 @@ command 'config export' do |c|
|
|
|
17
20
|
c.option '--[no-]merge', "Merge endpoints into a single routes.lua file"
|
|
18
21
|
|
|
19
22
|
c.action do |args, options|
|
|
20
|
-
options.
|
|
23
|
+
options.default :merge => true
|
|
21
24
|
|
|
22
25
|
solfile = Pathname.new($cfg['location.base'] + 'Solutionfile.json')
|
|
23
26
|
solsecret = Pathname.new($cfg['location.base'] + '.Solutionfile.secret')
|
|
@@ -91,10 +94,17 @@ command 'config import' do |c|
|
|
|
91
94
|
by the exosite-cli tool.
|
|
92
95
|
}
|
|
93
96
|
|
|
97
|
+
c.option '--[no-]move', %{Move files into expected places if needed}
|
|
98
|
+
|
|
94
99
|
c.action do |args, options|
|
|
100
|
+
options.default :move=>true
|
|
101
|
+
|
|
95
102
|
solfile = ($cfg['location.base'] + 'Solutionfile.json')
|
|
96
103
|
solsecret = ($cfg['location.base'] + '.Solutionfile.secret')
|
|
97
104
|
|
|
105
|
+
acc = MrMurano::Account.new
|
|
106
|
+
fuopts = {:noop=>$cfg['tool.dry'], :verbose=>$cfg['tool.verbose']}
|
|
107
|
+
|
|
98
108
|
if solfile.exist? then
|
|
99
109
|
# Is in JSON, which as a subset of YAML, so use YAML parser
|
|
100
110
|
solfile.open do |io|
|
|
@@ -102,6 +112,83 @@ command 'config import' do |c|
|
|
|
102
112
|
$cfg.set('location.files', sf['assets']) if sf.has_key? 'assets'
|
|
103
113
|
$cfg.set('location.files', sf['file_dir']) if sf.has_key? 'file_dir'
|
|
104
114
|
$cfg.set('files.default_page', sf['default_page']) if sf.has_key? 'default_page'
|
|
115
|
+
|
|
116
|
+
# look at :routes/:custom_api if in a subdir, set location.endpoints
|
|
117
|
+
# Otherwise to move it
|
|
118
|
+
routes = (sf['custom_api'] or sf['routes'] or '')
|
|
119
|
+
if routes == '' then
|
|
120
|
+
acc.verbose "No endpoints to import"
|
|
121
|
+
elsif File.dirname(routes) == '.' then
|
|
122
|
+
acc.warning "Routes file #{File.basename(routes)} not in endpoints directory"
|
|
123
|
+
if options.move then
|
|
124
|
+
acc.warning "Moving it to #{$cfg['location.endpoints']}"
|
|
125
|
+
FileUtils.mkpath($cfg['location.endpoints'], fuopts)
|
|
126
|
+
FileUtils.mv(routes, File.join($cfg['location.endpoints'], File.basename(routes)), fuopts)
|
|
127
|
+
end
|
|
128
|
+
else
|
|
129
|
+
# Otherwise just use the location they already have
|
|
130
|
+
routeDir = File.dirname(routes)
|
|
131
|
+
acc.verbose "For endpoints using #{routeDir}"
|
|
132
|
+
if $cfg['location.endpoints'] != routeDir then
|
|
133
|
+
$cfg.set('location.endpoints', routeDir)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# if has :cors, export it
|
|
138
|
+
if sf.has_key?('cors') then
|
|
139
|
+
acc.verbose "Exporting CORS to #{$cfg['location.cors']}"
|
|
140
|
+
File.open($cfg['location.cors'], 'w') do |cio|
|
|
141
|
+
cio << sf['cors'].to_yaml
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def update_or_stop(paths, cfgkey, what, acc=MrMurano::Account.new)
|
|
146
|
+
crd = Dir.common_root(paths)
|
|
147
|
+
acc.debug "crd => #{crd}"
|
|
148
|
+
if crd.empty? then
|
|
149
|
+
acc.error "#{what.capitalize} in multiple directories! #{crd.join(', ')}"
|
|
150
|
+
acc.error "Please move them manually into #{$cfg[cfgkey]}"
|
|
151
|
+
exit(1)
|
|
152
|
+
else
|
|
153
|
+
maxd = Dir.max_depth(paths) - crd.count
|
|
154
|
+
if maxd > 2 then
|
|
155
|
+
acc.error "Some #{what} are in directories too deep."
|
|
156
|
+
acc.error "Please move them manually to #{$cfg[cfgkey]}"
|
|
157
|
+
exit(1)
|
|
158
|
+
else
|
|
159
|
+
crd = File.join(crd)
|
|
160
|
+
acc.verbose "For #{what} using #{crd}"
|
|
161
|
+
if $cfg[cfgkey] != crd then
|
|
162
|
+
$cfg.set(cfgkey, crd)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# scan modules for common sub-dir. Set if found. Otherwise warn.
|
|
169
|
+
modules = (sf['modules'] or {})
|
|
170
|
+
update_or_stop(modules.values, 'location.modules', 'modules')
|
|
171
|
+
|
|
172
|
+
# scan eventhandlers for common sub-dir. Set if found. Otherwise warn.
|
|
173
|
+
eventhandlers = (sf['event_handler'] or sf['services'] or {})
|
|
174
|
+
evd = eventhandlers.values.map{|e| e.values}.flatten
|
|
175
|
+
update_or_stop(evd, 'location.eventhandlers', 'eventhandlers')
|
|
176
|
+
|
|
177
|
+
# add header to each eventhandler
|
|
178
|
+
eventhandlers.each do |service, events|
|
|
179
|
+
events.each do |event, path|
|
|
180
|
+
# open path, if no header, add it
|
|
181
|
+
data = IO.readlines(path)
|
|
182
|
+
dheader = "--#EVENT #{service} #{event}"
|
|
183
|
+
aheader = (data.first or "").chomp
|
|
184
|
+
if aheader != dheader then
|
|
185
|
+
acc.verbose "Adding event header to #{path}"
|
|
186
|
+
data.insert(0, dheader)
|
|
187
|
+
File.open(path, 'w'){|eio| eio.puts(data)} unless $cfg['tool.dry']
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
105
192
|
end
|
|
106
193
|
end
|
|
107
194
|
|
|
@@ -133,7 +220,7 @@ command 'config import' do |c|
|
|
|
133
220
|
end
|
|
134
221
|
|
|
135
222
|
say "Configuration items have been imported."
|
|
136
|
-
say "Use `mr syncdown` get get all endpoints, modules, and event handlers"
|
|
223
|
+
#say "Use `mr syncdown` get get all endpoints, modules, and event handlers"
|
|
137
224
|
end
|
|
138
225
|
|
|
139
226
|
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
require 'MrMurano/Account'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
command :init do |c|
|
|
5
|
+
c.syntax = %{mr init}
|
|
6
|
+
c.summary = %{The easy way to start a project}
|
|
7
|
+
c.description = %{}
|
|
8
|
+
|
|
9
|
+
c.option '--force', %{Override existing business, solution, or product ids}
|
|
10
|
+
c.option '--[no-]mkdirs', %{Create default directories}
|
|
11
|
+
|
|
12
|
+
c.action do |args, options|
|
|
13
|
+
options.default :force=>false, :mkdirs=>true
|
|
14
|
+
|
|
15
|
+
if not options.force and ($cfg['location.base'] + 'Solutionfile.json').exist? then
|
|
16
|
+
y=ask("A Solutionfile.json exists, Do you want exit and run `mr config import` instead? [yN]")
|
|
17
|
+
exit 0 unless y =~ /^n/i
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# If they have never logged in, then asking for the business.id will also ask
|
|
22
|
+
# for their username and password.
|
|
23
|
+
acc = MrMurano::Account.new
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# 1. Get business id
|
|
27
|
+
if not options.force and not $cfg['business.id'].nil? then
|
|
28
|
+
say "Using Business ID already set to #{$cfg['business.id']}"
|
|
29
|
+
else
|
|
30
|
+
bizz = acc.businesses
|
|
31
|
+
if bizz.count == 1 then
|
|
32
|
+
bizid = bizz.first
|
|
33
|
+
say "You are only part of one business; using #{bizid[:name]}"
|
|
34
|
+
$cfg.set('businesses.id', bizid[:bizid], :project)
|
|
35
|
+
|
|
36
|
+
else
|
|
37
|
+
choose do |menu|
|
|
38
|
+
menu.prompt = "Select which Business to use:"
|
|
39
|
+
menu.flow = :columns_across
|
|
40
|
+
bizz.sort{|a,b| a[:name]<=>b[:name]}.each do |b|
|
|
41
|
+
menu.choice(b[:name]) do
|
|
42
|
+
$cfg.set('business.id', b[:bizid], :project)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
puts '' # blank line
|
|
49
|
+
|
|
50
|
+
# 2. Get Solution id
|
|
51
|
+
if not options.force and not $cfg['solution.id'].nil? then
|
|
52
|
+
say "Using Solution ID already set to #{$cfg['solution.id']}"
|
|
53
|
+
else
|
|
54
|
+
solz = acc.solutions
|
|
55
|
+
if solz.count == 1 then
|
|
56
|
+
sol = solz.first
|
|
57
|
+
say "You only have one solution; using #{sol[:domain]}"
|
|
58
|
+
$cfg.set('solution.id', sol[:apiId], :project)
|
|
59
|
+
else
|
|
60
|
+
choose do |menu|
|
|
61
|
+
menu.prompt = "Select which Solution to use:"
|
|
62
|
+
menu.flow = :columns_across
|
|
63
|
+
solz.sort{|a,b| a[:domain]<=>b[:domain]}.each do |s|
|
|
64
|
+
menu.choice(s[:domain].sub(/\..*$/,'')) do
|
|
65
|
+
$cfg.set('solution.id', s[:apiId], :project)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
puts '' # blank line
|
|
72
|
+
|
|
73
|
+
# 3. Get Product id
|
|
74
|
+
if not options.force and not $cfg['product.id'].nil? then
|
|
75
|
+
say "Using Product ID already set to #{$cfg['product.id']}"
|
|
76
|
+
else
|
|
77
|
+
podz = acc.products
|
|
78
|
+
if podz.count == 1 then
|
|
79
|
+
prd = podz.first
|
|
80
|
+
say "You only have one product; using #{prd[:label]}"
|
|
81
|
+
$cfg.set('product.id', prd[:modelId], :project)
|
|
82
|
+
else
|
|
83
|
+
choose do |menu|
|
|
84
|
+
menu.prompt = "Select which Product to use:"
|
|
85
|
+
menu.flow = :columns_across
|
|
86
|
+
podz.sort{|a,b| a[:label]<=>b[:label]}.each do |p|
|
|
87
|
+
menu.choice(p[:label]) do
|
|
88
|
+
$cfg.set('product.id', p[:modelId], :project)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
puts ''
|
|
97
|
+
say "Ok, In business ID: #{$cfg['business.id']} using Solution ID: #{$cfg['solution.id']} with Product ID: #{$cfg['product.id']}"
|
|
98
|
+
|
|
99
|
+
if options.mkdirs then
|
|
100
|
+
%w{
|
|
101
|
+
location.files
|
|
102
|
+
location.endpoints
|
|
103
|
+
location.modules
|
|
104
|
+
location.eventhandlers
|
|
105
|
+
location.specs
|
|
106
|
+
}.each do |cfgi|
|
|
107
|
+
path = $cfg[cfgi]
|
|
108
|
+
path = Pathname.new(path) unless path.kind_of? Pathname
|
|
109
|
+
path.mkpath unless path.exist?
|
|
110
|
+
end
|
|
111
|
+
say "Default directories created"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# vim: set ai et sw=2 ts=2 :
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
require 'MrMurano/Product-1P-Device'
|
|
2
|
+
|
|
3
|
+
command 'product device' do |c|
|
|
4
|
+
c.syntax = %{mr product device}
|
|
5
|
+
c.summary = %{Interact with a device in a product}
|
|
6
|
+
c.description = %{}
|
|
7
|
+
|
|
8
|
+
c.action do |a,o|
|
|
9
|
+
::Commander::UI.enable_paging
|
|
10
|
+
say MrMurano::SubCmdGroupHelp.new(c).get_help
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
command 'product device read' do |c|
|
|
15
|
+
c.syntax = %{mr product device read <identifier> (<resources>)}
|
|
16
|
+
c.summary = %{Read recources on a device}
|
|
17
|
+
c.option '-o', '--output FILE', %{Download to file instead of STDOUT}
|
|
18
|
+
|
|
19
|
+
c.action do |args,options|
|
|
20
|
+
snid = args.shift
|
|
21
|
+
prd = MrMurano::Product1PDevice.new
|
|
22
|
+
|
|
23
|
+
if args.count == 0 then
|
|
24
|
+
# fetch list and read all
|
|
25
|
+
args = prd.list(snid).keys
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
io=nil
|
|
29
|
+
io = File.open(options.output, 'w') if options.output
|
|
30
|
+
data = prd.read(snid, args)
|
|
31
|
+
prd.outf(data, io)
|
|
32
|
+
io.close unless io.nil?
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
command 'product device twee' do |c|
|
|
38
|
+
c.syntax = %{mr product device twee <identifier>}
|
|
39
|
+
c.summary = %{Show info about a device}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
c.action do |args,options|
|
|
43
|
+
options.default :width=>HighLine::SystemExtensions.terminal_size[0]
|
|
44
|
+
snid = args.shift
|
|
45
|
+
prd = MrMurano::Product1PDevice.new
|
|
46
|
+
data = prd.twee(snid)
|
|
47
|
+
|
|
48
|
+
io=nil
|
|
49
|
+
io = File.open(options.output, 'w') if options.output
|
|
50
|
+
prd.outf(data, io) do |dd,ios|
|
|
51
|
+
data={}
|
|
52
|
+
data[:title] = "#{snid} #{dd[:description][:name]} #{dd[:basic][:status]}"
|
|
53
|
+
data[:headers] = [:Resource, :Format, :Modified, :Value]
|
|
54
|
+
data[:rows] = dd[:children].map do |child|
|
|
55
|
+
[ (child[:description][:name] or child[:alias]),
|
|
56
|
+
child[:description][:format],
|
|
57
|
+
child[:basic][:modified],
|
|
58
|
+
(child[:value] or "").to_s[0..22] # TODO adjust based on terminal width
|
|
59
|
+
]
|
|
60
|
+
end
|
|
61
|
+
prd.tabularize(data, ios)
|
|
62
|
+
end
|
|
63
|
+
io.close unless io.nil?
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# vim: set ai et sw=2 ts=2 :
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
require 'MrMurano/Product'
|
|
2
2
|
|
|
3
|
-
command 'product write' do |c|
|
|
4
|
-
c.syntax = %{mr product write <
|
|
5
|
-
c.summary = %{Write values into
|
|
3
|
+
command 'product device write' do |c|
|
|
4
|
+
c.syntax = %{mr product device write <identifier> <alias> <value> ([<alias> <value>]…)}
|
|
5
|
+
c.summary = %{Write values into a device}
|
|
6
6
|
|
|
7
7
|
c.action do |args,options|
|
|
8
8
|
sn = args.shift
|
|
@@ -194,12 +194,30 @@ end
|
|
|
194
194
|
command 'tsdb list tags' do |c|
|
|
195
195
|
c.syntax = %{mr tsdb list tags [options]}
|
|
196
196
|
c.summary = %{List tags}
|
|
197
|
+
c.option '--values', %{Also return the known tag values}
|
|
197
198
|
|
|
198
199
|
c.action do |args, options|
|
|
200
|
+
options.default :values=>false
|
|
201
|
+
|
|
199
202
|
sol = MrMurano::ServiceConfigs::Tsdb.new
|
|
200
203
|
ret = sol.listTags
|
|
201
204
|
# TODO: handle looping if :next != nil
|
|
202
|
-
|
|
205
|
+
|
|
206
|
+
if options.values then
|
|
207
|
+
sol.outf(ret[:tags]) do |dd, ios|
|
|
208
|
+
data={}
|
|
209
|
+
data[:headers] = dd.keys
|
|
210
|
+
data[:rows] = dd.keys.map{|k| dd[k]}
|
|
211
|
+
len = data[:rows].map{|i| i.length}.max
|
|
212
|
+
data[:rows].each{|r| r.fill(nil, r.length, len - r.length)}
|
|
213
|
+
data[:rows] = data[:rows].transpose
|
|
214
|
+
sol.tabularize(data, ios)
|
|
215
|
+
end
|
|
216
|
+
else
|
|
217
|
+
sol.outf ret[:tags].keys
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
|
|
203
221
|
end
|
|
204
222
|
end
|
|
205
223
|
|
data/lib/MrMurano/commands.rb
CHANGED
|
@@ -5,11 +5,13 @@ require 'MrMurano/commands/content'
|
|
|
5
5
|
require 'MrMurano/commands/cors'
|
|
6
6
|
require 'MrMurano/commands/domain'
|
|
7
7
|
require 'MrMurano/commands/exportImport'
|
|
8
|
+
require 'MrMurano/commands/init'
|
|
8
9
|
require 'MrMurano/commands/keystore'
|
|
9
10
|
require 'MrMurano/commands/logs'
|
|
10
11
|
require 'MrMurano/commands/product'
|
|
11
12
|
require 'MrMurano/commands/productCreate'
|
|
12
13
|
require 'MrMurano/commands/productDelete'
|
|
14
|
+
require 'MrMurano/commands/productDevice'
|
|
13
15
|
require 'MrMurano/commands/productList'
|
|
14
16
|
require 'MrMurano/commands/productSpec'
|
|
15
17
|
require 'MrMurano/commands/productWrite'
|
data/lib/MrMurano/dir.rb
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
|
|
2
|
+
## Get the root-most common directories from paths
|
|
3
|
+
def Dir.common_dirs(paths)
|
|
4
|
+
paths = paths.map do |p|
|
|
5
|
+
if p.kind_of? Array then
|
|
6
|
+
p
|
|
7
|
+
else
|
|
8
|
+
p.to_s.split(File::SEPARATOR)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
paths.map{|p| p.first}.uniq
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
## How deep is deepest directory path? (not including the file)
|
|
16
|
+
def Dir.max_depth(paths)
|
|
17
|
+
paths = paths.map do |p|
|
|
18
|
+
if p.kind_of? Array then
|
|
19
|
+
p
|
|
20
|
+
else
|
|
21
|
+
p.to_s.split(File::SEPARATOR)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
paths.map{|p| p.count - 1}.max
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
## Get the deepest common root directory for all paths
|
|
30
|
+
def Dir.common_root(paths, root=[])
|
|
31
|
+
paths = paths.map do |p|
|
|
32
|
+
if p.kind_of? Array then
|
|
33
|
+
p
|
|
34
|
+
else
|
|
35
|
+
p.to_s.split(File::SEPARATOR)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
base = Dir.common_dirs(paths)
|
|
40
|
+
if base.count == 1 then
|
|
41
|
+
return common_root(paths.map{|p| p[1..-1]}, root + base)
|
|
42
|
+
else
|
|
43
|
+
return root
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# vim: set ai et sw=2 ts=2 :
|
data/lib/MrMurano/version.rb
CHANGED
data/lib/MrMurano.rb
CHANGED
|
@@ -11,6 +11,7 @@ require 'MrMurano/Solution-Services'
|
|
|
11
11
|
require 'MrMurano/Solution-Users'
|
|
12
12
|
require 'MrMurano/Solution-ServiceConfig'
|
|
13
13
|
require 'MrMurano/Product'
|
|
14
|
+
require 'MrMurano/Product-1P-Device'
|
|
14
15
|
require 'MrMurano/Product-Resources'
|
|
15
16
|
|
|
16
17
|
require 'MrMurano/commands'
|
|
@@ -51,66 +51,6 @@ RSpec.describe MrMurano::ProductResources do
|
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
context "do_rpc" do
|
|
55
|
-
# Note, do_rpc is private.
|
|
56
|
-
it "Accepts an object" do
|
|
57
|
-
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
58
|
-
to_return(body: [{
|
|
59
|
-
:id=>1, :status=>"ok", :result=>{}
|
|
60
|
-
}])
|
|
61
|
-
|
|
62
|
-
ret = nil
|
|
63
|
-
@prd.instance_eval{ ret = do_rpc({:id=>1}) }
|
|
64
|
-
expect(ret).to eq({})
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
it "Accepts an Array" do
|
|
68
|
-
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
69
|
-
to_return(body: [{:id=>1, :status=>"ok", :result=>{:one=>1}},
|
|
70
|
-
{:id=>2, :status=>"ok", :result=>{:two=>2}}])
|
|
71
|
-
|
|
72
|
-
ret = nil
|
|
73
|
-
@prd.instance_eval{ ret = do_rpc([{:id=>1}, {:id=>2}]) }
|
|
74
|
-
expect(ret).to eq({:one=>1})
|
|
75
|
-
# yes it only returns first.
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
it "returns res if not Array" do
|
|
79
|
-
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
80
|
-
to_return(body: {:not=>'an array'}.to_json)
|
|
81
|
-
|
|
82
|
-
ret = nil
|
|
83
|
-
@prd.instance_eval{ ret = do_rpc({:id=>1}) }
|
|
84
|
-
expect(ret).to eq({:not=>'an array'})
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
it "returns res if count less than 1" do
|
|
88
|
-
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
89
|
-
to_return(body: [])
|
|
90
|
-
|
|
91
|
-
ret = nil
|
|
92
|
-
@prd.instance_eval{ ret = do_rpc({:id=>1}) }
|
|
93
|
-
expect(ret).to eq([])
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
it "returns res[0] if not Hash" do
|
|
97
|
-
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
98
|
-
to_return(body: ["foo"])
|
|
99
|
-
|
|
100
|
-
ret = nil
|
|
101
|
-
@prd.instance_eval{ ret = do_rpc({:id=>1}) }
|
|
102
|
-
expect(ret).to eq("foo")
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
it "returns res[0] if not status ok" do
|
|
106
|
-
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
107
|
-
to_return(body: [{:id=>1, :status=>'error'}])
|
|
108
|
-
|
|
109
|
-
ret = nil
|
|
110
|
-
@prd.instance_eval{ ret = do_rpc({:id=>1}) }
|
|
111
|
-
expect(ret).to eq({:id=>1, :status=>'error'})
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
54
|
|
|
115
55
|
context "queries" do
|
|
116
56
|
it "gets info" do
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
require 'MrMurano/version'
|
|
2
|
+
require 'MrMurano/Config'
|
|
3
|
+
require 'MrMurano/Product-1P-Device'
|
|
4
|
+
|
|
5
|
+
RSpec.describe MrMurano::Product1PDevice do
|
|
6
|
+
before(:example) do
|
|
7
|
+
$cfg = MrMurano::Config.new
|
|
8
|
+
$cfg.load
|
|
9
|
+
$cfg['net.host'] = 'bizapi.hosted.exosite.io'
|
|
10
|
+
$cfg['product.id'] = 'XYZ'
|
|
11
|
+
$cfg['product.spec'] = 'XYZ.yaml'
|
|
12
|
+
|
|
13
|
+
@prd = MrMurano::Product1PDevice.new
|
|
14
|
+
allow(@prd).to receive(:token).and_return("TTTTTTTTTT")
|
|
15
|
+
allow(@prd).to receive(:sn_rid).and_return("LLLLLLLLLL")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "initializes" do
|
|
19
|
+
uri = @prd.endPoint('')
|
|
20
|
+
expect(uri.to_s).to eq("https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "gets info" do
|
|
24
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
25
|
+
with(body: {:auth=>{:client_id=>"LLLLLLLLLL"},
|
|
26
|
+
:calls=>[{:id=>1,
|
|
27
|
+
:procedure=>"info",
|
|
28
|
+
:arguments=>["LLLLLLLLLL", {}]} ]}).
|
|
29
|
+
to_return(body: [{:id=>1, :status=>"ok", :result=>{:comments=>[]}}])
|
|
30
|
+
|
|
31
|
+
ret = @prd.info("12")
|
|
32
|
+
expect(ret).to eq({:comments=>[]})
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "lists resources" do
|
|
36
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
37
|
+
with(body: {:auth=>{:client_id=>"LLLLLLLLLL"},
|
|
38
|
+
:calls=>[{:id=>1,
|
|
39
|
+
:procedure=>"info",
|
|
40
|
+
:arguments=>["LLLLLLLLLL", {}]} ]}).
|
|
41
|
+
to_return(body: [{:id=>1, :status=>"ok", :result=>{:aliases=>{
|
|
42
|
+
:s2143rt4regf=>["one"],:njilh32o78rnq=>["two"]}}}])
|
|
43
|
+
|
|
44
|
+
ret = @prd.list("12")
|
|
45
|
+
expect(ret).to eq({"one"=>"s2143rt4regf", "two"=>"njilh32o78rnq"})
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
context "reads resources" do
|
|
49
|
+
it "single" do
|
|
50
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
51
|
+
with(body: {:auth=>{:client_id=>"LLLLLLLLLL"},
|
|
52
|
+
:calls=>[{:id=>1,
|
|
53
|
+
:procedure=>"read",
|
|
54
|
+
:arguments=>[{"alias"=>"one"}, {}]} ]}).
|
|
55
|
+
to_return(body: [{:id=>1, :status=>"ok", :result=>[ [12345678,10] ]}])
|
|
56
|
+
ret = @prd.read("12", "one")
|
|
57
|
+
expect(ret).to eq([10])
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "multiple" do
|
|
61
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
62
|
+
with(body: {:auth=>{:client_id=>"LLLLLLLLLL"},
|
|
63
|
+
:calls=>[{:id=>1,
|
|
64
|
+
:procedure=>"read",
|
|
65
|
+
:arguments=>[{"alias"=>"two"}, {}]},
|
|
66
|
+
{:id=>2,
|
|
67
|
+
:procedure=>"read",
|
|
68
|
+
:arguments=>[{"alias"=>"three"}, {}]},
|
|
69
|
+
{:id=>3,
|
|
70
|
+
:procedure=>"read",
|
|
71
|
+
:arguments=>[{"alias"=>"one"}, {}]},
|
|
72
|
+
]}).
|
|
73
|
+
to_return(body: [
|
|
74
|
+
{:id=>1, :status=>"ok", :result=>[ [12345678,10] ]},
|
|
75
|
+
{:id=>2, :status=>"ok", :result=>[ [12345678,15] ]},
|
|
76
|
+
{:id=>3, :status=>"ok", :result=>[ [12345678,20] ]},
|
|
77
|
+
])
|
|
78
|
+
ret = @prd.read("12", ["two","three","one"])
|
|
79
|
+
expect(ret).to eq([10,15,20])
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "gets tree info for device" do
|
|
85
|
+
# this makes three https calls, info on root, info and read on children
|
|
86
|
+
|
|
87
|
+
# root info.
|
|
88
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
89
|
+
with(body: {:auth=>{:client_id=>"LLLLLLLLLL"},
|
|
90
|
+
:calls=>[{:id=>1,
|
|
91
|
+
:procedure=>"info",
|
|
92
|
+
:arguments=>["LLLLLLLLLL", {}]} ]}).
|
|
93
|
+
to_return(body: [{:id=>1, :status=>"ok", :result=>{:aliases=>{
|
|
94
|
+
:s2143rt4regf=>["one"],:njilh32o78rnq=>["two"]}}}])
|
|
95
|
+
|
|
96
|
+
# children info
|
|
97
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
98
|
+
with(body: {:auth=>{:client_id=>"LLLLLLLLLL"},
|
|
99
|
+
:calls=>[{:id=>1,
|
|
100
|
+
:procedure=>"info",
|
|
101
|
+
:arguments=>["s2143rt4regf", {}]},
|
|
102
|
+
{:id=>2,
|
|
103
|
+
:procedure=>"info",
|
|
104
|
+
:arguments=>["njilh32o78rnq", {}]}
|
|
105
|
+
]}).
|
|
106
|
+
to_return(body: [
|
|
107
|
+
{:id=>1, :status=>"ok", :result=>{:basic=>{:type=>"dataport"}}},
|
|
108
|
+
{:id=>2, :status=>"ok", :result=>{:basic=>{:type=>"dataport"}}}
|
|
109
|
+
])
|
|
110
|
+
|
|
111
|
+
# children read
|
|
112
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
113
|
+
with(body: {:auth=>{:client_id=>"LLLLLLLLLL"},
|
|
114
|
+
:calls=>[{:id=>1,
|
|
115
|
+
:procedure=>"read",
|
|
116
|
+
:arguments=>["s2143rt4regf", {}]},
|
|
117
|
+
{:id=>2,
|
|
118
|
+
:procedure=>"read",
|
|
119
|
+
:arguments=>["njilh32o78rnq", {}]},
|
|
120
|
+
]}).
|
|
121
|
+
to_return(body: [
|
|
122
|
+
{:id=>1, :status=>"ok", :result=>[ [12345678,10] ]},
|
|
123
|
+
{:id=>2, :status=>"ok", :result=>[ [12345678,15] ]},
|
|
124
|
+
])
|
|
125
|
+
|
|
126
|
+
ret = @prd.twee('12')
|
|
127
|
+
expect(ret).to eq({:children=>[{:basic=>{:type=>"dataport"},
|
|
128
|
+
:rid=>:s2143rt4regf, :alias=>"one", :value=>10},
|
|
129
|
+
{:basic=>{:type=>"dataport"},
|
|
130
|
+
:rid=>:njilh32o78rnq, :alias=>"two",
|
|
131
|
+
:value=>15}]})
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# vim: set ai et sw=2 ts=2 :
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
require 'MrMurano/version'
|
|
2
|
+
require 'MrMurano/Config'
|
|
3
|
+
require 'MrMurano/Product-Resources'
|
|
4
|
+
|
|
5
|
+
RSpec.describe MrMurano::ProductResources, "#1PshimTests" do
|
|
6
|
+
before(:example) do
|
|
7
|
+
$cfg = MrMurano::Config.new
|
|
8
|
+
$cfg.load
|
|
9
|
+
$cfg['net.host'] = 'bizapi.hosted.exosite.io'
|
|
10
|
+
$cfg['product.id'] = 'XYZ'
|
|
11
|
+
$cfg['product.spec'] = 'XYZ.yaml'
|
|
12
|
+
|
|
13
|
+
@prd = MrMurano::ProductResources.new
|
|
14
|
+
allow(@prd).to receive(:token).and_return("TTTTTTTTTT")
|
|
15
|
+
allow(@prd).to receive(:model_rid).and_return("LLLLLLLLLL")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
context "do_rpc" do
|
|
19
|
+
# Note, do_rpc is private.
|
|
20
|
+
it "Accepts an object" do
|
|
21
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
22
|
+
to_return(body: [{
|
|
23
|
+
:id=>1, :status=>"ok", :result=>{}
|
|
24
|
+
}])
|
|
25
|
+
|
|
26
|
+
ret = nil
|
|
27
|
+
@prd.instance_eval{ ret = do_rpc({:id=>1}) }
|
|
28
|
+
expect(ret).to eq({})
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "Accepts an Array" do
|
|
32
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
33
|
+
to_return(body: [{:id=>1, :status=>"ok", :result=>{:one=>1}},
|
|
34
|
+
{:id=>2, :status=>"ok", :result=>{:two=>2}}])
|
|
35
|
+
|
|
36
|
+
ret = nil
|
|
37
|
+
@prd.instance_eval{ ret = do_rpc([{:id=>1}, {:id=>2}]) }
|
|
38
|
+
expect(ret).to eq({:one=>1})
|
|
39
|
+
# yes it only returns first.
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "returns res if not Array" do
|
|
43
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
44
|
+
to_return(body: {:not=>'an array'}.to_json)
|
|
45
|
+
|
|
46
|
+
ret = nil
|
|
47
|
+
@prd.instance_eval{ ret = do_rpc({:id=>1}) }
|
|
48
|
+
expect(ret).to eq({:not=>'an array'})
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "returns res if count less than 1" do
|
|
52
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
53
|
+
to_return(body: [])
|
|
54
|
+
|
|
55
|
+
ret = nil
|
|
56
|
+
@prd.instance_eval{ ret = do_rpc({:id=>1}) }
|
|
57
|
+
expect(ret).to eq([])
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "returns res[0] if not Hash" do
|
|
61
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
62
|
+
to_return(body: ["foo"])
|
|
63
|
+
|
|
64
|
+
ret = nil
|
|
65
|
+
@prd.instance_eval{ ret = do_rpc({:id=>1}) }
|
|
66
|
+
expect(ret).to eq("foo")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "returns res[0] if not status ok" do
|
|
70
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
71
|
+
to_return(body: [{:id=>1, :status=>'error'}])
|
|
72
|
+
|
|
73
|
+
ret = nil
|
|
74
|
+
@prd.instance_eval{ ret = do_rpc({:id=>1}) }
|
|
75
|
+
expect(ret).to eq({:id=>1, :status=>'error'})
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
context "do_mrpc" do
|
|
80
|
+
# Note, do_rpc is private.
|
|
81
|
+
it "Accepts an object" do
|
|
82
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
83
|
+
to_return(body: [{
|
|
84
|
+
:id=>1, :status=>"ok", :result=>{}
|
|
85
|
+
}])
|
|
86
|
+
|
|
87
|
+
ret = nil
|
|
88
|
+
@prd.instance_eval{ ret = do_mrpc({:id=>1}) }
|
|
89
|
+
expect(ret).to eq([{:id=>1, :status=>"ok", :result=>{}}])
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "Accepts an Array" do
|
|
93
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
94
|
+
to_return(body: [{:id=>1, :status=>"ok", :result=>{:one=>1}},
|
|
95
|
+
{:id=>2, :status=>"ok", :result=>{:two=>2}}])
|
|
96
|
+
|
|
97
|
+
ret = nil
|
|
98
|
+
@prd.instance_eval{ ret = do_mrpc([{:id=>1}, {:id=>2}]) }
|
|
99
|
+
expect(ret).to eq([{:id=>1, :status=>"ok", :result=>{:one=>1}},
|
|
100
|
+
{:id=>2, :status=>"ok", :result=>{:two=>2}}])
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "fills in all ids if missing" do
|
|
104
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
105
|
+
to_return(body: [{:id=>1, :status=>"ok", :result=>{:one=>1}},
|
|
106
|
+
{:id=>2, :status=>"ok", :result=>{:two=>2}},
|
|
107
|
+
{:id=>3, :status=>"ok", :result=>{:three=>3}}])
|
|
108
|
+
|
|
109
|
+
ret = nil
|
|
110
|
+
@prd.instance_eval{ ret = do_mrpc([{}, {}, {}]) }
|
|
111
|
+
expect(ret).to eq([{:id=>1, :status=>"ok", :result=>{:one=>1}},
|
|
112
|
+
{:id=>2, :status=>"ok", :result=>{:two=>2}},
|
|
113
|
+
{:id=>3, :status=>"ok", :result=>{:three=>3}}])
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it "fills in missing ids" do
|
|
117
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
118
|
+
with(body: {:auth=>{:client_id=>"LLLLLLLLLL"},
|
|
119
|
+
:calls=>[{:id=>5, :procedure=>"info"},
|
|
120
|
+
{:id=>4, :procedure=>"info"},
|
|
121
|
+
{:id=>6, :procedure=>"info"} ]}).
|
|
122
|
+
to_return(body: [{:id=>5, :status=>"ok", :result=>{:one=>1}},
|
|
123
|
+
{:id=>4, :status=>"ok", :result=>{:two=>2}},
|
|
124
|
+
{:id=>6, :status=>"ok", :result=>{:three=>3}}])
|
|
125
|
+
|
|
126
|
+
ret = nil
|
|
127
|
+
@prd.instance_eval{ ret = do_mrpc([{:procedure=>"info"},
|
|
128
|
+
{:procedure=>"info", :id=>4},
|
|
129
|
+
{:procedure=>"info"}]) }
|
|
130
|
+
expect(ret).to eq([{:id=>5, :status=>"ok", :result=>{:one=>1}},
|
|
131
|
+
{:id=>4, :status=>"ok", :result=>{:two=>2}},
|
|
132
|
+
{:id=>6, :status=>"ok", :result=>{:three=>3}}])
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it "returns res if not Array" do
|
|
136
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
137
|
+
to_return(body: {:not=>'an array'}.to_json)
|
|
138
|
+
|
|
139
|
+
ret = nil
|
|
140
|
+
@prd.instance_eval{ ret = do_mrpc({:id=>1}) }
|
|
141
|
+
expect(ret).to eq({:not=>'an array'})
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
it "returns res if count less than 1" do
|
|
145
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
146
|
+
to_return(body: [])
|
|
147
|
+
|
|
148
|
+
ret = nil
|
|
149
|
+
@prd.instance_eval{ ret = do_mrpc({:id=>1}) }
|
|
150
|
+
expect(ret).to eq([])
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it "returns res[0] if not Hash" do
|
|
154
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
155
|
+
to_return(body: ["foo"])
|
|
156
|
+
|
|
157
|
+
ret = nil
|
|
158
|
+
@prd.instance_eval{ ret = do_mrpc({:id=>1}) }
|
|
159
|
+
expect(ret).to eq(["foo"])
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it "returns res[0] if not status ok" do
|
|
163
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process").
|
|
164
|
+
to_return(body: [{:id=>1, :status=>'error'}])
|
|
165
|
+
|
|
166
|
+
ret = nil
|
|
167
|
+
@prd.instance_eval{ ret = do_mrpc({:id=>1}) }
|
|
168
|
+
expect(ret).to eq([{:id=>1, :status=>'error'}])
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# vim: set ai et sw=2 ts=2 :
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: MrMurano
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michael Conrad Tadpol Tilstra
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2016-11-
|
|
11
|
+
date: 2016-11-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: commander
|
|
@@ -200,6 +200,7 @@ files:
|
|
|
200
200
|
- lib/MrMurano.rb
|
|
201
201
|
- lib/MrMurano/Account.rb
|
|
202
202
|
- lib/MrMurano/Config.rb
|
|
203
|
+
- lib/MrMurano/Product-1P-Device.rb
|
|
203
204
|
- lib/MrMurano/Product-Resources.rb
|
|
204
205
|
- lib/MrMurano/Product.rb
|
|
205
206
|
- lib/MrMurano/Solution-Cors.rb
|
|
@@ -220,11 +221,13 @@ files:
|
|
|
220
221
|
- lib/MrMurano/commands/cors.rb
|
|
221
222
|
- lib/MrMurano/commands/domain.rb
|
|
222
223
|
- lib/MrMurano/commands/exportImport.rb
|
|
224
|
+
- lib/MrMurano/commands/init.rb
|
|
223
225
|
- lib/MrMurano/commands/keystore.rb
|
|
224
226
|
- lib/MrMurano/commands/logs.rb
|
|
225
227
|
- lib/MrMurano/commands/product.rb
|
|
226
228
|
- lib/MrMurano/commands/productCreate.rb
|
|
227
229
|
- lib/MrMurano/commands/productDelete.rb
|
|
230
|
+
- lib/MrMurano/commands/productDevice.rb
|
|
228
231
|
- lib/MrMurano/commands/productList.rb
|
|
229
232
|
- lib/MrMurano/commands/productSpec.rb
|
|
230
233
|
- lib/MrMurano/commands/productWrite.rb
|
|
@@ -238,6 +241,7 @@ files:
|
|
|
238
241
|
- lib/MrMurano/commands/timeseries.rb
|
|
239
242
|
- lib/MrMurano/commands/tsdb.rb
|
|
240
243
|
- lib/MrMurano/commands/zshcomplete.erb
|
|
244
|
+
- lib/MrMurano/dir.rb
|
|
241
245
|
- lib/MrMurano/hash.rb
|
|
242
246
|
- lib/MrMurano/http.rb
|
|
243
247
|
- lib/MrMurano/makePretty.rb
|
|
@@ -251,6 +255,8 @@ files:
|
|
|
251
255
|
- spec/ProductBase_spec.rb
|
|
252
256
|
- spec/ProductContent_spec.rb
|
|
253
257
|
- spec/ProductResources_spec.rb
|
|
258
|
+
- spec/Product_1P_Device_spec.rb
|
|
259
|
+
- spec/Product_1P_RPC_spec.rb
|
|
254
260
|
- spec/Product_spec.rb
|
|
255
261
|
- spec/Solution-Cors_spec.rb
|
|
256
262
|
- spec/Solution-ServiceConfig_spec.rb
|
|
@@ -300,6 +306,8 @@ test_files:
|
|
|
300
306
|
- spec/ProductBase_spec.rb
|
|
301
307
|
- spec/ProductContent_spec.rb
|
|
302
308
|
- spec/ProductResources_spec.rb
|
|
309
|
+
- spec/Product_1P_Device_spec.rb
|
|
310
|
+
- spec/Product_1P_RPC_spec.rb
|
|
303
311
|
- spec/Product_spec.rb
|
|
304
312
|
- spec/Solution-Cors_spec.rb
|
|
305
313
|
- spec/Solution-ServiceConfig_spec.rb
|