MuranoCLI 2.0.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 +7 -0
- data/.gitignore +28 -0
- data/.rspec +2 -0
- data/.travis.yml +21 -0
- data/Gemfile +27 -0
- data/LICENSE.txt +19 -0
- data/MuranoCLI.gemspec +50 -0
- data/MuranoCLI.iss +50 -0
- data/README.markdown +208 -0
- data/Rakefile +188 -0
- data/TODO.taskpaper +122 -0
- data/bin/mr +8 -0
- data/bin/murano +84 -0
- data/docs/demo.md +109 -0
- data/lib/MrMurano/Account.rb +211 -0
- data/lib/MrMurano/Config-Migrate.rb +47 -0
- data/lib/MrMurano/Config.rb +286 -0
- data/lib/MrMurano/Mock.rb +63 -0
- data/lib/MrMurano/Product-1P-Device.rb +145 -0
- data/lib/MrMurano/Product-Resources.rb +195 -0
- data/lib/MrMurano/Product.rb +358 -0
- data/lib/MrMurano/ProjectFile.rb +349 -0
- data/lib/MrMurano/Solution-Cors.rb +46 -0
- data/lib/MrMurano/Solution-Endpoint.rb +177 -0
- data/lib/MrMurano/Solution-File.rb +150 -0
- data/lib/MrMurano/Solution-ServiceConfig.rb +140 -0
- data/lib/MrMurano/Solution-Services.rb +326 -0
- data/lib/MrMurano/Solution-Users.rb +129 -0
- data/lib/MrMurano/Solution.rb +59 -0
- data/lib/MrMurano/SubCmdGroupContext.rb +49 -0
- data/lib/MrMurano/SyncUpDown.rb +565 -0
- data/lib/MrMurano/commands/assign.rb +57 -0
- data/lib/MrMurano/commands/businessList.rb +45 -0
- data/lib/MrMurano/commands/completion.rb +152 -0
- data/lib/MrMurano/commands/config.rb +67 -0
- data/lib/MrMurano/commands/content.rb +130 -0
- data/lib/MrMurano/commands/cors.rb +30 -0
- data/lib/MrMurano/commands/domain.rb +17 -0
- data/lib/MrMurano/commands/gb.rb +33 -0
- data/lib/MrMurano/commands/init.rb +138 -0
- data/lib/MrMurano/commands/keystore.rb +157 -0
- data/lib/MrMurano/commands/logs.rb +78 -0
- data/lib/MrMurano/commands/mock.rb +63 -0
- data/lib/MrMurano/commands/password.rb +88 -0
- data/lib/MrMurano/commands/postgresql.rb +41 -0
- data/lib/MrMurano/commands/product.rb +14 -0
- data/lib/MrMurano/commands/productCreate.rb +39 -0
- data/lib/MrMurano/commands/productDelete.rb +33 -0
- data/lib/MrMurano/commands/productDevice.rb +84 -0
- data/lib/MrMurano/commands/productDeviceIdCmds.rb +86 -0
- data/lib/MrMurano/commands/productList.rb +45 -0
- data/lib/MrMurano/commands/productWrite.rb +27 -0
- data/lib/MrMurano/commands/show.rb +80 -0
- data/lib/MrMurano/commands/solution.rb +14 -0
- data/lib/MrMurano/commands/solutionCreate.rb +39 -0
- data/lib/MrMurano/commands/solutionDelete.rb +34 -0
- data/lib/MrMurano/commands/solutionList.rb +45 -0
- data/lib/MrMurano/commands/status.rb +92 -0
- data/lib/MrMurano/commands/sync.rb +60 -0
- data/lib/MrMurano/commands/timeseries.rb +115 -0
- data/lib/MrMurano/commands/tsdb.rb +271 -0
- data/lib/MrMurano/commands/usage.rb +23 -0
- data/lib/MrMurano/commands/zshcomplete.erb +112 -0
- data/lib/MrMurano/commands.rb +32 -0
- data/lib/MrMurano/hash.rb +20 -0
- data/lib/MrMurano/http.rb +153 -0
- data/lib/MrMurano/makePretty.rb +75 -0
- data/lib/MrMurano/schema/pf-v1.0.0.yaml +114 -0
- data/lib/MrMurano/schema/sf-v0.2.0.yaml +77 -0
- data/lib/MrMurano/schema/sf-v0.3.0.yaml +78 -0
- data/lib/MrMurano/template/mock.erb +9 -0
- data/lib/MrMurano/template/projectFile.murano.erb +81 -0
- data/lib/MrMurano/verbosing.rb +99 -0
- data/lib/MrMurano/version.rb +4 -0
- data/lib/MrMurano.rb +20 -0
- data/spec/Account-Passwords_spec.rb +242 -0
- data/spec/Account_spec.rb +272 -0
- data/spec/ConfigFile_spec.rb +50 -0
- data/spec/ConfigMigrate_spec.rb +89 -0
- data/spec/Config_spec.rb +409 -0
- data/spec/Http_spec.rb +204 -0
- data/spec/MakePretties_spec.rb +118 -0
- data/spec/Mock_spec.rb +53 -0
- data/spec/ProductBase_spec.rb +113 -0
- data/spec/ProductContent_spec.rb +162 -0
- data/spec/ProductResources_spec.rb +329 -0
- data/spec/Product_1P_Device_spec.rb +202 -0
- data/spec/Product_1P_RPC_spec.rb +175 -0
- data/spec/Product_spec.rb +153 -0
- data/spec/ProjectFile_spec.rb +324 -0
- data/spec/Solution-Cors_spec.rb +164 -0
- data/spec/Solution-Endpoint_spec.rb +581 -0
- data/spec/Solution-File_spec.rb +212 -0
- data/spec/Solution-ServiceConfig_spec.rb +202 -0
- data/spec/Solution-ServiceDevice_spec.rb +176 -0
- data/spec/Solution-ServiceEventHandler_spec.rb +385 -0
- data/spec/Solution-ServiceModules_spec.rb +465 -0
- data/spec/Solution-UsersRoles_spec.rb +207 -0
- data/spec/Solution_spec.rb +92 -0
- data/spec/SyncRoot_spec.rb +83 -0
- data/spec/SyncUpDown_spec.rb +495 -0
- data/spec/Verbosing_spec.rb +279 -0
- data/spec/_workspace.rb +27 -0
- data/spec/cmd_assign_spec.rb +51 -0
- data/spec/cmd_business_spec.rb +59 -0
- data/spec/cmd_common.rb +72 -0
- data/spec/cmd_config_spec.rb +68 -0
- data/spec/cmd_content_spec.rb +71 -0
- data/spec/cmd_cors_spec.rb +50 -0
- data/spec/cmd_device_spec.rb +96 -0
- data/spec/cmd_domain_spec.rb +32 -0
- data/spec/cmd_init_spec.rb +30 -0
- data/spec/cmd_keystore_spec.rb +97 -0
- data/spec/cmd_password_spec.rb +62 -0
- data/spec/cmd_status_spec.rb +239 -0
- data/spec/cmd_syncdown_spec.rb +86 -0
- data/spec/cmd_syncup_spec.rb +62 -0
- data/spec/cmd_usage_spec.rb +36 -0
- data/spec/fixtures/.mrmuranorc +9 -0
- data/spec/fixtures/ProjectFiles/invalid.yaml +9 -0
- data/spec/fixtures/ProjectFiles/only_meta.yaml +24 -0
- data/spec/fixtures/ProjectFiles/with_routes.yaml +27 -0
- data/spec/fixtures/SolutionFiles/0.2.0.json +20 -0
- data/spec/fixtures/SolutionFiles/0.2.0_invalid.json +18 -0
- data/spec/fixtures/SolutionFiles/0.2.json +21 -0
- data/spec/fixtures/SolutionFiles/0.3.0.json +20 -0
- data/spec/fixtures/SolutionFiles/0.3.0_invalid.json +19 -0
- data/spec/fixtures/SolutionFiles/0.3.json +20 -0
- data/spec/fixtures/SolutionFiles/basic.json +20 -0
- data/spec/fixtures/SolutionFiles/secret.json +6 -0
- data/spec/fixtures/configfile +9 -0
- data/spec/fixtures/dumped_config +42 -0
- data/spec/fixtures/mrmuranorc_deleted_bob +8 -0
- data/spec/fixtures/mrmuranorc_tool_bob +3 -0
- data/spec/fixtures/product_spec_files/example.exoline.spec.yaml +116 -0
- data/spec/fixtures/product_spec_files/example.murano.spec.yaml +14 -0
- data/spec/fixtures/product_spec_files/gwe.exoline.spec.yaml +21 -0
- data/spec/fixtures/product_spec_files/gwe.murano.spec.yaml +16 -0
- data/spec/fixtures/product_spec_files/lightbulb-no-state.yaml +11 -0
- data/spec/fixtures/product_spec_files/lightbulb.yaml +14 -0
- data/spec/fixtures/roles-three.yaml +11 -0
- data/spec/fixtures/syncable_content/assets/icon.png +0 -0
- data/spec/fixtures/syncable_content/assets/index.html +0 -0
- data/spec/fixtures/syncable_content/assets/js/script.js +0 -0
- data/spec/fixtures/syncable_content/modules/table_util.lua +58 -0
- data/spec/fixtures/syncable_content/routes/manyRoutes.lua +11 -0
- data/spec/fixtures/syncable_content/routes/singleRoute.lua +5 -0
- data/spec/fixtures/syncable_content/services/devdata.lua +18 -0
- data/spec/fixtures/syncable_content/services/timers.lua +4 -0
- data/spec/spec_helper.rb +119 -0
- metadata +498 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
require 'json-schema'
|
|
3
|
+
require 'pathname'
|
|
4
|
+
require 'MrMurano/verbosing'
|
|
5
|
+
require 'MrMurano/Config'
|
|
6
|
+
require 'MrMurano/hash'
|
|
7
|
+
|
|
8
|
+
module MrMurano
|
|
9
|
+
##
|
|
10
|
+
# A Project File that describes details about a project that is synced into
|
|
11
|
+
# Murano
|
|
12
|
+
class ProjectFile
|
|
13
|
+
include Verbose
|
|
14
|
+
|
|
15
|
+
# Methods that are common to various internal structs.
|
|
16
|
+
module PrjStructCommonMethods
|
|
17
|
+
## Load data from Hash into self
|
|
18
|
+
#
|
|
19
|
+
# Also makes sure that :include and :exclude are arrays.
|
|
20
|
+
#
|
|
21
|
+
# @param obj [Hash] Data to load in
|
|
22
|
+
def load(obj)
|
|
23
|
+
self.members.reject{|key| [:legacy].include? key}.each do |key|
|
|
24
|
+
self[key] = obj[key] if obj.has_key? key
|
|
25
|
+
end
|
|
26
|
+
self.members.select{|k| [:include, :exclude].include? k}.each do |key|
|
|
27
|
+
self[key] = [self[key]] unless self[key].kind_of? Array
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
## Returns a sparse hash of the data in self
|
|
32
|
+
# @return [Hash] Just the non-nil members of this
|
|
33
|
+
def save
|
|
34
|
+
ret={}
|
|
35
|
+
self.members.reject{|key| [:legacy].include? key}.each do |key|
|
|
36
|
+
ret[key] = self[key] unless self[key].nil?
|
|
37
|
+
end
|
|
38
|
+
ret
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# The contents of this is explictily not just a nest of hashes and arrays.
|
|
43
|
+
# To keep expectations in check, there is a set number of known keys.
|
|
44
|
+
# This should also help by keeping the file format seperate from the internal
|
|
45
|
+
# lookups. Hopefully, this will avoid (or at least minimize) changes to the
|
|
46
|
+
# file format affecting all kinds of code.
|
|
47
|
+
PrjMeta = Struct.new(:name, :summary, :description, :authors, :version, :source, :dependencies) do
|
|
48
|
+
include PrjStructCommonMethods
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
PrjFiles = Struct.new(:location, :include, :exclude, :default_page) do
|
|
52
|
+
include PrjStructCommonMethods
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
PrjModules = Struct.new(:location, :include, :exclude) do
|
|
56
|
+
include PrjStructCommonMethods
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
PrjEndpoints = Struct.new(:location, :include, :exclude, :cors) do
|
|
60
|
+
include PrjStructCommonMethods
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
PrjEventHandlers = Struct.new(:location, :include, :exclude, :legacy) do
|
|
64
|
+
include PrjStructCommonMethods
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
PrjResources = Struct.new(:location, :include, :exclude) do
|
|
68
|
+
include PrjStructCommonMethods
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
PrfFile = Struct.new(:info, :assets, :modules, :routes, :services, :resources) do
|
|
72
|
+
## Returns a sparse hash of the data in self
|
|
73
|
+
# @return [Hash] Just the non-empty members of this
|
|
74
|
+
def save
|
|
75
|
+
ret={}
|
|
76
|
+
self.members.each do |key|
|
|
77
|
+
value = self[key].save
|
|
78
|
+
ret[key] = value unless value.empty?
|
|
79
|
+
end
|
|
80
|
+
ret
|
|
81
|
+
end
|
|
82
|
+
def get_binding
|
|
83
|
+
binding()
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def initialize()
|
|
88
|
+
@prjFile = nil
|
|
89
|
+
tname = $cfg['location.base'].basename.to_s.gsub(/[^A-Za-z0-9]/, '')
|
|
90
|
+
@data = PrfFile.new(
|
|
91
|
+
PrjMeta.new(
|
|
92
|
+
tname,
|
|
93
|
+
"One line summary of #{tname}",
|
|
94
|
+
"In depth description of #{tname}\n\nWith lots of details.",
|
|
95
|
+
[$cfg['user.name']],
|
|
96
|
+
"1.0.0",
|
|
97
|
+
nil,
|
|
98
|
+
nil
|
|
99
|
+
),
|
|
100
|
+
PrjFiles.new,
|
|
101
|
+
PrjModules.new,
|
|
102
|
+
PrjEndpoints.new,
|
|
103
|
+
PrjEventHandlers.new,
|
|
104
|
+
PrjResources.new,
|
|
105
|
+
)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Get the current Project file
|
|
109
|
+
# @return [Pathname] PAth to current project file.
|
|
110
|
+
def project_file
|
|
111
|
+
@prjFile
|
|
112
|
+
end
|
|
113
|
+
# Get a binding to the data for building the example ProjectFile
|
|
114
|
+
def data_binding
|
|
115
|
+
@data.get_binding
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Get a value for a key.
|
|
119
|
+
# Keys are 'section.key'
|
|
120
|
+
def get(key)
|
|
121
|
+
raise "Empty key" if key.empty?
|
|
122
|
+
section, ikey = key.split('.')
|
|
123
|
+
raise "Missing dot" if ikey.nil? and section == key
|
|
124
|
+
raise "Missing key" if ikey.nil? and section != key
|
|
125
|
+
ret = @data[section.to_sym][ikey.to_sym]
|
|
126
|
+
return default_value_for(key) if ret.nil?
|
|
127
|
+
return ret
|
|
128
|
+
end
|
|
129
|
+
alias [] get
|
|
130
|
+
|
|
131
|
+
# Get the default value for a key.
|
|
132
|
+
#
|
|
133
|
+
# Keys are 'section.key'
|
|
134
|
+
#
|
|
135
|
+
# All of these are currently stored in $cfg, but under different names.
|
|
136
|
+
def default_value_for(key)
|
|
137
|
+
keymap = {
|
|
138
|
+
'assets.location' => 'location.files',
|
|
139
|
+
'assets.include' => 'files.searchFor',
|
|
140
|
+
'assets.exclude' => 'files.ignoring',
|
|
141
|
+
'assets.default_page' => 'files.default_page',
|
|
142
|
+
'modules.location' => 'location.modules',
|
|
143
|
+
'modules.include' => 'modules.searchFor',
|
|
144
|
+
'modules.exclude' => 'modules.ignoring',
|
|
145
|
+
'routes.location' => 'location.endpoints',
|
|
146
|
+
'routes.include' => 'endpoints.searchFor',
|
|
147
|
+
'routes.exclude' => 'endpoints.ignoring',
|
|
148
|
+
'routes.cors' => 'location.cors',
|
|
149
|
+
'services.location' => 'location.eventhandlers',
|
|
150
|
+
'services.include' => 'eventhandler.searchFor',
|
|
151
|
+
'services.exclude' => 'eventhandler.ignoring',
|
|
152
|
+
'resources.location' => 'location.specs',
|
|
153
|
+
'resources.include' => 'product.spec',
|
|
154
|
+
'resources.exclude' => 'product.ignoring',
|
|
155
|
+
}.freeze
|
|
156
|
+
needSplit = %r{.*\.(searchFor|ignoring)$}.freeze
|
|
157
|
+
return nil unless keymap.has_key? key
|
|
158
|
+
# *.{include,exclude} want arrays returned. But what they map to is
|
|
159
|
+
# strings.
|
|
160
|
+
cfg_key = keymap[key]
|
|
161
|
+
ret = ($cfg[cfg_key] or '')
|
|
162
|
+
ret = ret.split() if cfg_key =~ needSplit
|
|
163
|
+
ret
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Set a value for a key.
|
|
167
|
+
# Keys are 'section.key'
|
|
168
|
+
def set(key, value)
|
|
169
|
+
section, ikey = key.split('.')
|
|
170
|
+
begin
|
|
171
|
+
sec = @data[section.to_sym]
|
|
172
|
+
sec[ikey.to_sym] = value
|
|
173
|
+
rescue NameError => e
|
|
174
|
+
debug ">>=>> #{e}"
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
alias []= set
|
|
178
|
+
|
|
179
|
+
## Save the Project File.
|
|
180
|
+
#
|
|
181
|
+
# This ALWAYS saves in the latest format only.
|
|
182
|
+
def save(ios=$stdout)
|
|
183
|
+
dt = @data.save
|
|
184
|
+
dt = Hash.transform_keys_to_strings(dt).to_yaml
|
|
185
|
+
if ios.nil?
|
|
186
|
+
dt
|
|
187
|
+
else
|
|
188
|
+
ios.write dt
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
##
|
|
193
|
+
# Load the project file in the project directory.
|
|
194
|
+
def load
|
|
195
|
+
possible = Dir[
|
|
196
|
+
($cfg['location.base'] + '*.murano').to_s,
|
|
197
|
+
($cfg['location.base'] + 'Solutionfile.json').to_s,
|
|
198
|
+
]
|
|
199
|
+
debug "Possible Project files: #{possible}"
|
|
200
|
+
return 0 if possible.empty? # this is ok.
|
|
201
|
+
|
|
202
|
+
warning "Multiple possible Project files! #{possible}" if possible.count > 1
|
|
203
|
+
@prjFile = Pathname.new(possible.first)
|
|
204
|
+
|
|
205
|
+
data = nil
|
|
206
|
+
begin
|
|
207
|
+
data = YAML.load_file(@prjFile.to_s)
|
|
208
|
+
rescue Exception => e
|
|
209
|
+
error "Load error; #{e}"
|
|
210
|
+
pp e
|
|
211
|
+
return -3
|
|
212
|
+
end
|
|
213
|
+
if data.nil? then
|
|
214
|
+
error "Failed to load #{@prjFile}"
|
|
215
|
+
return -2
|
|
216
|
+
end
|
|
217
|
+
unless data.kind_of?(Hash) then
|
|
218
|
+
error "Bad format in #{@prjFile}"
|
|
219
|
+
return -4
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
data = Hash.transform_keys_to_symbols(data)
|
|
223
|
+
|
|
224
|
+
# get format version; little different for older format.
|
|
225
|
+
if @prjFile.basename.to_s == "Solutionfile.json" then
|
|
226
|
+
fmtvers = (data[:version] or '0.2.0')
|
|
227
|
+
else
|
|
228
|
+
fmtvers = (data[:formatversion] or '1.0.0')
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
methodname = "load_#{fmtvers.gsub(/\./, '_')}".to_sym
|
|
232
|
+
debug "Will try to #{methodname}"
|
|
233
|
+
if respond_to? methodname then
|
|
234
|
+
errorlist = __send__(methodname, data)
|
|
235
|
+
unless errorlist.empty? then
|
|
236
|
+
error %{Project file #{@prjFile} not valid.}
|
|
237
|
+
errorlist.each{|er| error er}
|
|
238
|
+
return -5
|
|
239
|
+
end
|
|
240
|
+
else
|
|
241
|
+
error "Cannot load Project file of version #{fmtvers}"
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# Only set destination if source has key
|
|
246
|
+
# @param src [Hash,Struct] Source of value to copy
|
|
247
|
+
# @param skey [String,Symbol] Key in source to check and read
|
|
248
|
+
# @param dest [Hash,Struct] Destination to save value in
|
|
249
|
+
# @param dkey [String,Symbol] Key in destination to save to
|
|
250
|
+
def ifset(src, skey, dest, dkey)
|
|
251
|
+
dest[dkey] = src[skey] if src.has_key? skey
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
# Load data in the 1.0.0 format.
|
|
255
|
+
# @param data [Hash] the data to load
|
|
256
|
+
# @return [Array] An array of validation errors in the data
|
|
257
|
+
def load_1_0_0(data)
|
|
258
|
+
schemaPath = Pathname.new(::File.dirname(__FILE__)) + 'schema/pf-v1.0.0.yaml'
|
|
259
|
+
schema = YAML.load_file(schemaPath.to_s)
|
|
260
|
+
v = JSON::Validator.fully_validate(schema, data)
|
|
261
|
+
return v unless v.empty?
|
|
262
|
+
|
|
263
|
+
@data.each_pair do |key, str|
|
|
264
|
+
str.load(data[key]) if data.has_key? key
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
[]
|
|
268
|
+
end
|
|
269
|
+
alias load_1_0 load_1_0_0
|
|
270
|
+
alias load_1 load_1_0_0
|
|
271
|
+
|
|
272
|
+
# Load data in the 0.2.0 format.
|
|
273
|
+
# @param data [Hash] the data to load
|
|
274
|
+
# @return [Array] An array of validation errors in the data
|
|
275
|
+
def load_0_2_0(data)
|
|
276
|
+
schemaPath = Pathname.new(::File.dirname(__FILE__)) + 'schema/sf-v0.2.0.yaml'
|
|
277
|
+
schema = YAML.load_file(schemaPath.to_s)
|
|
278
|
+
v = JSON::Validator.fully_validate(schema, data)
|
|
279
|
+
return v unless v.empty?
|
|
280
|
+
|
|
281
|
+
ifset(data, :default_page, @data[:assets], :default_page)
|
|
282
|
+
ifset(data, :file_dir, @data[:assets], :location)
|
|
283
|
+
|
|
284
|
+
@data[:routes].location = '.'
|
|
285
|
+
@data[:routes][:include] = [data[:custom_api]] if data.has_key? :custom_api
|
|
286
|
+
ifset(data, :cors, @data[:routes], :cors)
|
|
287
|
+
|
|
288
|
+
if data.has_key? :modules then
|
|
289
|
+
@data[:modules].location = '.'
|
|
290
|
+
@data[:modules][:include] = data[:modules].values
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
if data.has_key? :event_handler then
|
|
294
|
+
@data[:services].location = '.'
|
|
295
|
+
evd = data[:event_handler].values.map{|e| e.values}.flatten
|
|
296
|
+
@data[:services].include = evd
|
|
297
|
+
@data.services.legacy = store_legacy_service_handlers(data[:event_handler])
|
|
298
|
+
end
|
|
299
|
+
[]
|
|
300
|
+
end
|
|
301
|
+
alias load_0_2 load_0_2_0
|
|
302
|
+
|
|
303
|
+
def store_legacy_service_handlers(services)
|
|
304
|
+
ret = {}
|
|
305
|
+
services.each do |service, events|
|
|
306
|
+
events.each do |event, path|
|
|
307
|
+
ret[path] = [service, event]
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
ret
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
# Load data in the 0.3.0 format.
|
|
314
|
+
# @param data [Hash] the data to load
|
|
315
|
+
# @return [Array] An array of validation errors in the data
|
|
316
|
+
def load_0_3_0(data)
|
|
317
|
+
schemaPath = Pathname.new(::File.dirname(__FILE__)) + 'schema/sf-v0.3.0.yaml'
|
|
318
|
+
schema = YAML.load_file(schemaPath.to_s)
|
|
319
|
+
v = JSON::Validator.fully_validate(schema, data)
|
|
320
|
+
return v unless v.empty?
|
|
321
|
+
|
|
322
|
+
ifset(data, :default_page, @data[:assets], :default_page)
|
|
323
|
+
ifset(data, :assets, @data[:assets], :location)
|
|
324
|
+
|
|
325
|
+
@data[:routes].location = '.'
|
|
326
|
+
@data[:routes][:include] = [data[:routes]] if data.has_key? :routes
|
|
327
|
+
ifset(data, :cors, @data[:routes], :cors)
|
|
328
|
+
|
|
329
|
+
if data.has_key? :modules then
|
|
330
|
+
@data[:modules].location = '.'
|
|
331
|
+
@data[:modules][:include] = data[:modules].values
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
if data.has_key? :services then
|
|
335
|
+
@data[:services].location = '.'
|
|
336
|
+
evd = data[:services].values.map{|e| e.values}.flatten
|
|
337
|
+
@data[:services].include = evd
|
|
338
|
+
@data.services.legacy = store_legacy_service_handlers(data[:services])
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
[]
|
|
342
|
+
end
|
|
343
|
+
alias load_0_3 load_0_3_0
|
|
344
|
+
alias load_0 load_0_3_0
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
end
|
|
349
|
+
# vim: set ai et sw=2 ts=2 :
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
require 'json'
|
|
3
|
+
require 'MrMurano/hash'
|
|
4
|
+
require 'MrMurano/Solution'
|
|
5
|
+
|
|
6
|
+
module MrMurano
|
|
7
|
+
class Cors < SolutionBase
|
|
8
|
+
def initialize
|
|
9
|
+
super
|
|
10
|
+
@uriparts << 'cors'
|
|
11
|
+
@project_section = :cors
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def fetch(id=nil, &block)
|
|
15
|
+
ret = get()
|
|
16
|
+
if ret.kind_of?(Hash) and ret.has_key?(:cors) then
|
|
17
|
+
# XXX cors is a JSON encoded string. That seems weird. keep an eye on this.
|
|
18
|
+
data = JSON.parse(ret[:cors], @json_opts)
|
|
19
|
+
else
|
|
20
|
+
data = ret
|
|
21
|
+
end
|
|
22
|
+
if block_given? then
|
|
23
|
+
yield Hash.transform_keys_to_strings(data).to_yaml
|
|
24
|
+
else
|
|
25
|
+
data
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# Upload CORS
|
|
31
|
+
# @param file [String,Nil] File path to upload other than defaults
|
|
32
|
+
def upload(file=nil)
|
|
33
|
+
unless file.nil? then
|
|
34
|
+
data = YAML.load_file(file)
|
|
35
|
+
else
|
|
36
|
+
data = $project['routes.cors']
|
|
37
|
+
# If it is just a string, then is a file to load.
|
|
38
|
+
data = YAML.load_file(data) if data.kind_of? String
|
|
39
|
+
end
|
|
40
|
+
put('', data)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# vim: set ai et sw=2 ts=2 :
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
require 'uri'
|
|
2
|
+
require 'net/http'
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'pp'
|
|
5
|
+
require 'MrMurano/Solution'
|
|
6
|
+
|
|
7
|
+
module MrMurano
|
|
8
|
+
# …/endpoint
|
|
9
|
+
class Endpoint < SolutionBase
|
|
10
|
+
def initialize
|
|
11
|
+
super
|
|
12
|
+
@uriparts << 'endpoint'
|
|
13
|
+
@project_section = :routes
|
|
14
|
+
|
|
15
|
+
@match_header = /--#ENDPOINT (?<method>\S+) (?<path>\S+)( (?<ctype>.*))?/
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# This gets all data about all endpoints
|
|
20
|
+
def list
|
|
21
|
+
get().map do |item|
|
|
22
|
+
if item[:content_type].nil? or item[:content_type].empty? then
|
|
23
|
+
item[:content_type] = 'application/json'
|
|
24
|
+
end
|
|
25
|
+
# XXX should this update the script header?
|
|
26
|
+
item
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def fetch(id)
|
|
31
|
+
ret = get('/' + id.to_s)
|
|
32
|
+
ret[:content_type] = 'application/json' if ret[:content_type].empty?
|
|
33
|
+
|
|
34
|
+
script = ret[:script].lines.map{|l|l.chomp}
|
|
35
|
+
|
|
36
|
+
aheader = (script.first or "")
|
|
37
|
+
|
|
38
|
+
rh = ['--#ENDPOINT', ret[:method].upcase, ret[:path]]
|
|
39
|
+
rh << ret[:content_type] if ret[:content_type] != 'application/json'
|
|
40
|
+
rheader = rh.join(' ')
|
|
41
|
+
|
|
42
|
+
# if header is missing add it.
|
|
43
|
+
# If header is wrong, replace it.
|
|
44
|
+
|
|
45
|
+
md = @match_header.match(aheader)
|
|
46
|
+
if md.nil? then
|
|
47
|
+
# header missing.
|
|
48
|
+
script.unshift rheader
|
|
49
|
+
elsif md[:method] != ret[:method] or
|
|
50
|
+
md[:path] != ret[:path] or
|
|
51
|
+
md[:ctype] != ret[:content_type] then
|
|
52
|
+
# header is wrong.
|
|
53
|
+
script[0] = rheader
|
|
54
|
+
end
|
|
55
|
+
# otherwise current header is good.
|
|
56
|
+
|
|
57
|
+
script = script.join("\n") + "\n"
|
|
58
|
+
if block_given? then
|
|
59
|
+
yield script
|
|
60
|
+
else
|
|
61
|
+
script
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
##
|
|
66
|
+
# Upload endpoint
|
|
67
|
+
# :local path to file to push
|
|
68
|
+
# :remote hash of method and endpoint path
|
|
69
|
+
# @param modify Bool: True if item exists already and this is changing it
|
|
70
|
+
def upload(local, remote, modify)
|
|
71
|
+
local = Pathname.new(local) unless local.kind_of? Pathname
|
|
72
|
+
raise "no file" unless local.exist?
|
|
73
|
+
|
|
74
|
+
# we assume these are small enough to slurp.
|
|
75
|
+
unless remote.has_key? :script then
|
|
76
|
+
script = local.read
|
|
77
|
+
remote[:script] = script
|
|
78
|
+
end
|
|
79
|
+
limitkeys = [:method, :path, :script, :content_type, @itemkey]
|
|
80
|
+
remote = remote.select{|k,v| limitkeys.include? k }
|
|
81
|
+
# post('', remote)
|
|
82
|
+
if remote.has_key? @itemkey then
|
|
83
|
+
put('/' + remote[@itemkey], remote) do |request, http|
|
|
84
|
+
response = http.request(request)
|
|
85
|
+
case response
|
|
86
|
+
when Net::HTTPSuccess
|
|
87
|
+
#return JSON.parse(response.body)
|
|
88
|
+
when Net::HTTPNotFound
|
|
89
|
+
verbose "\tDoesn't exist, creating"
|
|
90
|
+
post('/', remote)
|
|
91
|
+
else
|
|
92
|
+
showHttpError(request, response)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
else
|
|
96
|
+
verbose "\tNo itemkey, creating"
|
|
97
|
+
post('/', remote)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
##
|
|
102
|
+
# Delete an endpoint
|
|
103
|
+
def remove(id)
|
|
104
|
+
delete('/' + id.to_s)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def tolocalname(item, key)
|
|
108
|
+
name = ''
|
|
109
|
+
name << item[:path].split('/').reject{|i|i.empty?}.join('-')
|
|
110
|
+
name << '.'
|
|
111
|
+
name << item[:method].downcase
|
|
112
|
+
name << '.lua'
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def toRemoteItem(from, path)
|
|
116
|
+
# Path could be have multiple endpoints inside, so a loop.
|
|
117
|
+
items = []
|
|
118
|
+
path = Pathname.new(path) unless path.kind_of? Pathname
|
|
119
|
+
cur = nil
|
|
120
|
+
lineno=0
|
|
121
|
+
path.readlines().each do |line|
|
|
122
|
+
md = @match_header.match(line)
|
|
123
|
+
if not md.nil? then
|
|
124
|
+
# header line.
|
|
125
|
+
cur[:line_end] = lineno unless cur.nil?
|
|
126
|
+
items << cur unless cur.nil?
|
|
127
|
+
cur = {:method=>md[:method],
|
|
128
|
+
:path=>md[:path],
|
|
129
|
+
:content_type=> (md[:ctype] or 'application/json'),
|
|
130
|
+
:local_path=>path,
|
|
131
|
+
:line=>lineno,
|
|
132
|
+
:script=>line}
|
|
133
|
+
elsif not cur.nil? and not cur[:script].nil? then
|
|
134
|
+
cur[:script] << line
|
|
135
|
+
end
|
|
136
|
+
lineno += 1
|
|
137
|
+
end
|
|
138
|
+
cur[:line_end] = lineno unless cur.nil?
|
|
139
|
+
items << cur unless cur.nil?
|
|
140
|
+
items
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def match(item, pattern)
|
|
144
|
+
# Pattern is: #{method}#{path glob}
|
|
145
|
+
pattern_pattern = /^#(?<method>[^#]*)#(?<path>.*)/i
|
|
146
|
+
md = pattern_pattern.match(pattern)
|
|
147
|
+
return false if md.nil?
|
|
148
|
+
debug "match pattern: '#{md[:method]}' '#{md[:path]}'"
|
|
149
|
+
|
|
150
|
+
unless md[:method].empty? then
|
|
151
|
+
return false unless item[:method].downcase == md[:method].downcase
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
return true if md[:path].empty?
|
|
155
|
+
|
|
156
|
+
::File.fnmatch(md[:path],item[:path])
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def synckey(item)
|
|
160
|
+
"#{item[:method].upcase}_#{item[:path]}"
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def docmp(itemA, itemB)
|
|
164
|
+
if itemA[:script].nil? and itemA[:local_path] then
|
|
165
|
+
itemA[:script] = itemA[:local_path].read
|
|
166
|
+
end
|
|
167
|
+
if itemB[:script].nil? and itemB[:local_path] then
|
|
168
|
+
itemB[:script] = itemB[:local_path].read
|
|
169
|
+
end
|
|
170
|
+
return (itemA[:script] != itemB[:script] or itemA[:content_type] != itemB[:content_type])
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
SyncRoot.add('endpoints', Endpoint, 'A', %{Endpoints}, true)
|
|
176
|
+
end
|
|
177
|
+
# vim: set ai et sw=2 ts=2 :
|