MrMurano 1.3.2 → 1.4.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.
@@ -0,0 +1,113 @@
1
+
2
+ command 'content list' do |c|
3
+ c.syntax = %{mr content list}
4
+ c.summary = %{List downloadable content for a product}
5
+ c.description = %{List downloadable content for a product
6
+
7
+ Data uploaded to a product's content area can be downloaded by devices using the
8
+ HTTP Device API. (http://docs.exosite.com/http/#list-available-content)
9
+ }
10
+ c.action do |args, options|
11
+ prd = MrMurano::ProductContent.new
12
+ prd.list.each{|item| say item}
13
+ end
14
+ end
15
+ alias_command :content, 'content list'
16
+
17
+ command 'content info' do |c|
18
+ c.syntax = %{mr content info <content id>}
19
+ c.summary = %{Show more info for a content item}
20
+ c.description = %{Show more info for a content item
21
+
22
+ Data uploaded to a product's content area can be downloaded by devices using the
23
+ HTTP Device API. (http://docs.exosite.com/http/#list-available-content)
24
+ }
25
+ c.action do |args, options|
26
+ if args[0].nil? then
27
+ say_error "Missing <content id>"
28
+ else
29
+ prd = MrMurano::ProductContent.new
30
+ prd.info(args[0]).each{|line| say "#{args[0]} #{line.join(' ')}"}
31
+ end
32
+ end
33
+ end
34
+
35
+ command 'content delete' do |c|
36
+ c.syntax = %{mr content delete <content id>}
37
+ c.summary = %{Delete a content item}
38
+ c.description = %{Delete a content item
39
+
40
+ Data uploaded to a product's content area can be downloaded by devices using the
41
+ HTTP Device API. (http://docs.exosite.com/http/#list-available-content)
42
+ }
43
+ c.action do |args, options|
44
+ if args[0].nil? then
45
+ say_error "Missing <content id>"
46
+ else
47
+ prd = MrMurano::ProductContent.new
48
+ pp prd.remove(args[0])
49
+ end
50
+ end
51
+ end
52
+
53
+ command 'content upload' do |c|
54
+ c.syntax = %{mr content upload <content id> <file>}
55
+ c.summary = %{Upload content}
56
+ c.description = %{Upload a content item
57
+
58
+ Data uploaded to a product's content area can be downloaded by devices using the
59
+ HTTP Device API. (http://docs.exosite.com/http/#list-available-content)
60
+ }
61
+ c.option '--meta STRING', %{Add extra meta info to the content item}
62
+
63
+ c.action do |args, options|
64
+ options.defaults :meta=>' '
65
+
66
+ if args[0].nil? then
67
+ say_error "Missing <content id>"
68
+ elsif args[1].nil? then
69
+ say_error "Missing <file>"
70
+ else
71
+ prd = MrMurano::ProductContent.new
72
+
73
+ ret = prd.info(args[0])
74
+ if ret.nil? then
75
+ pp prd.create(args[0], options.meta)
76
+ end
77
+
78
+ pp prd.upload(args[0], args[1])
79
+ end
80
+ end
81
+ end
82
+
83
+ command 'content download' do |c|
84
+ c.syntax = %{mr content download <content id>}
85
+ c.summary = %{Download a content item}
86
+ c.description = %{Download a content item
87
+
88
+ Data uploaded to a product's content area can be downloaded by devices using the
89
+ HTTP Device API. (http://docs.exosite.com/http/#list-available-content)
90
+ }
91
+ c.option '-o','--output FILE',%{save to this file}
92
+ c.action do |args, options|
93
+ if args[0].nil? then
94
+ say_error "Missing <content id>"
95
+ else
96
+ prd = MrMurano::ProductContent.new
97
+
98
+ if options.output.nil? then
99
+ prd.download(args[0]) # to stdout
100
+ else
101
+ outFile = Pathname.new(options.output)
102
+ outFile.open('w') do |io|
103
+ prd.download(args[0]) do |chunk|
104
+ io << chunk
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+
113
+ # vim: set ai et sw=2 ts=2 :
@@ -0,0 +1,123 @@
1
+ require 'pathname'
2
+ require 'json'
3
+ require 'fileutils'
4
+
5
+ command 'config export' do |c|
6
+ c.syntax = %{mr config export}
7
+ c.summary = %{Export data to a Solutionfile.json and .Solutionfile.secret}
8
+
9
+ c.option '--force', "Overwrite existing files."
10
+
11
+ c.action do |args, options|
12
+ solfile = Pathname.new($cfg['location.base'] + 'Solutionfile.json')
13
+ solsecret = Pathname.new($cfg['location.base'] + '.Solutionfile.secret')
14
+
15
+ if not options.force and (solfile.exist? or solsecret.exist?) then
16
+ say_error "Solutionfile.json or .Solutionfile.secret already exist."
17
+ say "Use --force to overwrite"
18
+ end
19
+
20
+ epf = 'routes.lua'
21
+ File.open(epf, 'w') do |dst|
22
+ MrMurano::Endpoint.new.locallist.each do |ed|
23
+ ed[:local_path].open do |fin|
24
+ FileUtils.copy_stream(fin, dst)
25
+ end
26
+ dst << "\n\n"
27
+ end
28
+ end
29
+
30
+ solf = {
31
+ :routes => 'epf',
32
+ :assets => $cfg['location.files'],
33
+ :default_page => $cfg['files.default_page'],
34
+ :services => {},
35
+ :modules => {}
36
+ }
37
+
38
+ dpwd = Pathname.new(Dir.pwd)
39
+ MrMurano::EventHandler.new.locallist.each do |ev|
40
+ rp = ev[:local_path].relative_path_from(dpwd).to_s
41
+ if solf[:services].has_key?(ev[:service]) then
42
+ solf[:services][ev[:service]][ev[:event]] = rp
43
+ else
44
+ solf[:services][ev[:service]] = {ev[:event] => rp}
45
+ end
46
+ end
47
+
48
+ MrMurano::Library.new.locallist.each do |lb|
49
+ solf[:modules][lb[:name]] = lb[:local_path].relative_path_from(dpwd).to_s
50
+ end
51
+
52
+ solfile.open('w') do |io|
53
+ io << JSON.pretty_generate(solf)
54
+ end
55
+
56
+ solsecret.open('w') do |io|
57
+ pff = Pathname.new(ENV['HOME']) + '.mrmurano/passwords'
58
+ pwd = MrMurano::Passwords.new(pff)
59
+ pwd.load
60
+ ps = pwd.get($cfg['net.host'], $cfg['user.name'])
61
+ io << {
62
+ :email => $cfg['user.name'],
63
+ :password => ps,
64
+ :solution_id => $cfg['solution.id'],
65
+ :product_id => $cfg['product.id']
66
+ }.to_json
67
+ end
68
+
69
+ end
70
+ end
71
+
72
+ command 'config import' do |c|
73
+ c.syntax = %{mr config import}
74
+ c.summary = %{Import data from a Solutionfile.json and .Solutionfile.secret}
75
+
76
+ c.action do |args, options|
77
+ solfile = ($cfg['location.base'] + 'Solutionfile.json')
78
+ solsecret = ($cfg['location.base'] + '.Solutionfile.secret')
79
+
80
+ if solfile.exist? then
81
+ # Is in JSON, which as a subset of YAML, so use YAML parser
82
+ solfile.open do |io|
83
+ sf = YAML.load(io)
84
+ $cfg.set('location.files', sf['assets']) if sf.has_key? 'assets'
85
+ $cfg.set('location.files', sf['file_dir']) if sf.has_key? 'file_dir'
86
+ $cfg.set('files.default_page', sf['default_page']) if sf.has_key? 'default_page'
87
+ end
88
+ end
89
+
90
+ if solsecret.exist? then
91
+ # Is in JSON, which as a subset of YAML, so use YAML parser
92
+ solsecret.open do |io|
93
+ ss = YAML.load(io)
94
+
95
+ pff = Pathname.new(ENV['HOME']) + '.mrmurano/passwords'
96
+ pwd = MrMurano::Passwords.new(pff)
97
+ pwd.load
98
+ ps = pwd.get($cfg['net.host'], ss['email'])
99
+ if ps.nil? then
100
+ pwd.set($cfg['net.host'], ss['email'], ss['password'])
101
+ pwd.save
102
+ elsif ps != ss['password'] then
103
+ y = ask("A different password for this account already exists. Overwrite? N/y")
104
+ if y =~ /^y/i then
105
+ pwd.set($cfg['net.host'], ss['email'], ss['password'])
106
+ pwd.save
107
+ end
108
+ else
109
+ # already set, nothing to do.
110
+ end
111
+
112
+ $cfg.set('solution.id', ss['solution_id']) if ss.has_key? 'solution_id'
113
+ $cfg.set('solution.id', ss['product_id']) if ss.has_key? 'product_id'
114
+ end
115
+ end
116
+
117
+ say "Configuration items have been imported."
118
+ say "Use `mr syncdown` get get all endpoints, modules, and event handlers"
119
+ end
120
+
121
+ end
122
+
123
+ # vim: set ai et sw=2 ts=2 :
data/lib/MrMurano/http.rb CHANGED
@@ -23,7 +23,10 @@ module MrMurano
23
23
  def curldebug(request)
24
24
  if $cfg['tool.curldebug'] then
25
25
  a = []
26
- a << %{curl -s -H 'Authorization: #{request['authorization']}'}
26
+ a << %{curl -s }
27
+ if request.key?('Authorization') then
28
+ a << %{-H 'Authorization: #{request['Authorization']}'}
29
+ end
27
30
  a << %{-H 'User-Agent: #{request['User-Agent']}'}
28
31
  a << %{-H 'Content-Type: #{request.content_type}'}
29
32
  a << %{-X #{request.method}}
@@ -42,19 +45,21 @@ module MrMurano
42
45
  end
43
46
  @http
44
47
  end
48
+ def http_reset
49
+ @http = nil
50
+ end
45
51
 
46
- def set_req_defaults(request)
52
+ def set_def_headers(request)
47
53
  request.content_type = 'application/json'
48
- request['authorization'] = 'token ' + token
54
+ request['Authorization'] = 'token ' + token
49
55
  request['User-Agent'] = "MrMurano/#{MrMurano::VERSION}"
50
56
  request
51
57
  end
52
58
 
53
59
  def workit(request, &block)
54
- set_req_defaults(request)
55
60
  curldebug(request)
56
61
  if block_given? then
57
- yield request, http()
62
+ return yield request, http()
58
63
  else
59
64
  response = http().request(request)
60
65
  case response
@@ -76,26 +81,37 @@ module MrMurano
76
81
 
77
82
  def get(path='', &block)
78
83
  uri = endPoint(path)
79
- workit(Net::HTTP::Get.new(uri), &block)
84
+ workit(set_def_headers(Net::HTTP::Get.new(uri)), &block)
80
85
  end
81
86
 
82
87
  def post(path='', body={}, &block)
83
88
  uri = endPoint(path)
84
89
  req = Net::HTTP::Post.new(uri)
90
+ set_def_headers(req)
85
91
  req.body = JSON.generate(body)
86
92
  workit(req, &block)
87
93
  end
88
94
 
95
+ def postf(path='', form={}, &block)
96
+ uri = endPoint(path)
97
+ req = Net::HTTP::Post.new(uri)
98
+ set_def_headers(req)
99
+ req.content_type = 'application/x-www-form-urlencoded; charset=utf-8'
100
+ req.form_data = form
101
+ workit(req, &block)
102
+ end
103
+
89
104
  def put(path='', body={}, &block)
90
105
  uri = endPoint(path)
91
106
  req = Net::HTTP::Put.new(uri)
107
+ set_def_headers(req)
92
108
  req.body = JSON.generate(body)
93
109
  workit(req, &block)
94
110
  end
95
111
 
96
112
  def delete(path='', &block)
97
113
  uri = endPoint(path)
98
- workit(Net::HTTP::Delete.new(uri), &block)
114
+ workit(set_def_headers(Net::HTTP::Delete.new(uri)), &block)
99
115
  end
100
116
 
101
117
  end
@@ -68,7 +68,7 @@ end
68
68
 
69
69
  command 'keystore set' do |c|
70
70
  c.syntax = %{mr keystore set <key> <value...>}
71
- c.description = %{Set teh value of a key in the Keystore}
71
+ c.description = %{Set the value of a key in the Keystore}
72
72
  c.action do |args,options|
73
73
  sol = MrMurano::Keystore.new
74
74
  sol.setkey(args[0], args[1..-1].join(' '))
@@ -0,0 +1,17 @@
1
+ require 'pp'
2
+
3
+ module MrMurano
4
+ module Verbose
5
+ def verbose(msg)
6
+ if $cfg['tool.verbose'] then
7
+ say msg
8
+ end
9
+ end
10
+ def debug(msg)
11
+ if $cfg['tool.debug'] then
12
+ say msg
13
+ end
14
+ end
15
+ end
16
+ end
17
+ # vim: set ai et sw=2 ts=2 :
@@ -1,4 +1,4 @@
1
1
  module MrMurano
2
- VERSION = '1.3.2'.freeze
2
+ VERSION = '1.4.0'.freeze
3
3
  end
4
4
 
data/lib/MrMurano.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'MrMurano/version'
2
+ require 'MrMurano/verbosing'
2
3
  require 'MrMurano/hash.rb'
3
4
  require 'MrMurano/http'
4
5
  require 'MrMurano/configFile'
@@ -9,8 +10,12 @@ require 'MrMurano/Solution-File.rb'
9
10
  require 'MrMurano/Solution-Services.rb'
10
11
  require 'MrMurano/Solution-Users.rb'
11
12
  require 'MrMurano/Solution-ServiceConfig.rb'
13
+ require 'MrMurano/Product'
12
14
 
15
+ require 'MrMurano/configCommand'
16
+ require 'MrMurano/contentCommand'
13
17
  require 'MrMurano/cors'
18
+ require 'MrMurano/exportImport'
14
19
  require 'MrMurano/keystore.rb'
15
20
  require 'MrMurano/logs.rb'
16
21
  require 'MrMurano/sync.rb'
@@ -0,0 +1,121 @@
1
+ require 'rubygems'
2
+ require 'commander/import'
3
+ require 'MrMurano'
4
+ require 'tempfile'
5
+
6
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
7
+ # Some kludgery to work around commander. Likely a better way to do this.
8
+ program :version, MrMurano::VERSION
9
+ program :description, %{Manage a Solution in Exosite's Murano}
10
+ command(:nop){ |c| c.action {|a,o|} }
11
+ default_command :nop
12
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
13
+
14
+ RSpec.describe MrMurano::Passwords, "#pwd" do
15
+ it "Creates a file " do
16
+ tmpfile = Dir.tmpdir + '/pwtest' # This way because Tempfile.new creates.
17
+ begin
18
+ pwd = MrMurano::Passwords.new( tmpfile )
19
+ pwd.save
20
+
21
+ expect( FileTest.exists?(tmpfile) )
22
+ ensure
23
+ File.unlink(tmpfile) if File.exists? tmpfile
24
+ end
25
+ end
26
+
27
+ it "Loads a file" do
28
+ Tempfile.open('test') do |tf|
29
+ tf << %{---
30
+ this.is.a.host:
31
+ user: password
32
+ }
33
+ tf.close
34
+
35
+ pwd = MrMurano::Passwords.new( tf.path )
36
+ pwd.load
37
+ ps = pwd.get('this.is.a.host', 'user')
38
+ expect(ps).to eq('password')
39
+ end
40
+ end
41
+
42
+ it "Saves a file" do
43
+ Tempfile.open('pstest') do |tf|
44
+ tf.close
45
+
46
+ pwd = MrMurano::Passwords.new(tf.path)
47
+ pwd.set('this.is.a.host', 'user3', 'passwords4')
48
+ pwd.save
49
+
50
+ File.open(tf.path) do |io|
51
+ data = io.read
52
+ expect(data).to eq(%{---
53
+ this.is.a.host:
54
+ user3: passwords4
55
+ })
56
+ end
57
+ end
58
+ end
59
+
60
+ it "Writes multiple hosts" do
61
+ Tempfile.open('pwtest') do |tf|
62
+ tf.close
63
+
64
+ pwd = MrMurano::Passwords.new(tf.path)
65
+ pwd.set('this.is.a.host', 'user3', 'passwords4')
66
+ pwd.save
67
+ pwd = nil
68
+
69
+ pwd = MrMurano::Passwords.new(tf.path)
70
+ pwd.load
71
+ ps = pwd.get('this.is.a.host', 'user3')
72
+ expect(ps).to eq('passwords4')
73
+ pwd = nil
74
+
75
+ pwd = MrMurano::Passwords.new(tf.path)
76
+ pwd.load
77
+ pwd.set('another.host', 'user9', 'passwords2')
78
+ pwd.save
79
+ pwd = nil
80
+
81
+ pwd = MrMurano::Passwords.new(tf.path)
82
+ pwd.load
83
+ ps = pwd.get('this.is.a.host', 'user3')
84
+ expect(ps).to eq('passwords4')
85
+ ps = pwd.get('another.host', 'user9')
86
+ expect(ps).to eq('passwords2')
87
+ pwd = nil
88
+
89
+ end
90
+ end
91
+
92
+ it "Write multiple users to same host" do
93
+ Tempfile.open('pwstest') do |tf|
94
+ tf.close
95
+
96
+ pwd = MrMurano::Passwords.new(tf.path)
97
+ pwd.set('this.is.a.host', 'user3', 'passwords4')
98
+ pwd.save
99
+ pwd = nil
100
+
101
+ pwd = MrMurano::Passwords.new(tf.path)
102
+ pwd.load
103
+ pwd.set('this.is.a.host', 'user9', 'passwords2')
104
+ pwd.save
105
+ pwd = nil
106
+
107
+ pwd = MrMurano::Passwords.new(tf.path)
108
+ pwd.load
109
+ ps = pwd.get('this.is.a.host', 'user3')
110
+ expect(ps).to eq('passwords4')
111
+ ps = pwd.get('this.is.a.host', 'user9')
112
+ expect(ps).to eq('passwords2')
113
+ pwd = nil
114
+
115
+ end
116
+ end
117
+
118
+
119
+ end
120
+
121
+ # vim: set ai et sw=2 ts=2 :
@@ -0,0 +1,112 @@
1
+ require 'MrMurano/version'
2
+ require 'MrMurano/verbosing'
3
+ require 'MrMurano/http'
4
+ require 'MrMurano/Product'
5
+ require 'MrMurano/configFile'
6
+
7
+ RSpec.describe MrMurano::ProductBase, "#product_base" do
8
+ before(:example) do
9
+ $cfg = MrMurano::Config.new
10
+ $cfg.load
11
+ $cfg['net.host'] = 'bizapi.hosted.exosite.io'
12
+ $cfg['product.id'] = 'XYZ'
13
+
14
+ @prd = MrMurano::ProductBase.new
15
+ allow(@prd).to receive(:token).and_return("TTTTTTTTTT")
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/")
21
+ end
22
+
23
+ it "can get" do
24
+ stub_request(:get, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/").
25
+ with(:headers=>{'Authorization'=>'token TTTTTTTTTT',
26
+ 'Content-Type'=>'application/json'}).
27
+ to_return(body: "Fooo")
28
+
29
+ ret = @prd.get('/')
30
+ expect(ret).to eq("Fooo")
31
+ end
32
+
33
+ it "returns hash when getting empty body" do
34
+ stub_request(:get, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/").
35
+ with(:headers=>{'Authorization'=>'token TTTTTTTTTT',
36
+ 'Content-Type'=>'application/json'}).
37
+ to_return(body: "")
38
+
39
+ ret = @prd.get('/')
40
+ expect(ret).to eq({})
41
+ end
42
+
43
+ it "auto parses JSON responses" do
44
+ stub_request(:get, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/").
45
+ with(:headers=>{'Authorization'=>'token TTTTTTTTTT',
46
+ 'Content-Type'=>'application/json'}).
47
+ to_return(body: %{{"first": "str", "sec":[1,2,3], "third":{"a":"b"}}})
48
+
49
+ ret = @prd.get('/')
50
+ expect(ret).to eq({:sec=>[1,2,3],:third=>{:a=>'b'},:first=>'str'})
51
+ end
52
+
53
+ it "can post nothing" do
54
+ stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/").
55
+ with(:headers=>{'Authorization'=>'token TTTTTTTTTT',
56
+ 'Content-Type'=>'application/json'}).
57
+ to_return(body: "")
58
+ ret = @prd.post('/')
59
+ expect(ret).to eq({})
60
+ end
61
+
62
+ it "can post json" do
63
+ stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/").
64
+ with(headers: {'Authorization'=>'token TTTTTTTTTT',
65
+ 'Content-Type'=>'application/json'},
66
+ body: {:this=>"is", :a=>'test'}).
67
+ to_return(body: "")
68
+ ret = @prd.post('/', {:this=>"is", :a=>'test'})
69
+ expect(ret).to eq({})
70
+ end
71
+
72
+ it "can post form data" do
73
+ stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/").
74
+ with(headers: {'Authorization'=>'token TTTTTTTTTT',
75
+ 'Content-Type'=>'application/x-www-form-urlencoded'},
76
+ body: "this=is&a=test").
77
+ to_return(body: "")
78
+ ret = @prd.postf('/', {:this=>"is", :a=>'test'})
79
+ expect(ret).to eq({})
80
+ end
81
+
82
+ it "can put nothing" do
83
+ stub_request(:put, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/").
84
+ with(:headers=>{'Authorization'=>'token TTTTTTTTTT',
85
+ 'Content-Type'=>'application/json'}).
86
+ to_return(body: "")
87
+ ret = @prd.put('/')
88
+ expect(ret).to eq({})
89
+ end
90
+
91
+ it "can put json" do
92
+ stub_request(:put, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/").
93
+ with(headers: {'Authorization'=>'token TTTTTTTTTT',
94
+ 'Content-Type'=>'application/json'},
95
+ body: {:this=>"is", :a=>'test'}).
96
+ to_return(body: "")
97
+ ret = @prd.put('/', {:this=>"is", :a=>'test'})
98
+ expect(ret).to eq({})
99
+ end
100
+
101
+ it "can delete" do
102
+ stub_request(:delete, "https://bizapi.hosted.exosite.io/api:1/product/XYZ/").
103
+ with(headers: {'Authorization'=>'token TTTTTTTTTT',
104
+ 'Content-Type'=>'application/json'}).
105
+ to_return(body: "")
106
+ ret = @prd.delete('/')
107
+ expect(ret).to eq({})
108
+ end
109
+
110
+ end
111
+
112
+ # vim: set ai et sw=2 ts=2 :