MuranoCLI 3.0.0 → 3.0.1
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/.rubocop.yml +50 -27
- data/.trustme.vim +12 -8
- data/bin/murano +23 -8
- data/docs/basic_example.rst +1 -1
- data/docs/completions/murano_completion-bash +88 -0
- data/lib/MrMurano/Account.rb +3 -3
- data/lib/MrMurano/Business.rb +6 -6
- data/lib/MrMurano/Config-Migrate.rb +1 -3
- data/lib/MrMurano/Config.rb +16 -8
- data/lib/MrMurano/Content.rb +56 -45
- data/lib/MrMurano/Gateway.rb +62 -21
- data/lib/MrMurano/Mock.rb +27 -19
- data/lib/MrMurano/Passwords.rb +7 -7
- data/lib/MrMurano/ReCommander.rb +171 -28
- data/lib/MrMurano/Setting.rb +38 -40
- data/lib/MrMurano/Solution-ServiceConfig.rb +2 -1
- data/lib/MrMurano/Solution-Services.rb +196 -61
- data/lib/MrMurano/Solution-Users.rb +7 -7
- data/lib/MrMurano/Solution.rb +22 -8
- data/lib/MrMurano/SolutionId.rb +10 -4
- data/lib/MrMurano/SubCmdGroupContext.rb +14 -5
- data/lib/MrMurano/SyncAllowed.rb +42 -0
- data/lib/MrMurano/SyncUpDown.rb +122 -65
- data/lib/MrMurano/Webservice-Cors.rb +9 -3
- data/lib/MrMurano/Webservice-Endpoint.rb +39 -33
- data/lib/MrMurano/Webservice-File.rb +35 -24
- data/lib/MrMurano/commands/business.rb +18 -18
- data/lib/MrMurano/commands/content.rb +3 -2
- data/lib/MrMurano/commands/devices.rb +137 -22
- data/lib/MrMurano/commands/globals.rb +8 -2
- data/lib/MrMurano/commands/keystore.rb +3 -2
- data/lib/MrMurano/commands/link.rb +13 -13
- data/lib/MrMurano/commands/login.rb +3 -2
- data/lib/MrMurano/commands/mock.rb +4 -3
- data/lib/MrMurano/commands/password.rb +4 -2
- data/lib/MrMurano/commands/postgresql.rb +5 -3
- data/lib/MrMurano/commands/settings.rb +78 -62
- data/lib/MrMurano/commands/show.rb +79 -74
- data/lib/MrMurano/commands/solution.rb +6 -4
- data/lib/MrMurano/commands/solution_picker.rb +5 -4
- data/lib/MrMurano/commands/status.rb +15 -4
- data/lib/MrMurano/commands/timeseries.rb +3 -2
- data/lib/MrMurano/commands/tsdb.rb +3 -2
- data/lib/MrMurano/hash.rb +6 -6
- data/lib/MrMurano/http.rb +66 -67
- data/lib/MrMurano/makePretty.rb +18 -12
- data/lib/MrMurano/progress.rb +9 -2
- data/lib/MrMurano/verbosing.rb +14 -2
- data/lib/MrMurano/version.rb +2 -2
- data/spec/GatewayDevice_spec.rb +190 -149
- data/spec/Mock_spec.rb +3 -3
- data/spec/Solution-ServiceEventHandler_spec.rb +170 -137
- data/spec/SyncUpDown_spec.rb +205 -191
- metadata +3 -2
@@ -1,4 +1,4 @@
|
|
1
|
-
# Last Modified: 2017.08.
|
1
|
+
# Last Modified: 2017.08.20 /coding: utf-8
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
# Copyright © 2016-2017 Exosite LLC.
|
@@ -21,8 +21,6 @@ module MrMurano
|
|
21
21
|
return unless solsecret.exist?
|
22
22
|
# Is in JSON, which as a subset of YAML, so use YAML parser
|
23
23
|
solsecret.open do |io|
|
24
|
-
# rubocop:disable Security/YAMLLoad: Prefer using YAML.safe_load over YAML.load.
|
25
|
-
# MAYBE/2017-07-02: Switch to YAML.safe_load.
|
26
24
|
ss = YAML.load(io)
|
27
25
|
|
28
26
|
pff = $cfg.file_at('passwords', :user)
|
data/lib/MrMurano/Config.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Last Modified: 2017.08.
|
1
|
+
# Last Modified: 2017.08.23 /coding: utf-8
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
# Copyright © 2016-2017 Exosite LLC.
|
@@ -335,6 +335,8 @@ module MrMurano
|
|
335
335
|
@paths.each(&:load)
|
336
336
|
# If user wants curl commands dumped to a file, open that file.
|
337
337
|
init_curl_file
|
338
|
+
# If user doesn't want paging, disable it.
|
339
|
+
program :help_paging, !$cfg['tool.no-page'] unless $cfg['tool.no-page'].nil?
|
338
340
|
end
|
339
341
|
|
340
342
|
## Load specified file into the config stack
|
@@ -354,6 +356,7 @@ module MrMurano
|
|
354
356
|
|
355
357
|
section, ikey = key.split('.')
|
356
358
|
paths.each do |path|
|
359
|
+
next if path.data.nil?
|
357
360
|
next unless path.data.has_section?(section)
|
358
361
|
sec = path.data[section]
|
359
362
|
return sec if ikey.nil?
|
@@ -435,8 +438,12 @@ module MrMurano
|
|
435
438
|
end
|
436
439
|
|
437
440
|
paths = @paths.select { |p| scope == p.kind }
|
438
|
-
|
439
|
-
|
441
|
+
if paths.empty? || paths.length > 1
|
442
|
+
scope_q = fancy_ticks(scope)
|
443
|
+
raise "Unknown scope: #{scope_q}" if paths.empty?
|
444
|
+
paths_q = fancy_ticks(paths)
|
445
|
+
raise "Too many paths: #{paths_q} / scope: #{scope_q}" if paths.length > 1
|
446
|
+
end
|
440
447
|
|
441
448
|
cfg = paths.first
|
442
449
|
data = cfg.data
|
@@ -488,7 +495,8 @@ module MrMurano
|
|
488
495
|
|
489
496
|
cfg_paths = @paths.select { |p| p.kind == scope }
|
490
497
|
|
491
|
-
|
498
|
+
scope_q = fancy_ticks(scope)
|
499
|
+
msg = "Scope: #{scope_q}\n\n"
|
492
500
|
locats += Rainbow(msg).bright.underline
|
493
501
|
|
494
502
|
if !cfg_paths.empty?
|
@@ -498,9 +506,9 @@ module MrMurano
|
|
498
506
|
path = "Path: #{cfg.path}\n"
|
499
507
|
elsif %i[internal defaults].include? cfg.kind
|
500
508
|
# cfg.path is nil.
|
501
|
-
path = "Path:
|
509
|
+
path = "Path: #{scope_q} config is not saved.\n"
|
502
510
|
else
|
503
|
-
path = "Path:
|
511
|
+
path = "Path: #{scope_q} config does not exist.\n"
|
504
512
|
end
|
505
513
|
#locats += Rainbow(path).bright
|
506
514
|
locats += path
|
@@ -535,11 +543,11 @@ module MrMurano
|
|
535
543
|
locats += msg
|
536
544
|
end
|
537
545
|
else
|
538
|
-
msg = "No config found for
|
546
|
+
msg = "No config found for #{scope_q}.\n"
|
539
547
|
if scope != :specified
|
540
548
|
locats += Rainbow(msg).red.bright
|
541
549
|
else
|
542
|
-
locats += "Path:
|
550
|
+
locats += "Path: #{scope_q} config does not exist.\n\n"
|
543
551
|
locats += "Use --configfile to specify this config file.\n"
|
544
552
|
end
|
545
553
|
end
|
data/lib/MrMurano/Content.rb
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
# Last Modified: 2017.08.23 /coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Copyright © 2016-2017 Exosite LLC.
|
5
|
+
# License: MIT. See LICENSE.txt.
|
6
|
+
# vim:tw=0:ts=2:sw=2:et:ai
|
7
|
+
|
1
8
|
require 'uri'
|
2
9
|
require 'cgi'
|
3
10
|
require 'net/http'
|
@@ -8,6 +15,7 @@ require 'MrMurano/Config'
|
|
8
15
|
require 'MrMurano/http'
|
9
16
|
require 'MrMurano/verbosing'
|
10
17
|
require 'MrMurano/SolutionId'
|
18
|
+
require 'MrMurano/SyncAllowed'
|
11
19
|
require 'MrMurano/SyncUpDown'
|
12
20
|
|
13
21
|
module MrMurano
|
@@ -17,6 +25,7 @@ module MrMurano
|
|
17
25
|
include Http
|
18
26
|
include Verbose
|
19
27
|
include SolutionId
|
28
|
+
include SyncAllowed
|
20
29
|
|
21
30
|
def initialize
|
22
31
|
@solntype = 'product.id'
|
@@ -35,7 +44,7 @@ module MrMurano
|
|
35
44
|
def endpoint(path='')
|
36
45
|
super
|
37
46
|
parts = ['https:/', $cfg['net.host'], 'api:1'] + @uriparts
|
38
|
-
s = parts.map
|
47
|
+
s = parts.map(&:to_s).join('/')
|
39
48
|
URI(s + path.to_s)
|
40
49
|
end
|
41
50
|
|
@@ -73,16 +82,16 @@ module MrMurano
|
|
73
82
|
# ?type=
|
74
83
|
sha256 = Digest::SHA256.new
|
75
84
|
sha256.file(local_path.to_s)
|
76
|
-
mime = MIME::Types.type_for(local_path.to_s)[0] || MIME::Types[
|
85
|
+
mime = MIME::Types.type_for(local_path.to_s)[0] || MIME::Types['application/octet-stream'][0]
|
77
86
|
|
78
87
|
params = {
|
79
|
-
:
|
80
|
-
:
|
81
|
-
:
|
88
|
+
sha256: sha256.hexdigest,
|
89
|
+
expires_in: 30,
|
90
|
+
type: mime,
|
82
91
|
}
|
83
|
-
|
84
|
-
|
85
|
-
|
92
|
+
params[:tags] = tags.to_json if !tags.nil? && tags.is_a?(Hash)
|
93
|
+
|
94
|
+
return unless upload_item_allowed(name)
|
86
95
|
|
87
96
|
ret = get("/#{CGI.escape(name)}/upload?#{URI.encode_www_form(params)}")
|
88
97
|
debug "POST instructions: #{ret}"
|
@@ -91,24 +100,24 @@ module MrMurano
|
|
91
100
|
|
92
101
|
uri = URI(ret[:url])
|
93
102
|
request = Net::HTTP::Post.new(uri)
|
94
|
-
file = HTTP::FormData::File.new(local_path.to_s,
|
95
|
-
form = HTTP::FormData.create(ret[:inputs].merge(
|
103
|
+
file = HTTP::FormData::File.new(local_path.to_s, content_type: mime)
|
104
|
+
form = HTTP::FormData.create(ret[:inputs].merge(ret[:field] => file))
|
96
105
|
|
97
106
|
request['User-Agent'] = "MrMurano/#{MrMurano::VERSION}"
|
98
107
|
request.content_type = form.content_type
|
99
108
|
request.content_length = form.content_length
|
100
109
|
request.body = form.to_s
|
101
110
|
|
102
|
-
if $cfg['tool.curldebug']
|
111
|
+
if $cfg['tool.curldebug']
|
103
112
|
a = []
|
104
|
-
a << %
|
105
|
-
a << %
|
106
|
-
a << %
|
107
|
-
a << %
|
113
|
+
a << %(curl -s)
|
114
|
+
a << %(-H 'User-Agent: #{request['User-Agent']}')
|
115
|
+
a << %(-X #{request.method})
|
116
|
+
a << %('#{request.uri}')
|
108
117
|
ret[:inputs].each_pair do |key, value|
|
109
|
-
a << %
|
118
|
+
a << %(-F '#{key}=#{value}')
|
110
119
|
end
|
111
|
-
a << %
|
120
|
+
a << %(-F #{ret[:field]}=@#{local_path})
|
112
121
|
if $cfg.curlfile_f.nil?
|
113
122
|
puts a.join(' ')
|
114
123
|
else
|
@@ -117,14 +126,17 @@ module MrMurano
|
|
117
126
|
end
|
118
127
|
end
|
119
128
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
129
|
+
return if $cfg['tool.dry']
|
130
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |ihttp|
|
131
|
+
response = ihttp.request(request)
|
132
|
+
case response
|
133
|
+
# rubocop:disable Lint/EmptyWhen
|
134
|
+
# "Avoid when branches without a body."
|
135
|
+
# "I'm going to allow this."
|
136
|
+
when Net::HTTPSuccess
|
137
|
+
# pass
|
138
|
+
else
|
139
|
+
showHttpError(request, response)
|
128
140
|
end
|
129
141
|
end
|
130
142
|
end
|
@@ -132,6 +144,7 @@ module MrMurano
|
|
132
144
|
# Remove content by name
|
133
145
|
# @param name [String] Name of content to be deleted
|
134
146
|
def remove(name)
|
147
|
+
return unless remove_item_allowed(name)
|
135
148
|
delete("/#{CGI.escape(name)}")
|
136
149
|
end
|
137
150
|
|
@@ -139,6 +152,7 @@ module MrMurano
|
|
139
152
|
# @param name [String] Name of content to be downloaded
|
140
153
|
# @param block [Block] Block to process data as it is downloaded
|
141
154
|
def download(name, &block)
|
155
|
+
return unless download_item_allowed(name)
|
142
156
|
# This is a two step process.
|
143
157
|
# 1: Get the get instructions for S3.
|
144
158
|
# 2: fetch from S3.
|
@@ -150,12 +164,12 @@ module MrMurano
|
|
150
164
|
request = Net::HTTP::Get.new(uri)
|
151
165
|
request['User-Agent'] = "MrMurano/#{MrMurano::VERSION}"
|
152
166
|
|
153
|
-
if $cfg['tool.curldebug']
|
167
|
+
if $cfg['tool.curldebug']
|
154
168
|
a = []
|
155
|
-
a << %
|
156
|
-
a << %
|
157
|
-
a << %
|
158
|
-
a << %
|
169
|
+
a << %(curl -s)
|
170
|
+
a << %(-H 'User-Agent: #{request['User-Agent']}')
|
171
|
+
a << %(-X #{request.method})
|
172
|
+
a << %('#{request.uri}')
|
159
173
|
if $cfg.curlfile_f.nil?
|
160
174
|
puts a.join(' ')
|
161
175
|
else
|
@@ -164,28 +178,25 @@ module MrMurano
|
|
164
178
|
end
|
165
179
|
end
|
166
180
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
else
|
175
|
-
response.read_body do |chunk|
|
176
|
-
$stdout.write chunk
|
177
|
-
end
|
178
|
-
end
|
181
|
+
return if $cfg['tool.dry']
|
182
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |ihttp|
|
183
|
+
ihttp.request(request) do |response|
|
184
|
+
case response
|
185
|
+
when Net::HTTPSuccess
|
186
|
+
if block_given?
|
187
|
+
response.read_body(&block)
|
179
188
|
else
|
180
|
-
|
189
|
+
response.read_body do |chunk|
|
190
|
+
$stdout.write chunk
|
191
|
+
end
|
181
192
|
end
|
193
|
+
else
|
194
|
+
showHttpError(request, response)
|
182
195
|
end
|
183
196
|
end
|
184
197
|
end
|
185
198
|
end
|
186
|
-
|
187
199
|
end
|
188
200
|
end
|
189
201
|
end
|
190
202
|
|
191
|
-
# vim: set ai et sw=2 ts=2 :
|
data/lib/MrMurano/Gateway.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Last Modified: 2017.08.
|
1
|
+
# Last Modified: 2017.08.23 /coding: utf-8
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
# Copyright © 2016-2017 Exosite LLC.
|
@@ -25,6 +25,7 @@ module MrMurano
|
|
25
25
|
include Http
|
26
26
|
include Verbose
|
27
27
|
include SolutionId
|
28
|
+
include SyncAllowed
|
28
29
|
|
29
30
|
def initialize
|
30
31
|
@solntype = 'product.id'
|
@@ -155,10 +156,13 @@ module MrMurano
|
|
155
156
|
end
|
156
157
|
|
157
158
|
def remove(itemkey)
|
159
|
+
return unless remove_item_allowed(itemkey)
|
158
160
|
@there.delete_if { |item| item[@itemkey] == itemkey }
|
159
161
|
end
|
160
162
|
|
161
163
|
def upload(_local, remote, _modify)
|
164
|
+
# Not calling, e.g., `return unless upload_item_allowed(local)`
|
165
|
+
# See instead: syncup_after and syncdown_after/resources_write
|
162
166
|
@there.delete_if { |item| item[@itemkey] == remote[@itemkey] }
|
163
167
|
@there << remote.reject { |k, _v| %i[synckey synctype].include? k }
|
164
168
|
end
|
@@ -192,6 +196,8 @@ module MrMurano
|
|
192
196
|
end
|
193
197
|
|
194
198
|
def download(_local, item)
|
199
|
+
# Not calling, e.g., `return unless download_item_allowed(item[@itemkey])`
|
200
|
+
# See instead: syncup_after and syncdown_after/resources_write
|
195
201
|
@here = locallist if @here.nil?
|
196
202
|
# needs to append/merge with file
|
197
203
|
@here.delete_if do |i|
|
@@ -218,7 +224,9 @@ module MrMurano
|
|
218
224
|
end
|
219
225
|
|
220
226
|
def removelocal(_local, item)
|
221
|
-
#
|
227
|
+
# Not calling, e.g., `return unless removelocal_item_allowed(item[@itemkey])`
|
228
|
+
# See instead: syncup_after and syncdown_after/resources_write
|
229
|
+
# Append/merge with file.
|
222
230
|
key = @itemkey.to_sym
|
223
231
|
@here.delete_if do |it|
|
224
232
|
it[key] == item[key]
|
@@ -297,8 +305,6 @@ module MrMurano
|
|
297
305
|
end
|
298
306
|
|
299
307
|
here = {}
|
300
|
-
# rubocop:disable Security/YAMLLoad: Prefer using YAML.safe_load over YAML.load.
|
301
|
-
# MAYBE/2017-07-02: Convert to safe_load.
|
302
308
|
from.open { |io| here = YAML.load(io) }
|
303
309
|
here = {} if here == false
|
304
310
|
|
@@ -375,23 +381,39 @@ module MrMurano
|
|
375
381
|
## Create a device with given Identity
|
376
382
|
# @param id [String] The new identity
|
377
383
|
# @param opts [Hash] Options for the new device
|
384
|
+
# @option opts [Integer] :expire Time at microsecs since epoch when activation window closes.
|
385
|
+
# (EXPLAIN/2017-08-23: For a certificate, is this different? Time when it must be reprovisioned?)
|
386
|
+
# @option opts [String,Pathname,IO] :key Shared secret for hash, password, token types;
|
387
|
+
# or public key for certificate auth type.
|
388
|
+
# May be string or IO/Pathname to file.
|
378
389
|
# @option opts [String] :type One of: certificate, hash, password, signature, token
|
379
|
-
|
380
|
-
# @option opts [String] :privatekey Shared secret for hash, password, token types
|
381
|
-
# @option opts [String,Integer] :expire For Cert, when it must be reprovisioned, otherwise when the activation window closes.
|
390
|
+
DEVICE_AUTH_TYPES = %i[certificate hash password signature token].freeze
|
382
391
|
def enable(id, opts=nil)
|
383
|
-
if
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
392
|
+
opts = {} if opts.nil?
|
393
|
+
props = { auth: {}, locked: false }
|
394
|
+
# See: okami_api/api/swagger/swagger.yaml
|
395
|
+
unless opts[:expire].nil?
|
396
|
+
begin
|
397
|
+
props[:auth][:expire] = Integer(opts[:expire])
|
398
|
+
rescue ArgumentError
|
399
|
+
# Callers should prevent this, so ugly raise is okay.
|
400
|
+
raise ':expire option is not a valid number: #{fancy_ticks(opts[:expire])}'
|
389
401
|
end
|
390
|
-
|
391
|
-
|
402
|
+
end
|
403
|
+
unless opts[:type].nil?
|
404
|
+
opts[:type] = opts[:type].to_sym
|
405
|
+
unless DEVICE_AUTH_TYPES.include?(opts[:type])
|
406
|
+
complaint = ":type must be one of #{DEVICE_AUTH_TYPES.join('|')}"
|
407
|
+
raise complaint
|
408
|
+
end
|
409
|
+
props[:auth][:type] = opts[:type]
|
410
|
+
end
|
411
|
+
unless opts[:key].nil?
|
412
|
+
props[:auth][:key] = opts[:key].is_a?(String) && opts[:key] || opts[:key].read
|
413
|
+
props[:auth][:type] = :certificate if props[:auth][:type].nil?
|
392
414
|
end
|
393
415
|
whirly_start('Enabling Device...')
|
394
|
-
putted = put("/#{CGI.escape(id.to_s)}",
|
416
|
+
putted = put("/#{CGI.escape(id.to_s)}", props)
|
395
417
|
whirly_stop
|
396
418
|
putted
|
397
419
|
end
|
@@ -400,19 +422,21 @@ module MrMurano
|
|
400
422
|
|
401
423
|
## Create a bunch of devices at once
|
402
424
|
# @param local [String, Pathname] CSV file of identifiers
|
403
|
-
# @param expire [Number] Expire time for all identities
|
425
|
+
# @param expire [Number] Expire time for all identities
|
404
426
|
# @return [void]
|
405
|
-
def enable_batch(local,
|
427
|
+
def enable_batch(local, expire=nil)
|
406
428
|
# Need to modify @uriparts for just this endpoint call.
|
407
429
|
uriparts = @uriparts
|
408
430
|
@uriparts[-1] = :identities
|
409
431
|
uri = endpoint
|
410
432
|
@uriparts = uriparts
|
411
|
-
|
412
433
|
file = HTTP::FormData::File.new(local.to_s, content_type: 'text/csv')
|
413
|
-
|
434
|
+
opts = {}
|
435
|
+
opts[:identities] = file
|
436
|
+
opts[:expire] = expire unless expire.nil?
|
437
|
+
form = HTTP::FormData.create(**opts)
|
414
438
|
req = Net::HTTP::Post.new(uri)
|
415
|
-
|
439
|
+
add_headers(req)
|
416
440
|
req.content_type = form.content_type
|
417
441
|
req.content_length = form.content_length
|
418
442
|
req.body = form.to_s
|
@@ -425,6 +449,7 @@ module MrMurano
|
|
425
449
|
## Delete a device
|
426
450
|
# @param identifier [String] Who to delete.
|
427
451
|
def remove(identifier)
|
452
|
+
return unless remove_item_allowed(identifier)
|
428
453
|
delete("/#{CGI.escape(identifier.to_s)}")
|
429
454
|
end
|
430
455
|
|
@@ -475,14 +500,30 @@ module MrMurano
|
|
475
500
|
# @param values [Hash] Aliases and the values to write.
|
476
501
|
def write(identifier, values)
|
477
502
|
debug "Will Write: #{values}"
|
503
|
+
# EXPLAIN/2017-08-23: Why not escape the ID?
|
504
|
+
# #{CGI.escape(identifier.to_s)}
|
478
505
|
patch("/#{identifier}/state", values)
|
479
506
|
end
|
480
507
|
|
481
508
|
# Read the current state for a device
|
482
509
|
# @param identifier [String] The identifier for the device to read.
|
483
510
|
def read(identifier)
|
511
|
+
# EXPLAIN/2017-08-23: Why not escape the ID?
|
512
|
+
# #{CGI.escape(identifier.to_s)}
|
484
513
|
get("/#{identifier}/state")
|
485
514
|
end
|
515
|
+
|
516
|
+
def lock(identifier)
|
517
|
+
patch("/#{CGI.escape(identifier.to_s)}", locked: true)
|
518
|
+
end
|
519
|
+
|
520
|
+
def unlock(identifier)
|
521
|
+
patch("/#{CGI.escape(identifier.to_s)}", locked: false)
|
522
|
+
end
|
523
|
+
|
524
|
+
def revoke(identifier)
|
525
|
+
patch("/#{CGI.escape(identifier.to_s)}", auth: { expire: 0 })
|
526
|
+
end
|
486
527
|
end
|
487
528
|
end
|
488
529
|
end
|
data/lib/MrMurano/Mock.rb
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
# Last Modified: 2017.08.20 /coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Copyright © 2016-2017 Exosite LLC.
|
5
|
+
# License: MIT. See LICENSE.txt.
|
6
|
+
# vim:tw=0:ts=2:sw=2:et:ai
|
7
|
+
|
1
8
|
require 'erb'
|
2
9
|
require 'securerandom'
|
3
10
|
|
@@ -9,55 +16,56 @@ module MrMurano
|
|
9
16
|
end
|
10
17
|
|
11
18
|
def show
|
12
|
-
file = Pathname.new(
|
13
|
-
if file.exist?
|
14
|
-
authorization = %
|
19
|
+
file = Pathname.new(testpoint_path)
|
20
|
+
if file.exist?
|
21
|
+
authorization = %(if request.headers["authorization"] == ")
|
15
22
|
file.open('rb') do |io|
|
16
23
|
io.each_line do |line|
|
17
24
|
auth_line = line.include?(authorization)
|
18
|
-
if auth_line
|
25
|
+
if auth_line
|
19
26
|
capture = /\=\= "(.*)"/.match(line)
|
20
27
|
return capture.captures[0]
|
21
28
|
end
|
22
29
|
end
|
23
30
|
end
|
24
31
|
end
|
25
|
-
|
32
|
+
false
|
26
33
|
end
|
27
34
|
|
28
|
-
def
|
29
|
-
path =
|
30
|
-
|
35
|
+
def mock_template
|
36
|
+
path = mock_template_path
|
37
|
+
::File.read(path)
|
31
38
|
end
|
32
39
|
|
33
|
-
def
|
40
|
+
def testpoint_path
|
34
41
|
file_name = 'testpoint.post.lua'
|
35
|
-
path = %
|
36
|
-
|
42
|
+
path = %(#{$cfg['location.endpoints']}/#{file_name})
|
43
|
+
path
|
37
44
|
end
|
38
45
|
|
39
|
-
def
|
40
|
-
|
46
|
+
def mock_template_path
|
47
|
+
::File.join(::File.dirname(__FILE__), 'template', 'mock.erb')
|
41
48
|
end
|
42
49
|
|
43
50
|
def create_testpoint
|
44
51
|
uuid = SecureRandom.uuid
|
45
|
-
template = ERB.new(
|
52
|
+
template = ERB.new(mock_template)
|
46
53
|
endpoint = template.result(binding)
|
47
54
|
|
48
|
-
Pathname.new(
|
55
|
+
Pathname.new(testpoint_path).open('wb') do |io|
|
49
56
|
io << endpoint
|
50
57
|
end
|
51
|
-
|
58
|
+
uuid
|
52
59
|
end
|
53
60
|
|
54
61
|
def remove_testpoint
|
55
|
-
file = Pathname.new(
|
56
|
-
if file.exist?
|
62
|
+
file = Pathname.new(testpoint_path)
|
63
|
+
if file.exist?
|
57
64
|
file.unlink
|
58
65
|
return true
|
59
66
|
end
|
60
|
-
|
67
|
+
false
|
61
68
|
end
|
62
69
|
end
|
63
70
|
end
|
71
|
+
|