MrMurano 1.6.3 → 1.7.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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -2
  3. data/Gemfile +1 -1
  4. data/MrMurano.gemspec +1 -1
  5. data/README.markdown +4 -0
  6. data/TODO.taskpaper +17 -4
  7. data/lib/MrMurano/Account.rb +5 -1
  8. data/lib/MrMurano/Config.rb +3 -1
  9. data/lib/MrMurano/Product-Resources.rb +239 -0
  10. data/lib/MrMurano/Product.rb +51 -2
  11. data/lib/MrMurano/Solution-Cors.rb +81 -0
  12. data/lib/MrMurano/Solution-Endpoint.rb +8 -4
  13. data/lib/MrMurano/Solution-File.rb +4 -2
  14. data/lib/MrMurano/Solution-ServiceConfig.rb +74 -1
  15. data/lib/MrMurano/Solution-Services.rb +4 -2
  16. data/lib/MrMurano/Solution-Users.rb +6 -3
  17. data/lib/MrMurano/Solution.rb +6 -274
  18. data/lib/MrMurano/SyncUpDown.rb +433 -0
  19. data/lib/MrMurano/commands/completion.rb +152 -0
  20. data/lib/MrMurano/commands/content.rb +15 -14
  21. data/lib/MrMurano/commands/cors.rb +11 -38
  22. data/lib/MrMurano/commands/exportImport.rb +4 -3
  23. data/lib/MrMurano/commands/keystore.rb +15 -16
  24. data/lib/MrMurano/commands/logs.rb +2 -2
  25. data/lib/MrMurano/commands/productCreate.rb +4 -4
  26. data/lib/MrMurano/commands/productDelete.rb +6 -8
  27. data/lib/MrMurano/commands/productSpec.rb +31 -36
  28. data/lib/MrMurano/commands/productWrite.rb +9 -7
  29. data/lib/MrMurano/commands/serialNumberCmds.rb +4 -4
  30. data/lib/MrMurano/commands/solutionCreate.rb +4 -4
  31. data/lib/MrMurano/commands/solutionDelete.rb +5 -5
  32. data/lib/MrMurano/commands/status.rb +9 -42
  33. data/lib/MrMurano/commands/sync.rb +15 -71
  34. data/lib/MrMurano/commands/timeseries.rb +23 -29
  35. data/lib/MrMurano/commands/tsdb.rb +202 -0
  36. data/lib/MrMurano/commands/zshcomplete.erb +112 -0
  37. data/lib/MrMurano/commands.rb +4 -1
  38. data/lib/MrMurano/http.rb +4 -3
  39. data/lib/MrMurano/makePretty.rb +15 -6
  40. data/lib/MrMurano/verbosing.rb +71 -0
  41. data/lib/MrMurano/version.rb +1 -1
  42. data/lib/MrMurano.rb +1 -0
  43. data/spec/ConfigFile_spec.rb +3 -3
  44. data/spec/ProductContent_spec.rb +2 -2
  45. data/spec/ProductResources_spec.rb +152 -0
  46. data/spec/Product_spec.rb +38 -1
  47. data/spec/Solution-Cors_spec.rb +134 -0
  48. data/spec/Solution-ServiceConfig_spec.rb +198 -0
  49. data/spec/SyncRoot_spec.rb +74 -0
  50. data/spec/cmd_config_spec.rb +51 -0
  51. data/spec/fixtures/.mrmuranorc +9 -0
  52. data/spec/{testfiles → fixtures}/configfile +0 -0
  53. data/spec/fixtures/mrmuranorc_deleted_bob +8 -0
  54. data/spec/fixtures/product_spec_files/example.exoline.spec.yaml +116 -0
  55. data/spec/fixtures/product_spec_files/example.murano.spec.yaml +14 -0
  56. data/spec/fixtures/product_spec_files/gwe.exoline.spec.yaml +21 -0
  57. data/spec/fixtures/product_spec_files/gwe.murano.spec.yaml +16 -0
  58. data/spec/{lightbulb.yaml → fixtures/product_spec_files/lightbulb.yaml} +0 -0
  59. data/spec/spec_helper.rb +4 -4
  60. metadata +47 -19
@@ -18,15 +18,88 @@ module MrMurano
18
18
  def scid_for_name(name)
19
19
  name = name.to_s unless name.kind_of? String
20
20
  scr = list().select{|i| i[:service] == name}.first
21
- scid = scr[:id]
21
+ return nil if scr.nil?
22
+ scr[:id]
22
23
  end
23
24
 
24
25
  def scid
25
26
  return @scid unless @scid.nil?
26
27
  @scid = scid_for_name(@serviceName)
27
28
  end
29
+
30
+ def info(id=scid)
31
+ get("/#{id}/info")
32
+ end
33
+
34
+ def logs(id=scid)
35
+ get("/#{id}/logs")
36
+ end
37
+
38
+ def call(opid, meth=:get, data=nil, id=scid, &block)
39
+ call = "/#{id.to_s}/call/#{opid.to_s}"
40
+ case meth
41
+ when :get
42
+ get(call, data, &block)
43
+ when :post
44
+ data = {} if data.nil?
45
+ post(call, data, &block)
46
+ when :put
47
+ data = {} if data.nil?
48
+ put(call, data, &block)
49
+ when :delete
50
+ delete(call, &block)
51
+ else
52
+ raise "Unknown method: #{meth}"
53
+ end
54
+ end
55
+
28
56
  end
29
57
 
58
+ class Services < SolutionBase
59
+ def initialize
60
+ super
61
+ @uriparts << 'service'
62
+ end
63
+
64
+ def sid_for_name(name)
65
+ name = name.to_s unless name.kind_of? String
66
+ scr = list().select{|i| i[:alias] == name}.first
67
+ scr[:id]
68
+ end
69
+
70
+ def sid
71
+ return @sid unless @sid.nil?
72
+ @sid = sid_for_name(@serviceName)
73
+ end
74
+
75
+ def list
76
+ ret = get()
77
+ ret[:items]
78
+ end
79
+
80
+ def schema(id=sid)
81
+ # TODO: cache schema in user dir?
82
+ get("/#{id}/schema")
83
+ end
84
+
85
+ ## Get list of call operations from a schema
86
+ def callable(id=sid)
87
+ scm = schema(id)
88
+ calls = []
89
+ scm[:paths].each do |path, methods|
90
+ methods.each do |method, params|
91
+ if params.kind_of?(Hash) and
92
+ not params['x-internal-use'.to_sym] and
93
+ params.has_key?(:operationId) then
94
+ calls << [method, params[:operationId]]
95
+ end
96
+ end
97
+ end
98
+ calls
99
+ end
100
+ end
101
+
102
+
30
103
  class SC_Device < ServiceConfig
31
104
  def initialize
32
105
  super
@@ -47,7 +47,8 @@ module MrMurano
47
47
  delete('/'+name)
48
48
  end
49
49
 
50
- def upload(local, remote)
50
+ # @param modify Bool: True if item exists already and this is changing it
51
+ def upload(local, remote, modify=false)
51
52
  local = Pathname.new(local) unless local.kind_of? Pathname
52
53
  raise "no file" unless local.exist?
53
54
 
@@ -164,6 +165,7 @@ module MrMurano
164
165
  item[:name]
165
166
  end
166
167
  end
168
+ SyncRoot.add('modules', Library, 'M', %{Modules}, true)
167
169
 
168
170
  # …/eventhandler
169
171
  class EventHandler < ServiceBase
@@ -222,7 +224,7 @@ module MrMurano
222
224
  "#{item[:service]}_#{item[:event]}"
223
225
  end
224
226
  end
227
+ SyncRoot.add('eventhandlers', EventHandler, 'E', %{Event Handlers}, true)
225
228
 
226
- # How do we enable product.id to flow into the eventhandler?
227
229
  end
228
230
  # vim: set ai et sw=2 ts=2 :
@@ -21,7 +21,8 @@ module MrMurano
21
21
  delete('/' + id.to_s)
22
22
  end
23
23
 
24
- def upload(local, remote)
24
+ # @param modify Bool: True if item exists already and this is changing it
25
+ def upload(local, remote, modify)
25
26
  # Roles cannot be modified, so must delete and post.
26
27
  delete('/' + remote[@itemkey]) do |request, http|
27
28
  response = http.request(request)
@@ -84,7 +85,6 @@ module MrMurano
84
85
  say_warning "Cannot read from #{from.to_s}"
85
86
  return []
86
87
  end
87
- key = @itemkey.to_sym
88
88
 
89
89
  here = []
90
90
  from.open {|io| here = YAML.load(io) }
@@ -103,6 +103,7 @@ module MrMurano
103
103
  @location = $cfg['location.roles']
104
104
  end
105
105
  end
106
+ SyncRoot.add('roles', Role, 'R', %{Roles})
106
107
 
107
108
  # …/user
108
109
  class User < UserBase
@@ -112,7 +113,8 @@ module MrMurano
112
113
  @location = $cfg['location.users']
113
114
  end
114
115
 
115
- def upload(local, remote)
116
+ # @param modify Bool: True if item exists already and this is changing it
117
+ def upload(local, remote, modify)
116
118
  # TODO figure out APIs for updating users.
117
119
  say_warning "Updating Users isn't working currently."
118
120
  # post does work if the :password field is set.
@@ -122,5 +124,6 @@ module MrMurano
122
124
  item[:email]
123
125
  end
124
126
  end
127
+ SyncRoot.add('users', User, 'U', %{Users})
125
128
  end
126
129
  # vim: set ai et sw=2 ts=2 :
@@ -1,16 +1,11 @@
1
1
  require 'uri'
2
- require 'net/http'
3
- require 'json'
4
- require 'tempfile'
5
- require 'shellwords'
6
- require 'pp'
7
2
  require 'MrMurano/Config'
8
3
  require 'MrMurano/http'
9
4
  require 'MrMurano/verbosing'
5
+ require 'MrMurano/SyncUpDown'
10
6
 
11
7
  module MrMurano
12
8
  class SolutionBase
13
- # This might also be a valid ProductBase.
14
9
  def initialize
15
10
  @sid = $cfg['solution.id']
16
11
  raise "No solution!" if @sid.nil?
@@ -23,6 +18,10 @@ module MrMurano
23
18
  include Http
24
19
  include Verbose
25
20
 
21
+ ## Generate an endpoint in Murano
22
+ # Uses the uriparts and path
23
+ # @param path String: any additional parts for the URI
24
+ # @return URI: The full URI for this enpoint.
26
25
  def endPoint(path='')
27
26
  parts = ['https:/', $cfg['net.host'], 'api:1'] + @uriparts
28
27
  s = parts.map{|v| v.to_s}.join('/')
@@ -30,274 +29,7 @@ module MrMurano
30
29
  end
31
30
  # …
32
31
 
33
- ##
34
- # Compute a remote item hash from the local path
35
- # @param root Pathname: Root path for this resource type from config files
36
- # @param path Pathname: Path to local item
37
- # @return Hash: hash of the details for the remote item for this path
38
- def toRemoteItem(root, path)
39
- path = Pathname.new(path) unless path.kind_of? Pathname
40
- root = Pathname.new(root) unless root.kind_of? Pathname
41
- {:name => path.relative_path_from(root).to_s}
42
- end
43
-
44
- ##
45
- # Compute the local name from remote item details
46
- # @param item Hash: listing details for the item.
47
- # @param itemkey Symbol: Key for look up.
48
- def tolocalname(item, itemkey)
49
- item[itemkey]
50
- end
51
-
52
- ##
53
- # Compute the local path from the listing details
54
- #
55
- # If there is already a matching local item, some of its details are also in
56
- # the item hash.
57
- #
58
- # @param into Pathname: Root path for this resource type from config files
59
- # @param item Hash: listing details for the item.
60
- # @return Pathname: path to save (or merge) remote item into
61
- def tolocalpath(into, item)
62
- return item[:local_path] if item.has_key? :local_path
63
- itemkey = @itemkey.to_sym
64
- name = tolocalname(item, itemkey)
65
- raise "Bad key(#{itemkey}) for #{item}" if name.nil?
66
- name = Pathname.new(name) unless name.kind_of? Pathname
67
- name = name.relative_path_from(Pathname.new('/')) if name.absolute?
68
- dest = into + name
69
- end
70
-
71
- ##
72
- # So, for bundles this needs to look at all the places and build up the mered
73
- # stack of local items.
74
- #
75
- # Which means it needs the from to be split into the base and the sub so we can
76
- # inject bundle directories.
77
-
78
- ##
79
- # Get a list of local items.
80
- #
81
- # This collects items in the project and all bundles.
82
- def locallist()
83
- # so. if @locationbase/bundles exists
84
- # gather and merge: @locationbase/bundles/*/@location
85
- # then merge @locationbase/@location
86
- #
87
-
88
- bundleDir = $cfg['location.bundles'] or 'bundles'
89
- bundleDir = 'bundles' if bundleDir.nil?
90
- items = {}
91
- if (@locationbase + bundleDir).directory? then
92
- (@locationbase + bundleDir).children.sort.each do |bndl|
93
- if (bndl + @location).exist? then
94
- verbose("Loading from bundle #{bndl.basename}")
95
- bitems = localitems(bndl + @location)
96
- bitems.map!{|b| b[:bundled] = true; b} # mark items from bundles.
97
-
98
-
99
- # use synckey for quicker merging.
100
- bitems.each { |b| items[synckey(b)] = b }
101
- end
102
- end
103
- end
104
- if (@locationbase + @location).exist? then
105
- bitems = localitems(@locationbase + @location)
106
- # use synckey for quicker merging.
107
- bitems.each { |b| items[synckey(b)] = b }
108
- end
109
-
110
- items.values
111
- end
112
-
113
- ##
114
- # Get a list of local items rooted at #from
115
- def localitems(from)
116
- from.children.map do |path|
117
- if path.directory? then
118
- # TODO: look for definition. ( ?.rockspec? ?mr.modules? ?mr.manifest? )
119
- # Lacking definition, find all *.lua but not *_test.lua
120
- # This specifically and intentionally only goes one level deep.
121
- path.children
122
- else
123
- path
124
- end
125
- end.flatten.compact.reject do |path|
126
- path.fnmatch('*_test.lua') or path.basename.fnmatch('.*')
127
- end.select do |path|
128
- path.extname == '.lua'
129
- end.map do |path|
130
- # sometimes this is a name, sometimes it is an item.
131
- # do I want to keep that? NO.
132
- name = toRemoteItem(from, path)
133
- unless name.nil? then
134
- name[:local_path] = path
135
- name
136
- end
137
- end.flatten.compact
138
- end
139
-
140
- def synckey(item)
141
- key = @itemkey.to_sym
142
- item[key]
143
- end
144
-
145
- def download(local, item)
146
- if item[:bundled] then
147
- say_warning "Not downloading into bundled item #{synckey(item)}"
148
- return
149
- end
150
- local.dirname.mkpath
151
- id = item[@itemkey.to_sym]
152
- local.open('wb') do |io|
153
- fetch(id) do |chunk|
154
- io.write chunk
155
- end
156
- end
157
- end
158
-
159
- def removelocal(dest, item)
160
- dest.unlink
161
- end
162
-
163
- def syncup(options=Commander::Command::Options.new)
164
- itemkey = @itemkey.to_sym
165
- options.asdown=false
166
- dt = status(options)
167
- toadd = dt[:toadd]
168
- todel = dt[:todel]
169
- tomod = dt[:tomod]
170
-
171
- if options.delete then
172
- todel.each do |item|
173
- verbose "Removing item #{item[:synckey]}"
174
- unless $cfg['tool.dry'] then
175
- remove(item[itemkey])
176
- end
177
- end
178
- end
179
- if options.create then
180
- toadd.each do |item|
181
- verbose "Adding item #{item[:synckey]}"
182
- unless $cfg['tool.dry'] then
183
- upload(item[:local_path], item.reject{|k,v| k==:local_path})
184
- end
185
- end
186
- end
187
- if options.update then
188
- tomod.each do |item|
189
- verbose "Updating item #{item[:synckey]}"
190
- unless $cfg['tool.dry'] then
191
- upload(item[:local_path], item.reject{|k,v| k==:local_path})
192
- end
193
- end
194
- end
195
- end
196
-
197
- # FIXME this still needs the path passed in.
198
- # Need to think some more on how syncdown works with bundles.
199
- def syncdown(options=Commander::Command::Options.new)
200
- options.asdown = true
201
- dt = status(options)
202
- into = @locationbase + @location ###
203
- toadd = dt[:toadd]
204
- todel = dt[:todel]
205
- tomod = dt[:tomod]
206
-
207
- if options.delete then
208
- todel.each do |item|
209
- verbose "Removing item #{item[:synckey]}"
210
- unless $cfg['tool.dry'] then
211
- dest = tolocalpath(into, item)
212
- removelocal(dest, item)
213
- end
214
- end
215
- end
216
- if options.create then
217
- toadd.each do |item|
218
- verbose "Adding item #{item[:synckey]}"
219
- unless $cfg['tool.dry'] then
220
- dest = tolocalpath(into, item)
221
- download(dest, item)
222
- end
223
- end
224
- end
225
- if options.update then
226
- tomod.each do |item|
227
- verbose "Updating item #{item[:synckey]}"
228
- unless $cfg['tool.dry'] then
229
- dest = tolocalpath(into, item)
230
- download(dest, item)
231
- end
232
- end
233
- end
234
- end
235
-
236
- ##
237
- # True if itemA and itemB are different
238
- def docmp(itemA, itemB)
239
- true
240
- end
241
-
242
- def dodiff(item)
243
- tfp = Tempfile.new([tolocalname(item, @itemkey), '.lua'])
244
- df = ""
245
- begin
246
- download(Pathname.new(tfp.path), item)
247
-
248
- cmd = $cfg['diff.cmd'].shellsplit
249
- cmd << tfp.path
250
- cmd << item[:local_path].to_s
251
-
252
- IO.popen(cmd) {|io| df = io.read }
253
- ensure
254
- tfp.close
255
- tfp.unlink
256
- end
257
- df
258
- end
259
-
260
- def status(options=Commander::Command::Options.new)
261
- there = list()
262
- here = locallist()
263
- itemkey = @itemkey.to_sym
264
-
265
- therebox = {}
266
- there.each do |item|
267
- item = Hash.transform_keys_to_symbols(item)
268
- item[:synckey] = synckey(item)
269
- therebox[ item[:synckey] ] = item
270
- end
271
- herebox = {}
272
- here.each do |item|
273
- item = Hash.transform_keys_to_symbols(item)
274
- item[:synckey] = synckey(item)
275
- herebox[ item[:synckey] ] = item
276
- end
277
- toadd = []
278
- todel = []
279
- tomod = []
280
- unchg = []
281
- if options.asdown then
282
- todel = (herebox.keys - therebox.keys).map{|key| herebox[key] }
283
- toadd = (therebox.keys - herebox.keys).map{|key| therebox[key] }
284
- else
285
- toadd = (herebox.keys - therebox.keys).map{|key| herebox[key] }
286
- todel = (therebox.keys - herebox.keys).map{|key| therebox[key] }
287
- end
288
- (herebox.keys & therebox.keys).each do |key|
289
- # Want here to override there except for itemkey.
290
- mrg = herebox[key].reject{|k,v| k==itemkey}
291
- mrg = therebox[key].merge(mrg)
292
- if docmp(herebox[key], therebox[key]) then
293
- mrg[:diff] = dodiff(mrg) if options.diff
294
- tomod << mrg
295
- else
296
- unchg << mrg
297
- end
298
- end
299
- { :toadd=>toadd, :todel=>todel, :tomod=>tomod, :unchg=>unchg }
300
- end
32
+ include SyncUpDown
301
33
  end
302
34
 
303
35
  class Solution < SolutionBase