MuranoCLI 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +28 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +21 -0
  5. data/Gemfile +27 -0
  6. data/LICENSE.txt +19 -0
  7. data/MuranoCLI.gemspec +50 -0
  8. data/MuranoCLI.iss +50 -0
  9. data/README.markdown +208 -0
  10. data/Rakefile +188 -0
  11. data/TODO.taskpaper +122 -0
  12. data/bin/mr +8 -0
  13. data/bin/murano +84 -0
  14. data/docs/demo.md +109 -0
  15. data/lib/MrMurano/Account.rb +211 -0
  16. data/lib/MrMurano/Config-Migrate.rb +47 -0
  17. data/lib/MrMurano/Config.rb +286 -0
  18. data/lib/MrMurano/Mock.rb +63 -0
  19. data/lib/MrMurano/Product-1P-Device.rb +145 -0
  20. data/lib/MrMurano/Product-Resources.rb +195 -0
  21. data/lib/MrMurano/Product.rb +358 -0
  22. data/lib/MrMurano/ProjectFile.rb +349 -0
  23. data/lib/MrMurano/Solution-Cors.rb +46 -0
  24. data/lib/MrMurano/Solution-Endpoint.rb +177 -0
  25. data/lib/MrMurano/Solution-File.rb +150 -0
  26. data/lib/MrMurano/Solution-ServiceConfig.rb +140 -0
  27. data/lib/MrMurano/Solution-Services.rb +326 -0
  28. data/lib/MrMurano/Solution-Users.rb +129 -0
  29. data/lib/MrMurano/Solution.rb +59 -0
  30. data/lib/MrMurano/SubCmdGroupContext.rb +49 -0
  31. data/lib/MrMurano/SyncUpDown.rb +565 -0
  32. data/lib/MrMurano/commands/assign.rb +57 -0
  33. data/lib/MrMurano/commands/businessList.rb +45 -0
  34. data/lib/MrMurano/commands/completion.rb +152 -0
  35. data/lib/MrMurano/commands/config.rb +67 -0
  36. data/lib/MrMurano/commands/content.rb +130 -0
  37. data/lib/MrMurano/commands/cors.rb +30 -0
  38. data/lib/MrMurano/commands/domain.rb +17 -0
  39. data/lib/MrMurano/commands/gb.rb +33 -0
  40. data/lib/MrMurano/commands/init.rb +138 -0
  41. data/lib/MrMurano/commands/keystore.rb +157 -0
  42. data/lib/MrMurano/commands/logs.rb +78 -0
  43. data/lib/MrMurano/commands/mock.rb +63 -0
  44. data/lib/MrMurano/commands/password.rb +88 -0
  45. data/lib/MrMurano/commands/postgresql.rb +41 -0
  46. data/lib/MrMurano/commands/product.rb +14 -0
  47. data/lib/MrMurano/commands/productCreate.rb +39 -0
  48. data/lib/MrMurano/commands/productDelete.rb +33 -0
  49. data/lib/MrMurano/commands/productDevice.rb +84 -0
  50. data/lib/MrMurano/commands/productDeviceIdCmds.rb +86 -0
  51. data/lib/MrMurano/commands/productList.rb +45 -0
  52. data/lib/MrMurano/commands/productWrite.rb +27 -0
  53. data/lib/MrMurano/commands/show.rb +80 -0
  54. data/lib/MrMurano/commands/solution.rb +14 -0
  55. data/lib/MrMurano/commands/solutionCreate.rb +39 -0
  56. data/lib/MrMurano/commands/solutionDelete.rb +34 -0
  57. data/lib/MrMurano/commands/solutionList.rb +45 -0
  58. data/lib/MrMurano/commands/status.rb +92 -0
  59. data/lib/MrMurano/commands/sync.rb +60 -0
  60. data/lib/MrMurano/commands/timeseries.rb +115 -0
  61. data/lib/MrMurano/commands/tsdb.rb +271 -0
  62. data/lib/MrMurano/commands/usage.rb +23 -0
  63. data/lib/MrMurano/commands/zshcomplete.erb +112 -0
  64. data/lib/MrMurano/commands.rb +32 -0
  65. data/lib/MrMurano/hash.rb +20 -0
  66. data/lib/MrMurano/http.rb +153 -0
  67. data/lib/MrMurano/makePretty.rb +75 -0
  68. data/lib/MrMurano/schema/pf-v1.0.0.yaml +114 -0
  69. data/lib/MrMurano/schema/sf-v0.2.0.yaml +77 -0
  70. data/lib/MrMurano/schema/sf-v0.3.0.yaml +78 -0
  71. data/lib/MrMurano/template/mock.erb +9 -0
  72. data/lib/MrMurano/template/projectFile.murano.erb +81 -0
  73. data/lib/MrMurano/verbosing.rb +99 -0
  74. data/lib/MrMurano/version.rb +4 -0
  75. data/lib/MrMurano.rb +20 -0
  76. data/spec/Account-Passwords_spec.rb +242 -0
  77. data/spec/Account_spec.rb +272 -0
  78. data/spec/ConfigFile_spec.rb +50 -0
  79. data/spec/ConfigMigrate_spec.rb +89 -0
  80. data/spec/Config_spec.rb +409 -0
  81. data/spec/Http_spec.rb +204 -0
  82. data/spec/MakePretties_spec.rb +118 -0
  83. data/spec/Mock_spec.rb +53 -0
  84. data/spec/ProductBase_spec.rb +113 -0
  85. data/spec/ProductContent_spec.rb +162 -0
  86. data/spec/ProductResources_spec.rb +329 -0
  87. data/spec/Product_1P_Device_spec.rb +202 -0
  88. data/spec/Product_1P_RPC_spec.rb +175 -0
  89. data/spec/Product_spec.rb +153 -0
  90. data/spec/ProjectFile_spec.rb +324 -0
  91. data/spec/Solution-Cors_spec.rb +164 -0
  92. data/spec/Solution-Endpoint_spec.rb +581 -0
  93. data/spec/Solution-File_spec.rb +212 -0
  94. data/spec/Solution-ServiceConfig_spec.rb +202 -0
  95. data/spec/Solution-ServiceDevice_spec.rb +176 -0
  96. data/spec/Solution-ServiceEventHandler_spec.rb +385 -0
  97. data/spec/Solution-ServiceModules_spec.rb +465 -0
  98. data/spec/Solution-UsersRoles_spec.rb +207 -0
  99. data/spec/Solution_spec.rb +92 -0
  100. data/spec/SyncRoot_spec.rb +83 -0
  101. data/spec/SyncUpDown_spec.rb +495 -0
  102. data/spec/Verbosing_spec.rb +279 -0
  103. data/spec/_workspace.rb +27 -0
  104. data/spec/cmd_assign_spec.rb +51 -0
  105. data/spec/cmd_business_spec.rb +59 -0
  106. data/spec/cmd_common.rb +72 -0
  107. data/spec/cmd_config_spec.rb +68 -0
  108. data/spec/cmd_content_spec.rb +71 -0
  109. data/spec/cmd_cors_spec.rb +50 -0
  110. data/spec/cmd_device_spec.rb +96 -0
  111. data/spec/cmd_domain_spec.rb +32 -0
  112. data/spec/cmd_init_spec.rb +30 -0
  113. data/spec/cmd_keystore_spec.rb +97 -0
  114. data/spec/cmd_password_spec.rb +62 -0
  115. data/spec/cmd_status_spec.rb +239 -0
  116. data/spec/cmd_syncdown_spec.rb +86 -0
  117. data/spec/cmd_syncup_spec.rb +62 -0
  118. data/spec/cmd_usage_spec.rb +36 -0
  119. data/spec/fixtures/.mrmuranorc +9 -0
  120. data/spec/fixtures/ProjectFiles/invalid.yaml +9 -0
  121. data/spec/fixtures/ProjectFiles/only_meta.yaml +24 -0
  122. data/spec/fixtures/ProjectFiles/with_routes.yaml +27 -0
  123. data/spec/fixtures/SolutionFiles/0.2.0.json +20 -0
  124. data/spec/fixtures/SolutionFiles/0.2.0_invalid.json +18 -0
  125. data/spec/fixtures/SolutionFiles/0.2.json +21 -0
  126. data/spec/fixtures/SolutionFiles/0.3.0.json +20 -0
  127. data/spec/fixtures/SolutionFiles/0.3.0_invalid.json +19 -0
  128. data/spec/fixtures/SolutionFiles/0.3.json +20 -0
  129. data/spec/fixtures/SolutionFiles/basic.json +20 -0
  130. data/spec/fixtures/SolutionFiles/secret.json +6 -0
  131. data/spec/fixtures/configfile +9 -0
  132. data/spec/fixtures/dumped_config +42 -0
  133. data/spec/fixtures/mrmuranorc_deleted_bob +8 -0
  134. data/spec/fixtures/mrmuranorc_tool_bob +3 -0
  135. data/spec/fixtures/product_spec_files/example.exoline.spec.yaml +116 -0
  136. data/spec/fixtures/product_spec_files/example.murano.spec.yaml +14 -0
  137. data/spec/fixtures/product_spec_files/gwe.exoline.spec.yaml +21 -0
  138. data/spec/fixtures/product_spec_files/gwe.murano.spec.yaml +16 -0
  139. data/spec/fixtures/product_spec_files/lightbulb-no-state.yaml +11 -0
  140. data/spec/fixtures/product_spec_files/lightbulb.yaml +14 -0
  141. data/spec/fixtures/roles-three.yaml +11 -0
  142. data/spec/fixtures/syncable_content/assets/icon.png +0 -0
  143. data/spec/fixtures/syncable_content/assets/index.html +0 -0
  144. data/spec/fixtures/syncable_content/assets/js/script.js +0 -0
  145. data/spec/fixtures/syncable_content/modules/table_util.lua +58 -0
  146. data/spec/fixtures/syncable_content/routes/manyRoutes.lua +11 -0
  147. data/spec/fixtures/syncable_content/routes/singleRoute.lua +5 -0
  148. data/spec/fixtures/syncable_content/services/devdata.lua +18 -0
  149. data/spec/fixtures/syncable_content/services/timers.lua +4 -0
  150. data/spec/spec_helper.rb +119 -0
  151. metadata +498 -0
@@ -0,0 +1,36 @@
1
+ require 'fileutils'
2
+ require 'open3'
3
+ require 'pathname'
4
+ require 'cmd_common'
5
+
6
+ RSpec.describe 'murano usage', :cmd, :needs_password do
7
+ include_context "CI_CMD"
8
+
9
+ before(:example) do
10
+ @project_name = rname('usageTest')
11
+ out, err, status = Open3.capture3(capcmd('murano', 'solution', 'create', @project_name, '--save'))
12
+ expect(err).to eq('')
13
+ expect(out.chomp).to match(/^[a-zA-Z0-9]+$/)
14
+ expect(status.exitstatus).to eq(0)
15
+ end
16
+ after(:example) do
17
+ out, err, status = Open3.capture3(capcmd('murano', 'solution', 'delete', @project_name))
18
+ expect(out).to eq('')
19
+ expect(err).to eq('')
20
+ expect(status.exitstatus).to eq(0)
21
+ end
22
+
23
+ it "show usage" do
24
+ out, err, status = Open3.capture3(capcmd('murano', 'usage'))
25
+ expect(err).to eq('')
26
+ olines = out.lines
27
+ expect(olines[0]).to match(/^(\+-+){5}\+$/)
28
+ expect(olines[1]).to match(/^\|\s+\| Quota\s+\| Daily\s+\| Monthly\s+\| Total\s+\|$/)
29
+ expect(olines[2]).to match(/^(\+-+){5}\+$/)
30
+ expect(olines[-1]).to match(/^(\+-+){5}\+$/)
31
+ expect(status.exitstatus).to eq(0)
32
+ end
33
+
34
+ end
35
+
36
+ # vim: set ai et sw=2 ts=2 :
@@ -0,0 +1,9 @@
1
+ [solution]
2
+ id = XXXXXXXXXX
3
+
4
+ [doThisTest]
5
+ bob = build
6
+
7
+ [s-fDEADBEEFGAEG]
8
+ spec = bobo.yaml
9
+
@@ -0,0 +1,9 @@
1
+ ---
2
+ info:
3
+ name: tested
4
+ summary: My little test project
5
+ authors: ["a person with <an@email.address>"]
6
+ version: 1.56.12
7
+ routes:
8
+ include: custom_api.lua
9
+ formatversion: 1.0.0
@@ -0,0 +1,24 @@
1
+ ---
2
+ # First up is some metadata about this project.
3
+ info:
4
+ # Nice short and easy. Also must be a valid domain name component.
5
+ name: tested
6
+
7
+ # Short one line summary of this project.
8
+ summary: My little test project
9
+
10
+ # Longer, multiple paragraph explanation.
11
+ description: |
12
+ There is a lot to say about my little project.
13
+
14
+ But I am not saying any of it right now.
15
+
16
+
17
+ # Who made this
18
+ authors: ["a person with <an@email.address>"]
19
+
20
+ # The version of the Project
21
+ version: 1.56.12
22
+
23
+
24
+ formatversion: 1.0.0
@@ -0,0 +1,27 @@
1
+ ---
2
+ # First up is some metadata about this project.
3
+ info:
4
+ # Nice short and easy. Also must be a valid domain name component.
5
+ name: tested
6
+
7
+ # Short one line summary of this project.
8
+ summary: My little test project
9
+
10
+ # Longer, multiple paragraph explanation.
11
+ description: |
12
+ There is a lot to say about my little project.
13
+
14
+ But I am not saying any of it right now.
15
+
16
+
17
+ # Who made this
18
+ authors: ["a person with <an@email.address>"]
19
+
20
+ # The version of the Project
21
+ version: 1.56.12
22
+
23
+ routes:
24
+ include:
25
+ - custom_api.lua
26
+
27
+ formatversion: 1.0.0
@@ -0,0 +1,20 @@
1
+ {
2
+ "default_page": "index.html",
3
+ "file_dir": "public",
4
+ "custom_api": "sample_api.lua",
5
+ "custom_api_hook": "_init",
6
+ "modules": {
7
+ "auth": "modules/util.lua",
8
+ "debug": "modules/debug.lua",
9
+ "listen": "modules/listen.lua"
10
+ },
11
+ "event_handler": {
12
+ "device": {
13
+ "datapoint": "event_handler/product.lua"
14
+ },
15
+ "timer": {
16
+ "timer": "event_handler/timer.lua"
17
+ }
18
+ },
19
+ "cors": {"origin": ["http://localhost:*"]}
20
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "default_page": "index.html",
3
+ "file_dir": "public",
4
+ "custom_api_hook": "_init",
5
+ "modules": {
6
+ "auth": "modules/util.lua",
7
+ "debug": "modules/debug.lua",
8
+ "listen": "modules/listen.lua"
9
+ },
10
+ "event_handler": {
11
+ "device": {
12
+ "datapoint": "event_handler/product.lua"
13
+ },
14
+ "timer": {
15
+ "timer": "event_handler/timer.lua"
16
+ }
17
+ }
18
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "default_page": "index.html",
3
+ "file_dir": "public",
4
+ "custom_api": "sample_api.lua",
5
+ "custom_api_hook": "_init",
6
+ "modules": {
7
+ "auth": "modules/util.lua",
8
+ "debug": "modules/debug.lua",
9
+ "listen": "modules/listen.lua"
10
+ },
11
+ "event_handler": {
12
+ "device": {
13
+ "datapoint": "event_handler/product.lua"
14
+ },
15
+ "timer": {
16
+ "timer": "event_handler/timer.lua"
17
+ }
18
+ },
19
+ "cors": {"origin": ["http://localhost:*"]},
20
+ "version": "0.2"
21
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "default_page": "index.html",
3
+ "assets": "public",
4
+ "routes": "sample_api.lua",
5
+ "modules": {
6
+ "auth": "modules/util.lua",
7
+ "debug": "modules/debug.lua",
8
+ "listen": "modules/listen.lua"
9
+ },
10
+ "services": {
11
+ "device": {
12
+ "datapoint": "event_handler/product.lua"
13
+ },
14
+ "timer": {
15
+ "timer": "event_handler/timer.lua"
16
+ }
17
+ },
18
+ "cors": {"origin": ["http://localhost:*"]},
19
+ "version": "0.3.0"
20
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "default_page": "index.html",
3
+ "assets": "public",
4
+ "modules": {
5
+ "auth": "modules/util.lua",
6
+ "debug": "modules/debug.lua",
7
+ "listen": "modules/listen.lua"
8
+ },
9
+ "services": {
10
+ "device": {
11
+ "datapoint": "event_handler/product.lua"
12
+ },
13
+ "timer": {
14
+ "timer": "event_handler/timer.lua"
15
+ }
16
+ },
17
+ "cors": {"origin": ["http://localhost:*"]},
18
+ "version": "0.3.0"
19
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "default_page": "index.html",
3
+ "assets": "public",
4
+ "routes": "sample_api.lua",
5
+ "modules": {
6
+ "auth": "modules/util.lua",
7
+ "debug": "modules/debug.lua",
8
+ "listen": "modules/listen.lua"
9
+ },
10
+ "services": {
11
+ "device": {
12
+ "datapoint": "event_handler/product.lua"
13
+ },
14
+ "timer": {
15
+ "timer": "event_handler/timer.lua"
16
+ }
17
+ },
18
+ "cors": {"origin": ["http://localhost:*"]},
19
+ "version": "0.3"
20
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "default_page": "index.html",
3
+ "file_dir": "public",
4
+ "custom_api": "sample_api.lua",
5
+ "custom_api_hook": "_init",
6
+ "modules": {
7
+ "auth": "modules/util.lua",
8
+ "debug": "modules/debug.lua",
9
+ "listen": "modules/listen.lua"
10
+ },
11
+ "event_handler": {
12
+ "device": {
13
+ "datapoint": "event_handler/product.lua"
14
+ },
15
+ "timer": {
16
+ "timer": "event_handler/timer.lua"
17
+ }
18
+ },
19
+ "cors": {"origin": ["http://localhost:*"]}
20
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "email": "test@user.account",
3
+ "password": "gibblygook",
4
+ "solution_id": "ABCDEFG",
5
+ "product_id": "HIJKLMNOP"
6
+ }
@@ -0,0 +1,9 @@
1
+ [solution]
2
+ id = XXXXXXXXXX
3
+
4
+ [test]
5
+ bob = build
6
+
7
+ [s-fDEADBEEFGAEG]
8
+ spec = bobo.yaml
9
+
@@ -0,0 +1,42 @@
1
+ [tool]
2
+ verbose = false
3
+ debug = false
4
+ dry = false
5
+ fullerror = false
6
+ outformat = best
7
+
8
+ [net]
9
+ host = bizapi.hosted.exosite.io
10
+
11
+ [location]
12
+ base = <%= File.realpath(@projectDir) %>
13
+ files = files
14
+ endpoints = routes
15
+ modules = modules
16
+ eventhandlers = services
17
+ specs = specs/resources.yaml
18
+ cors = cors.yaml
19
+
20
+ [sync]
21
+ bydefault = files
22
+
23
+ [files]
24
+ default_page = index.html
25
+ searchFor = **/*
26
+ ignoring =
27
+
28
+ [endpoints]
29
+ searchFor = {,../endpoints}/*.lua {,../endpoints}s/*/*.lua
30
+ ignoring = *_test.lua *_spec.lua .*
31
+
32
+ [eventhandler]
33
+ searchFor = *.lua */*.lua {../eventhandlers,../event_handler}/*.lua {../eventhandlers,../event_handler}/*/*.lua
34
+ ignoring = *_test.lua *_spec.lua .*
35
+ skiplist = websocket webservice device.service_call
36
+
37
+ [modules]
38
+ searchFor = *.lua */*.lua
39
+ ignoring = *_test.lua *_spec.lua .*
40
+
41
+ [diff]
42
+ cmd = <%= Gem.win_platform? ? 'fc' : 'diff -u' %>
@@ -0,0 +1,8 @@
1
+ [solution]
2
+ id = XXXXXXXXXX
3
+
4
+ [doThisTest]
5
+
6
+ [s-fDEADBEEFGAEG]
7
+ spec = bobo.yaml
8
+
@@ -0,0 +1,3 @@
1
+ [tool]
2
+ bob = build
3
+
@@ -0,0 +1,116 @@
1
+
2
+ # Example client specification file
3
+ # Specification files are in YAML format (a superset of JSON
4
+ # with more readable syntax and support for comments) and
5
+ # look like this. They may contain comments that begin
6
+ # with a # sign.
7
+
8
+ # Device client model information
9
+ device:
10
+ model: "myModel"
11
+ vendor: "myVendor"
12
+
13
+ # list of dataports that must exist
14
+ dataports:
15
+ # this the absolute minimum needed to specify a
16
+ # dataport.
17
+ - alias: mystring
18
+ # names are created, but not compared
19
+ - name: Temperature
20
+ # aliases, type, and format are created
21
+ # and compared
22
+ alias: temp
23
+ format: float
24
+ unit: °F
25
+ - name: LED Control
26
+ alias: led6
27
+ format: integer
28
+ - alias: config
29
+ # format should be string, and parseable JSON
30
+ format: string/json
31
+ # initial value (if no other value is read back)
32
+ initial: '{"text": "555-555-1234", "email": "jeff@555.com"}'
33
+ - alias: person
34
+ format: string/json
35
+ # JSON schema specified inline (http://json-schema.org/)
36
+ # format must be string/json to do validate
37
+ # you may also specify a string to reference schema in an
38
+ # external file. E.g. jsonschema: personschema.json
39
+ jsonschema: {"title": "Person Schema",
40
+ "type": "object",
41
+ "properties": {"name": {"type": "string"}},
42
+ "required": ["name"]}
43
+ initial: '{"name":"John Doe"}'
44
+ - alias: place
45
+ # An description of the dataport.
46
+ description: 'This is a place I have been'
47
+ # Dataports are not public by default,
48
+ # but if you want to share one with the world
49
+ public: true
50
+
51
+ # any dataports not listed but found in the client
52
+ # are ignored. The spec command does not delete things.
53
+
54
+ # list of script datarules that must exist
55
+ scripts:
56
+ # by default, scripts are datarules with
57
+ # names and aliases set to the file name
58
+ - file: test/files/helloworld.lua
59
+ # you can also set them explicitly
60
+ - file: test/files/helloworld.lua
61
+ alias: greeting
62
+ # you can also place lua code inline
63
+ - alias: singleLineScript
64
+ code: debug('hello from inside lua!')
65
+ # multiline lua scripts should start with | and
66
+ # be indented inside the "code:" key.
67
+ - alias: multilineScript
68
+ code: |
69
+ for x=1,10 do
70
+ debug('hello from for loop ' .. x)
71
+ end
72
+ # simple templating for script aliases and
73
+ # content is also supported.
74
+ - file: test/files/convert.lua
75
+ # if <% id %> is embedded in aliases
76
+ # or script content, the --ids parameter must
77
+ # be passed in. The spec command then expects
78
+ # a script or dataport resource per id passed, substituting
79
+ # each ID for <% id %>. In this example, if the command was:
80
+ #
81
+ # $ exo spec mysensor sensorspec.yaml --ids=A,B
82
+ #
83
+ # ...then the spec command looks for *two* script datarules
84
+ # in mysensor, with aliases convertA.lua and convertB.lua.
85
+ # Additionally, any instances of <% id %> in the content of
86
+ # convert.lua are substituted with A and B before being
87
+ # written to each script datarule.
88
+ #
89
+ alias: convert<% id %>.lua
90
+
91
+ # list of dispatches that must exist
92
+ dispatches:
93
+ - alias: myDispatch
94
+ # email | http_get | http_post | http_put | sms | xmpp
95
+ method: email
96
+ recipient: support@exosite.com
97
+ message: hello from Exoline spec example!
98
+ subject: hello!
99
+ # may be an RID or alias
100
+ subscribe: mystring
101
+
102
+ # list of simple datarules that must exist.
103
+ # scripts may go here too, but it's better to
104
+ # to put them under scripts (above)
105
+ datarules:
106
+ - alias: highTemp
107
+ format: float
108
+ subscribe: temp
109
+ rule: {
110
+ "simple": {
111
+ "comparison": "gt",
112
+ "constant": 80,
113
+ "repeat": true
114
+ }
115
+ }
116
+