MuranoCLI 2.0.0 → 2.1.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/.gitignore +4 -0
- data/Gemfile +3 -2
- data/MuranoCLI.gemspec +2 -2
- data/README.markdown +107 -10
- data/lib/MrMurano/Product-Resources.rb +10 -0
- data/lib/MrMurano/ProjectFile.rb +7 -0
- data/lib/MrMurano/Solution-Endpoint.rb +24 -12
- data/lib/MrMurano/Solution-File.rb +17 -5
- data/lib/MrMurano/Solution-ServiceConfig.rb +7 -5
- data/lib/MrMurano/Solution-Services.rb +61 -31
- data/lib/MrMurano/SyncUpDown.rb +200 -49
- data/lib/MrMurano/commands/gb.rb +4 -3
- data/lib/MrMurano/commands/init.rb +55 -3
- data/lib/MrMurano/commands/login.rb +20 -0
- data/lib/MrMurano/commands/password.rb +12 -9
- data/lib/MrMurano/commands/status.rb +5 -1
- data/lib/MrMurano/commands.rb +1 -1
- data/lib/MrMurano/version.rb +1 -1
- data/spec/Account_spec.rb +8 -0
- data/spec/ConfigMigrate_spec.rb +3 -0
- data/spec/Config_spec.rb +16 -1
- data/spec/Solution-Endpoint_spec.rb +28 -20
- data/spec/Solution-ServiceEventHandler_spec.rb +27 -21
- data/spec/Solution-ServiceModules_spec.rb +42 -43
- data/spec/SyncUpDown_spec.rb +75 -68
- data/spec/cmd_init_spec.rb +303 -9
- data/spec/cmd_status_spec.rb +14 -12
- data/spec/spec_helper.rb +4 -1
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 06a751fa4b7c0b5cd5ee4c3ec8e5075d915b31bf
|
4
|
+
data.tar.gz: d3d7f35806143bdea0849b71b8117626dcf58727
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 26f18f7661d62fda5df06fad381f0c1a81286027bf91dcd1da991973cb43489947a257dcffd9d69a4454247ac6a095390147cc993c51d180255d9376c724a152
|
7
|
+
data.tar.gz: 03829cb88d7211ddab47e1c0dc9a9ceed6a8858cf0d320e409a97a1acda999c08548c4f8615768f30a24246532f6bc94d9ef232a4f3e1ba98df71d25fcfc2b84
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
@@ -2,7 +2,7 @@ source 'http://rubygems.org'
|
|
2
2
|
|
3
3
|
#gemspec
|
4
4
|
|
5
|
-
gem 'commander', '~> 4.4.
|
5
|
+
gem 'commander', '~> 4.4.3'
|
6
6
|
gem 'certified', '1.0.0'
|
7
7
|
gem 'dotenv', '~> 2.1.1'
|
8
8
|
gem 'highline', '~> 1.7.8'
|
@@ -11,7 +11,7 @@ gem 'inifile', '~> 3.0'
|
|
11
11
|
gem 'json-schema', '~> 2.7.0'
|
12
12
|
gem 'mime-types', '~> 3.1'
|
13
13
|
gem 'mime-types-data', '~> 3.2016.0521'
|
14
|
-
gem 'terminal-table', '~> 1.
|
14
|
+
gem 'terminal-table', '~> 1.7.3'
|
15
15
|
|
16
16
|
group :test do
|
17
17
|
gem 'coderay', :require => false
|
@@ -19,6 +19,7 @@ group :test do
|
|
19
19
|
gem 'rspec', '~> 3.5'
|
20
20
|
gem 'simplecov', :require => false
|
21
21
|
gem 'webmock', '~> 2.3.0'
|
22
|
+
gem 'yard'
|
22
23
|
end
|
23
24
|
|
24
25
|
group :windows do
|
data/MuranoCLI.gemspec
CHANGED
@@ -27,7 +27,7 @@ Gem::Specification.new do |s|
|
|
27
27
|
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
28
28
|
s.require_paths = ['lib']
|
29
29
|
|
30
|
-
s.add_runtime_dependency('commander', '~> 4.4.
|
30
|
+
s.add_runtime_dependency('commander', '~> 4.4.3')
|
31
31
|
s.add_runtime_dependency('certified', '1.0.0')
|
32
32
|
s.add_runtime_dependency('dotenv', '~> 2.1.1')
|
33
33
|
s.add_runtime_dependency('highline', '~> 1.7.8')
|
@@ -36,7 +36,7 @@ Gem::Specification.new do |s|
|
|
36
36
|
s.add_runtime_dependency('json-schema', '~> 2.7.0')
|
37
37
|
s.add_runtime_dependency('mime-types', '~> 3.1')
|
38
38
|
s.add_runtime_dependency('mime-types-data', '~> 3.2016.0521')
|
39
|
-
s.add_runtime_dependency('terminal-table', '~> 1.
|
39
|
+
s.add_runtime_dependency('terminal-table', '~> 1.7.3')
|
40
40
|
|
41
41
|
s.add_development_dependency('bundler', '~> 1.7.6')
|
42
42
|
s.add_development_dependency('ocra', '~> 1.3.8')
|
data/README.markdown
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
#
|
1
|
+
# Murano Command Line Interface (CLI)
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/MuranoCLI)
|
5
|
-
[](https://travis-ci.org/exosite/MuranoCLI)
|
6
6
|
[](http://inch-ci.org/github/exosite/MuranoCLI)
|
7
7
|
|
8
|
-
Do more from the command line with [Murano](https://exosite.com/platform/)
|
8
|
+
Do more from the command line with [Murano](https://exosite.com/platform/).
|
9
9
|
|
10
10
|
MuranoCLI is the command-line tool that interacts with Murano and makes different
|
11
11
|
tasks easier. MuranoCLI makes it easy to deploy code to a solution, import many
|
@@ -17,18 +17,24 @@ directory are synced up (or down) from Murano.
|
|
17
17
|
|
18
18
|
## Usage
|
19
19
|
|
20
|
-
### To start from an existing project in Murano
|
21
20
|
```
|
22
21
|
mkdir myproject
|
23
22
|
cd myproject
|
24
|
-
murano
|
25
|
-
murano syncdown -V
|
23
|
+
murano init
|
26
24
|
```
|
27
25
|
|
28
|
-
|
26
|
+
Update `myproject.murano` with the info about your project.
|
27
|
+
|
28
|
+
If this is a new project, you will also need to run `murano assign set` to connect
|
29
|
+
the product and solution.
|
30
|
+
|
31
|
+
If this is an existing project, you want to run `murano syncdown -V` after
|
32
|
+
running `murano init`.
|
33
|
+
|
34
|
+
Now do stuff, see what changed: `murano status` or `murano diff`.
|
29
35
|
Then deploy with `murano syncup`
|
30
36
|
|
31
|
-
### To start a brand new project
|
37
|
+
### To start a brand new project the hard way.
|
32
38
|
There are a few steps and pieces to getting a solution with a product up and
|
33
39
|
running in Murano. Here is the list.
|
34
40
|
|
@@ -53,7 +59,7 @@ Then deploy with `murano syncup`
|
|
53
59
|
When upgrading from a 1.\* version to a 2.0, you should uninstall the old versions
|
54
60
|
first.
|
55
61
|
```
|
56
|
-
> gem uninstall MuranoCLI
|
62
|
+
> gem uninstall MuranoCLI
|
57
63
|
```
|
58
64
|
|
59
65
|
And then install:
|
@@ -91,6 +97,8 @@ installing the new version.
|
|
91
97
|
|
92
98
|
## Features
|
93
99
|
|
100
|
+
### Project File
|
101
|
+
|
94
102
|
### Logs
|
95
103
|
|
96
104
|
You can monitor the log messages from your solution with the `murano logs --follow`.
|
@@ -103,6 +111,95 @@ MuranoCLI does a few things to make your log output easier to follow.
|
|
103
111
|
|
104
112
|
All of these can be toggled with command line options.
|
105
113
|
|
114
|
+
### CORS
|
115
|
+
|
116
|
+
If you are developing you UI on seperate services and you need cross-origin
|
117
|
+
resource sharing, you will need to set the
|
118
|
+
[CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) options.
|
119
|
+
|
120
|
+
The current CORS options can be fetched with `murano cors`
|
121
|
+
|
122
|
+
There are three opitons for setting, the first and preferred way is to put your CORS
|
123
|
+
opitons into a file named `cors.yaml`.
|
124
|
+
|
125
|
+
Second and third are to put the CORS opitons in your project file. In the `routes`
|
126
|
+
section, add a `cors` sub-section with either the name of the file to read, or the
|
127
|
+
CORS options inline.
|
128
|
+
|
129
|
+
```yaml
|
130
|
+
routes:
|
131
|
+
cors: my_cors_file.json
|
132
|
+
```
|
133
|
+
OR:
|
134
|
+
```yaml
|
135
|
+
routes:
|
136
|
+
cors: {"origin": true}
|
137
|
+
```
|
138
|
+
|
139
|
+
Then use `murano cors set` to push these options up to your solution.
|
140
|
+
|
141
|
+
### Writing Routes (or endpoints)
|
142
|
+
|
143
|
+
All of the routes that you create in your solution are identified by their method
|
144
|
+
and path. You set this with the following line:
|
145
|
+
|
146
|
+
```lua
|
147
|
+
--#ENDPOINT METHOD PATH
|
148
|
+
```
|
149
|
+
|
150
|
+
Optionally, you can set what the expected content type is too. (If you don't set
|
151
|
+
this, the value is application/json)
|
152
|
+
|
153
|
+
```lua
|
154
|
+
--#ENDPOINT METHOD PATH CONTENT_TYPE
|
155
|
+
```
|
156
|
+
|
157
|
+
An example of a route that puts csv data:
|
158
|
+
```lua
|
159
|
+
--#ENDPOINT PUT /api/upload text/csv
|
160
|
+
```
|
161
|
+
|
162
|
+
After this header line, the script to handle the route follows. Since many routes
|
163
|
+
end up being a couple of lines or less, you can put multiple routes into a single
|
164
|
+
file.
|
165
|
+
|
166
|
+
Which looks like this:
|
167
|
+
```lua
|
168
|
+
--#ENDPOINT GET /api/somedata
|
169
|
+
return Tsdb.query(…)
|
170
|
+
|
171
|
+
--#ENDPOINT PUT /api/somedata text/csv
|
172
|
+
return myimport_module.import(request)
|
173
|
+
|
174
|
+
--#ENDPOINT DELETE /api/startover
|
175
|
+
return Tsdb.deleteAll()
|
176
|
+
```
|
177
|
+
|
178
|
+
### Writing Service Event Handlers
|
179
|
+
|
180
|
+
All of the event handlers you add to your solution are identified by which service
|
181
|
+
they are watching and which event in that service triggers the script.
|
182
|
+
|
183
|
+
This is set with the following line:
|
184
|
+
```lua
|
185
|
+
--#EVENT SERVICE EVENT
|
186
|
+
```
|
187
|
+
|
188
|
+
For example, the event handler that processes all data coming from your devices
|
189
|
+
could be:
|
190
|
+
```lua
|
191
|
+
--#EVENT device datapoint
|
192
|
+
local stamped = nil
|
193
|
+
if data.api == "record" then
|
194
|
+
stamped = tostring(data.value[1]) .. 's'
|
195
|
+
end
|
196
|
+
Tsdb.write{
|
197
|
+
tags = {sn=data.device_sn},
|
198
|
+
metrics = {[data.alias] = tonumber(data.value[2])},
|
199
|
+
ts = stamped
|
200
|
+
}
|
201
|
+
```
|
202
|
+
|
106
203
|
### MURANO_CONFIGFILE environment and Dotenv
|
107
204
|
|
108
205
|
The environment variable `MURANO_CONFIGFILE` is checked for an additional config to
|
@@ -199,7 +296,7 @@ tests work with the live Murano servers (`--tag needs_password`).
|
|
199
296
|
|
200
297
|
To use the live tests, the following environment variables need to be set:
|
201
298
|
- `MURANO_USER` : User name to log into Murano with
|
202
|
-
- `
|
299
|
+
- `MURANO_PASSWORD` : Password for that user
|
203
300
|
- `MURANO_BUSINESS` : Business id to run tests within.
|
204
301
|
|
205
302
|
A free account on Murano is sufficient for these tests.
|
@@ -11,6 +11,16 @@ module MrMurano
|
|
11
11
|
include SyncUpDown
|
12
12
|
include ProductOnePlatformRpcShim
|
13
13
|
|
14
|
+
# Resource Specific details on an Item
|
15
|
+
class ResourceItem < Item
|
16
|
+
# @return [String] Reasource Identifier, internal use only.
|
17
|
+
attr_accessor :rid
|
18
|
+
# @return [String] The name of this resource
|
19
|
+
attr_accessor :alias
|
20
|
+
# @return [String] The format of thie resource.
|
21
|
+
attr_accessor :format
|
22
|
+
end
|
23
|
+
|
14
24
|
def initialize
|
15
25
|
super
|
16
26
|
@uriparts << :proxy
|
data/lib/MrMurano/ProjectFile.rb
CHANGED
@@ -12,6 +12,8 @@ module MrMurano
|
|
12
12
|
class ProjectFile
|
13
13
|
include Verbose
|
14
14
|
|
15
|
+
attr_reader :usingProjectfile, :usingSolutionfile
|
16
|
+
|
15
17
|
# Methods that are common to various internal structs.
|
16
18
|
module PrjStructCommonMethods
|
17
19
|
## Load data from Hash into self
|
@@ -85,6 +87,8 @@ module MrMurano
|
|
85
87
|
end
|
86
88
|
|
87
89
|
def initialize()
|
90
|
+
@usingProjectfile = false
|
91
|
+
@usingSolutionfile = false
|
88
92
|
@prjFile = nil
|
89
93
|
tname = $cfg['location.base'].basename.to_s.gsub(/[^A-Za-z0-9]/, '')
|
90
94
|
@data = PrfFile.new(
|
@@ -259,6 +263,7 @@ module MrMurano
|
|
259
263
|
schema = YAML.load_file(schemaPath.to_s)
|
260
264
|
v = JSON::Validator.fully_validate(schema, data)
|
261
265
|
return v unless v.empty?
|
266
|
+
@usingProjectfile = true
|
262
267
|
|
263
268
|
@data.each_pair do |key, str|
|
264
269
|
str.load(data[key]) if data.has_key? key
|
@@ -277,6 +282,7 @@ module MrMurano
|
|
277
282
|
schema = YAML.load_file(schemaPath.to_s)
|
278
283
|
v = JSON::Validator.fully_validate(schema, data)
|
279
284
|
return v unless v.empty?
|
285
|
+
@usingSolutionfile = true
|
280
286
|
|
281
287
|
ifset(data, :default_page, @data[:assets], :default_page)
|
282
288
|
ifset(data, :file_dir, @data[:assets], :location)
|
@@ -318,6 +324,7 @@ module MrMurano
|
|
318
324
|
schema = YAML.load_file(schemaPath.to_s)
|
319
325
|
v = JSON::Validator.fully_validate(schema, data)
|
320
326
|
return v unless v.empty?
|
327
|
+
@usingSolutionfile = true
|
321
328
|
|
322
329
|
ifset(data, :default_page, @data[:assets], :default_page)
|
323
330
|
ifset(data, :assets, @data[:assets], :location)
|
@@ -7,6 +7,18 @@ require 'MrMurano/Solution'
|
|
7
7
|
module MrMurano
|
8
8
|
# …/endpoint
|
9
9
|
class Endpoint < SolutionBase
|
10
|
+
# Route Specific details on an Item
|
11
|
+
class RouteItem < Item
|
12
|
+
# @return [String] HTTP method for this endpoint
|
13
|
+
attr_accessor :method
|
14
|
+
# @return [String] path for URL maps to this endpoint
|
15
|
+
attr_accessor :path
|
16
|
+
# @return [String] Acceptable Content-Type for this endpoint
|
17
|
+
attr_accessor :content_type
|
18
|
+
# ???? What is this?
|
19
|
+
attr_accessor :use_basic_auth
|
20
|
+
end
|
21
|
+
|
10
22
|
def initialize
|
11
23
|
super
|
12
24
|
@uriparts << 'endpoint'
|
@@ -23,7 +35,7 @@ module MrMurano
|
|
23
35
|
item[:content_type] = 'application/json'
|
24
36
|
end
|
25
37
|
# XXX should this update the script header?
|
26
|
-
item
|
38
|
+
RouteItem.new(item)
|
27
39
|
end
|
28
40
|
end
|
29
41
|
|
@@ -64,20 +76,20 @@ module MrMurano
|
|
64
76
|
|
65
77
|
##
|
66
78
|
# Upload endpoint
|
67
|
-
#
|
68
|
-
#
|
69
|
-
# @param modify
|
79
|
+
# @param local [Pathname] path to file to push
|
80
|
+
# @param remote [RouteItem] of method and endpoint path
|
81
|
+
# @param modify [Boolean] True if item exists already and this is changing it
|
70
82
|
def upload(local, remote, modify)
|
71
83
|
local = Pathname.new(local) unless local.kind_of? Pathname
|
72
84
|
raise "no file" unless local.exist?
|
73
85
|
|
74
86
|
# we assume these are small enough to slurp.
|
75
|
-
|
87
|
+
if remote.script.nil? then
|
76
88
|
script = local.read
|
77
89
|
remote[:script] = script
|
78
90
|
end
|
79
91
|
limitkeys = [:method, :path, :script, :content_type, @itemkey]
|
80
|
-
remote = remote.select{|k,v| limitkeys.include? k }
|
92
|
+
remote = remote.to_h.select{|k,v| limitkeys.include? k }
|
81
93
|
# post('', remote)
|
82
94
|
if remote.has_key? @itemkey then
|
83
95
|
put('/' + remote[@itemkey], remote) do |request, http|
|
@@ -124,12 +136,12 @@ module MrMurano
|
|
124
136
|
# header line.
|
125
137
|
cur[:line_end] = lineno unless cur.nil?
|
126
138
|
items << cur unless cur.nil?
|
127
|
-
cur =
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
139
|
+
cur = RouteItem.new(:method=>md[:method],
|
140
|
+
:path=>md[:path],
|
141
|
+
:content_type=> (md[:ctype] or 'application/json'),
|
142
|
+
:local_path=>path,
|
143
|
+
:line=>lineno,
|
144
|
+
:script=>line)
|
133
145
|
elsif not cur.nil? and not cur[:script].nil? then
|
134
146
|
cur[:script] << line
|
135
147
|
end
|
@@ -9,6 +9,15 @@ require 'MrMurano/Solution'
|
|
9
9
|
module MrMurano
|
10
10
|
# …/file
|
11
11
|
class File < SolutionBase
|
12
|
+
# File Specific details on an Item
|
13
|
+
class FileItem < Item
|
14
|
+
# @return [String] path for URL maps to this static file
|
15
|
+
attr_accessor :path
|
16
|
+
# @return [String] The MIME-Type for this content
|
17
|
+
attr_accessor :mime_type
|
18
|
+
# @return [String] Checksum for the content.
|
19
|
+
attr_accessor :checksum
|
20
|
+
end
|
12
21
|
def initialize
|
13
22
|
super
|
14
23
|
@uriparts << 'file'
|
@@ -19,7 +28,7 @@ module MrMurano
|
|
19
28
|
##
|
20
29
|
# Get a list of all of the static content
|
21
30
|
def list
|
22
|
-
get()
|
31
|
+
get().map{|i| FileItem.new(i)}
|
23
32
|
end
|
24
33
|
|
25
34
|
##
|
@@ -47,8 +56,8 @@ module MrMurano
|
|
47
56
|
##
|
48
57
|
# Delete a file
|
49
58
|
def remove(path)
|
50
|
-
|
51
|
-
delete('/'+path)
|
59
|
+
path = path[1..-1] if path[0] == '/'
|
60
|
+
delete('/'+ URI.encode_www_form_component(path))
|
52
61
|
end
|
53
62
|
|
54
63
|
def curldebug(request)
|
@@ -65,7 +74,10 @@ module MrMurano
|
|
65
74
|
def upload(local, remote, modify)
|
66
75
|
local = Pathname.new(local) unless local.kind_of? Pathname
|
67
76
|
|
68
|
-
|
77
|
+
path = remote[:path]
|
78
|
+
path = path[1..-1] if path[0] == '/'
|
79
|
+
uri = endPoint('upload/' + URI.encode_www_form_component(path))
|
80
|
+
|
69
81
|
# kludge past for a bit.
|
70
82
|
#`curl -s -H 'Authorization: token #{@token}' '#{uri.to_s}' -F file=@#{local.to_s}`
|
71
83
|
|
@@ -132,7 +144,7 @@ module MrMurano
|
|
132
144
|
end
|
133
145
|
debug "Checking #{name} (#{mime.simplified} #{sha1.hexdigest})"
|
134
146
|
|
135
|
-
|
147
|
+
FileItem.new(:path=>name, :mime_type=>mime.simplified, :checksum=>sha1.hexdigest)
|
136
148
|
end
|
137
149
|
|
138
150
|
def synckey(item)
|
@@ -94,15 +94,17 @@ module MrMurano
|
|
94
94
|
end
|
95
95
|
|
96
96
|
## Get list of call operations from a schema
|
97
|
-
def callable(id=sid)
|
97
|
+
def callable(id=sid, all=false)
|
98
98
|
scm = schema(id)
|
99
99
|
calls = []
|
100
100
|
scm[:paths].each do |path, methods|
|
101
101
|
methods.each do |method, params|
|
102
|
-
if params.kind_of?(Hash)
|
103
|
-
|
104
|
-
|
105
|
-
|
102
|
+
if params.kind_of?(Hash) then
|
103
|
+
call = [method]
|
104
|
+
call << path.to_s if all
|
105
|
+
call << params[:operationId]
|
106
|
+
call << (params['x-internal-use'.to_sym] or false) if all
|
107
|
+
calls << call
|
106
108
|
end
|
107
109
|
end
|
108
110
|
end
|