rockette 0.0.1 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +21 -1
- data/Gemfile.lock +58 -14
- data/README.md +1 -0
- data/exe/rockette +0 -17
- data/lib/requirements.rb +21 -0
- data/lib/rockette/cli.rb +25 -6
- data/lib/rockette/commands/deploy.rb +54 -24
- data/lib/rockette/commands/export.rb +17 -18
- data/lib/rockette/commands/interactive.rb +24 -0
- data/lib/rockette/controller/configurator.rb +85 -0
- data/lib/rockette/controller/deployer.rb +106 -0
- data/lib/rockette/controller/exporter.rb +73 -0
- data/lib/rockette/controller/viewer.rb +108 -0
- data/lib/rockette/controller.rb +85 -0
- data/lib/rockette/rester.rb +79 -0
- data/lib/rockette/text_helper.rb +14 -1
- data/lib/rockette/version.rb +1 -1
- data/lib/rockette.rb +28 -84
- data/rockette.gemspec +9 -4
- data/templates/config.yml.erb +7 -16
- metadata +123 -3
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../text_helper"
|
4
|
+
require_relative "../commands/export"
|
5
|
+
|
6
|
+
module Rockette
|
7
|
+
# Export and download APEX application
|
8
|
+
class Exporter
|
9
|
+
include TextHelper
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@pastel = Pastel.new
|
13
|
+
@prompt = TTY::Prompt.new
|
14
|
+
@spinner = TTY::Spinner.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def launch!
|
18
|
+
@conf = Psych.load(File.read(CONF))
|
19
|
+
puts padder("Choose an application to download.")
|
20
|
+
puts
|
21
|
+
|
22
|
+
# input/action loop
|
23
|
+
loop do
|
24
|
+
enviros = Rockette::Viewer.new.environments
|
25
|
+
list = list_builder(enviros)
|
26
|
+
action = @prompt.select("Which environment is the app found?", list)
|
27
|
+
break if action == list.length
|
28
|
+
|
29
|
+
apps_url = enviros[action - 1]["deployment_api"]
|
30
|
+
choose_app(apps_url)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def do_export(app_id, apps_url)
|
35
|
+
response = @prompt.yes?("Would you like to enter a filename for the export?")
|
36
|
+
if response == true
|
37
|
+
file = @prompt.ask("Please enter your desired filename:")
|
38
|
+
options = Thor::CoreExt::HashWithIndifferentAccess.new "app_id" => app_id, "url" => apps_url, "file" => file,
|
39
|
+
"force" => true
|
40
|
+
else
|
41
|
+
puts padder("Saving under default file name: f#{app_id}.sql")
|
42
|
+
options = Thor::CoreExt::HashWithIndifferentAccess.new "app_id" => app_id, "url" => apps_url, "force" => true
|
43
|
+
end
|
44
|
+
Rockette::Commands::Export.new(options).execute
|
45
|
+
puts
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
def choose_app(apps_url)
|
51
|
+
loop do
|
52
|
+
apps = Rockette::Viewer.new.applications(apps_url)
|
53
|
+
list = list_builder(apps)
|
54
|
+
action = @prompt.slider("Download application => ", list, default: 1)
|
55
|
+
break if action == list.length
|
56
|
+
|
57
|
+
app_id = apps[action - 1]["application_id"]
|
58
|
+
do_export(app_id, apps_url)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def list_builder(array)
|
63
|
+
names = [] # Start building selection list
|
64
|
+
if array[0].key?("name")
|
65
|
+
array.each { |n| names << n["name"] }
|
66
|
+
else
|
67
|
+
array.each { |n| names << "#{n["application_name"]} App ID: #{n["application_id"]}" }
|
68
|
+
end
|
69
|
+
names << "Go Back"
|
70
|
+
names.map.with_index { |n, x| [n, x + 1] }.to_h
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../text_helper"
|
4
|
+
|
5
|
+
module Rockette
|
6
|
+
# View resources
|
7
|
+
class Viewer
|
8
|
+
include TextHelper
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@conf = Psych.load(File.read(CONF))
|
12
|
+
@pastel = Pastel.new
|
13
|
+
@prompt = TTY::Prompt.new
|
14
|
+
@spinner = TTY::Spinner.new # ("[:spinner] Loading APEX environments ...", format: pulse_2)
|
15
|
+
@view_actions = { "🏔 APEX Environments" => 1, "🎭 Registered Applications" => 2,
|
16
|
+
"🌎 Applications by Environment" => 3, "⬅️ Go Back" => 4 }
|
17
|
+
end
|
18
|
+
|
19
|
+
def launch!
|
20
|
+
puts padder("You can view environments or registered applications")
|
21
|
+
puts
|
22
|
+
|
23
|
+
# input/action loop
|
24
|
+
loop do
|
25
|
+
action = @prompt.select("Which would you like to see?", @view_actions)
|
26
|
+
break if action == 4
|
27
|
+
|
28
|
+
do_action(action)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def do_action(action)
|
33
|
+
case action
|
34
|
+
when 1
|
35
|
+
puts
|
36
|
+
environments unless @table_env
|
37
|
+
spinner(0.5)
|
38
|
+
puts @table_env.render(:unicode, resize: true, border: { style: :yellow })
|
39
|
+
puts
|
40
|
+
when 2
|
41
|
+
puts
|
42
|
+
registered unless @table_reg
|
43
|
+
puts @table_reg.render(:unicode, resize: true, border: { style: :yellow })
|
44
|
+
puts
|
45
|
+
when 3
|
46
|
+
puts
|
47
|
+
puts "This can take a while...hang tight!"
|
48
|
+
puts
|
49
|
+
all_apps unless @table_all_apps
|
50
|
+
puts @table_all_apps.render(:unicode, resize: true, border: { style: :yellow })
|
51
|
+
puts
|
52
|
+
else
|
53
|
+
puts "\nI don't understand that command.\n\n"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def ape_e_i(uri)
|
58
|
+
response = Rester.new(url: uri).rest_try
|
59
|
+
bail unless response
|
60
|
+
abort padder("#{uri} didn't work. Received: #{response.code}") unless response.code == 200
|
61
|
+
response
|
62
|
+
end
|
63
|
+
|
64
|
+
def applications(url)
|
65
|
+
uri = "#{url}deploy/apps"
|
66
|
+
response = ape_e_i(uri)
|
67
|
+
JSON.parse(response.body)["items"]
|
68
|
+
end
|
69
|
+
|
70
|
+
def environments
|
71
|
+
uri = "#{@conf["rockette"]["controller_url"]}deploy/environments/"
|
72
|
+
response = ape_e_i(uri)
|
73
|
+
@table_env = TTY::Table.new(header: ["Environment Name", "API", "Domain", "Owner", "Workspace"])
|
74
|
+
items = JSON.parse(response.body)["items"]
|
75
|
+
items.each { |h| @table_env << [h["name"], h["deployment_api"], h["domain"], h["owner"], h["workspace"]] }
|
76
|
+
end
|
77
|
+
|
78
|
+
def registered
|
79
|
+
uri = "#{@conf["rockette"]["controller_url"]}deploy/registered_apps/"
|
80
|
+
response = ape_e_i(uri)
|
81
|
+
@table_reg = TTY::Table.new(header: ["Registered Name", "Source App ID", "Source URI", "Target App ID",
|
82
|
+
"Target URI"])
|
83
|
+
JSON.parse(response.body)["items"].each do |h|
|
84
|
+
@table_reg << [h["registered_name"], h["src_app_id"], h["src_url"], h["tgt_app_id"], h["tgt_url"]]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def all_apps
|
89
|
+
environments unless @table_env
|
90
|
+
@table_all_apps = TTY::Table.new(header: ["Environment Name", "Application Name", "Application ID"])
|
91
|
+
@table_env.each do |env|
|
92
|
+
next if env[0] == "Environment Name"
|
93
|
+
|
94
|
+
apps = applications(env[1])
|
95
|
+
apps.each do |app|
|
96
|
+
@table_all_apps << [env[0], app["application_name"], app["application_id"]]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def spinner(dur=1)
|
102
|
+
@spinner.auto_spin
|
103
|
+
sleep(dur)
|
104
|
+
@spinner.stop
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "controller/configurator"
|
4
|
+
require_relative "controller/deployer"
|
5
|
+
require_relative "controller/exporter"
|
6
|
+
require_relative "controller/viewer"
|
7
|
+
require_relative "text_helper"
|
8
|
+
|
9
|
+
module Rockette
|
10
|
+
MAIN_ACTIONS = { "🔭 View Resources" => 1, "🚀 Deploy" => 2, "📥 Export" => 3,
|
11
|
+
"🛠 Configure" => 4, "❌ Quit" => 5 }.freeze
|
12
|
+
# Manage Rockette in interactive mode
|
13
|
+
class Controller
|
14
|
+
def initialize
|
15
|
+
@conf = Psych.load(File.read(CONF))
|
16
|
+
@pastel = Pastel.new
|
17
|
+
@prompt = TTY::Prompt.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def launch!
|
21
|
+
introduction
|
22
|
+
|
23
|
+
if @conf["rockette"]["check_for_url"] && @conf["rockette"]["controller_url"].length < 10
|
24
|
+
configurer = Rockette::Configurator.new
|
25
|
+
configurer.first_run
|
26
|
+
end
|
27
|
+
|
28
|
+
# input/action loop
|
29
|
+
loop do
|
30
|
+
action = actions
|
31
|
+
break if action == 5
|
32
|
+
|
33
|
+
do_action(action)
|
34
|
+
end
|
35
|
+
conclusion
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def introduction
|
41
|
+
font = TTY::Font.new(:starwars)
|
42
|
+
puts "-" * 85
|
43
|
+
puts
|
44
|
+
puts @pastel.yellow(font.write("Rockette"))
|
45
|
+
puts
|
46
|
+
puts "-" * 85
|
47
|
+
puts "Version: " + VERSION
|
48
|
+
puts "-" * 14
|
49
|
+
puts
|
50
|
+
puts "Rockette helps export and deploy APEX applications."
|
51
|
+
puts
|
52
|
+
end
|
53
|
+
|
54
|
+
def conclusion
|
55
|
+
puts
|
56
|
+
puts "-" * 85
|
57
|
+
puts @pastel.yellow("Have a good one!".upcase.center(85))
|
58
|
+
puts "-" * 85
|
59
|
+
puts
|
60
|
+
end
|
61
|
+
|
62
|
+
def actions
|
63
|
+
@prompt.select("What would you like to do?", MAIN_ACTIONS)
|
64
|
+
end
|
65
|
+
|
66
|
+
def do_action(action)
|
67
|
+
case action
|
68
|
+
when 1
|
69
|
+
viewer = Rockette::Viewer.new
|
70
|
+
viewer.launch!
|
71
|
+
when 2
|
72
|
+
deployer = Rockette::Deployer.new
|
73
|
+
deployer.launch!
|
74
|
+
when 3
|
75
|
+
exporter = Rockette::Exporter.new
|
76
|
+
exporter.launch!
|
77
|
+
when 4
|
78
|
+
configurer = Rockette::Configurator.new
|
79
|
+
configurer.launch!
|
80
|
+
else
|
81
|
+
puts "\nI don't understand that command.\n\n"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rockette
|
4
|
+
# configurable rest-client calls with error handling and auto retries
|
5
|
+
class Rester
|
6
|
+
VERBS = {
|
7
|
+
"delete" => :Delete,
|
8
|
+
"get" => :Get,
|
9
|
+
"post" => :Post,
|
10
|
+
"put" => :Put
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
def initialize(headers: {}, meth: "Get", params: {}, url: "https://array/", config: {})
|
14
|
+
@headers = headers
|
15
|
+
@meth = meth
|
16
|
+
@params = params
|
17
|
+
@url = url
|
18
|
+
@config = config
|
19
|
+
@config["timeout"] = @config["timeout"] ||= 30
|
20
|
+
end
|
21
|
+
|
22
|
+
def make_call
|
23
|
+
response = RestClient::Request.execute(headers: @headers,
|
24
|
+
method: VERBS[@meth.downcase], payload: @params,
|
25
|
+
timeout: @config["timeout"], url: @url, verify_ssl: false)
|
26
|
+
rescue SocketError, IOError => e
|
27
|
+
puts "#{e.class}: #{e.message}"
|
28
|
+
nil
|
29
|
+
rescue StandardError => e
|
30
|
+
e.response
|
31
|
+
else
|
32
|
+
response
|
33
|
+
end
|
34
|
+
|
35
|
+
def cookie
|
36
|
+
if @url =~ %r{auth/session}
|
37
|
+
response = make_call
|
38
|
+
raise "There was an issue getting a cookie!" unless response.code == 200
|
39
|
+
|
40
|
+
(response.cookies.map { |key, val| "#{key}=#{val}" })[0]
|
41
|
+
else
|
42
|
+
error_text("cookie", @url.to_s, "auth/session")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# use rest-client with retry
|
47
|
+
def rest_try(tries = 3)
|
48
|
+
tries.times do |i|
|
49
|
+
response = make_call
|
50
|
+
unless response.nil?
|
51
|
+
break response if (200..299).include? response.code
|
52
|
+
break response if i > 1
|
53
|
+
end
|
54
|
+
puts "Failed #{@meth} on #{@url}, retry...#{i + 1}"
|
55
|
+
sleep 3 unless i > 1
|
56
|
+
return nil if i > 1 # Handles socket errors, etc. where there is no response.
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def error_text(method_name, url, wanted)
|
63
|
+
{
|
64
|
+
"response" =>
|
65
|
+
"ERROR: Wrong url for the #{method_name} method.\n"\
|
66
|
+
"Sent: #{url}\n"\
|
67
|
+
"Expected: \"#{wanted}\" as part of the url.",
|
68
|
+
"status" => 400
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def responder(response)
|
73
|
+
{
|
74
|
+
"response" => JSON.parse(response.body),
|
75
|
+
"status" => response.code.to_i
|
76
|
+
}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/rockette/text_helper.rb
CHANGED
@@ -3,8 +3,9 @@
|
|
3
3
|
# Add common methods to Rockette module
|
4
4
|
module TextHelper
|
5
5
|
def bail
|
6
|
-
puts "Bailing,
|
6
|
+
puts "Bailing, a socket (network) or IO error occurred. Can you access the url from here?"
|
7
7
|
puts "Double check the url and make sure you have a network connection to the target."
|
8
|
+
puts
|
8
9
|
exit
|
9
10
|
end
|
10
11
|
|
@@ -12,6 +13,18 @@ module TextHelper
|
|
12
13
|
input unless input.nil?
|
13
14
|
end
|
14
15
|
|
16
|
+
def file_not_found
|
17
|
+
puts "File not found. Double check file name and place in ~/.rockette/exports, /usr/app, or /usr/local/app"
|
18
|
+
puts
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
|
22
|
+
def no_rockette_dir
|
23
|
+
puts "Unable to locate a suitable place for the .rockette directory."
|
24
|
+
puts "Please double check your home directory..."
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
|
15
28
|
def padder(str)
|
16
29
|
"\n#{str}\n"
|
17
30
|
end
|
data/lib/rockette/version.rb
CHANGED
data/lib/rockette.rb
CHANGED
@@ -1,91 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
3
|
+
require_relative "requirements"
|
4
|
+
|
5
|
+
# Set paths
|
6
|
+
APP_PATH = if File.exist?(File.join("/", "usr", "app"))
|
7
|
+
File.join("/", "usr", "app", ".rockette")
|
8
|
+
elsif File.exist?(Dir.home)
|
9
|
+
File.join(Dir.home, ".rockette")
|
10
|
+
else
|
11
|
+
no_rockette_dir
|
12
|
+
end
|
13
|
+
lib_path = File.expand_path("../lib", __dir__)
|
14
|
+
tem_path = File.expand_path("../templates", __dir__)
|
15
|
+
$LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
|
16
|
+
|
17
|
+
# Create directories and config file if needed
|
18
|
+
Dir.mkdir(APP_PATH) unless File.exist?(APP_PATH)
|
19
|
+
unless File.exist?(File.join(APP_PATH, "config.yml"))
|
20
|
+
template = File.read(File.join(tem_path, "config.yml.erb"))
|
21
|
+
data = ERB.new(template).result(binding)
|
22
|
+
File.write(File.join(APP_PATH, "config.yml"), data)
|
23
|
+
end
|
24
|
+
Dir.mkdir(File.join(APP_PATH, "exports")) unless File.exist?(File.join(APP_PATH, "exports"))
|
25
|
+
|
26
|
+
# Set config and export directory paths
|
27
|
+
CONF = File.join(APP_PATH, "config.yml")
|
28
|
+
ENV["THOR_SILENCE_DEPRECATION"] = "true"
|
29
|
+
EXPORT_DIR = File.join(APP_PATH, "exports")
|
11
30
|
|
12
31
|
# APEX deployment
|
13
32
|
module Rockette
|
14
33
|
class Error < StandardError; end
|
15
|
-
|
16
|
-
# rest-client calls with error handling and auto retries
|
17
|
-
class Rester
|
18
|
-
VERBS = {
|
19
|
-
"delete" => :Delete,
|
20
|
-
"get" => :Get,
|
21
|
-
"post" => :Post,
|
22
|
-
"put" => :Put
|
23
|
-
}.freeze
|
24
|
-
|
25
|
-
def initialize(headers: {}, meth: "Get", params: {}, url: "https://array/", config: {})
|
26
|
-
@headers = headers
|
27
|
-
@meth = meth
|
28
|
-
@params = params
|
29
|
-
@url = url
|
30
|
-
@config = config
|
31
|
-
@config["timeout"] = @config["timeout"] ||= 30
|
32
|
-
end
|
33
|
-
|
34
|
-
def make_call
|
35
|
-
response = RestClient::Request.execute(headers: @headers,
|
36
|
-
method: VERBS[@meth.downcase], payload: @params,
|
37
|
-
timeout: @config["timeout"], url: @url, verify_ssl: false)
|
38
|
-
rescue SocketError => e
|
39
|
-
puts "#{e.class}: #{e.message}"
|
40
|
-
nil
|
41
|
-
rescue StandardError => e
|
42
|
-
e.response
|
43
|
-
else
|
44
|
-
response
|
45
|
-
end
|
46
|
-
|
47
|
-
def cookie
|
48
|
-
if @url =~ %r{auth/session}
|
49
|
-
response = make_call
|
50
|
-
raise "There was an issue getting a cookie!" unless response.code == 200
|
51
|
-
|
52
|
-
(response.cookies.map { |key, val| "#{key}=#{val}" })[0]
|
53
|
-
else
|
54
|
-
error_text("cookie", @url.to_s, "auth/session")
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
# use rest-client with retry
|
59
|
-
def rest_try
|
60
|
-
3.times do |i|
|
61
|
-
response = make_call
|
62
|
-
unless response.nil?
|
63
|
-
break response if (200..299).include? response.code
|
64
|
-
break response if i > 1
|
65
|
-
end
|
66
|
-
puts "Failed #{@meth} on #{@url}, retry...#{i + 1}"
|
67
|
-
sleep 3 unless i > 1
|
68
|
-
return nil if i > 1 # Handles socket errors, etc. where there is no response.
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
|
74
|
-
def error_text(method_name, url, wanted)
|
75
|
-
{
|
76
|
-
"response" =>
|
77
|
-
"ERROR: Wrong url for the #{method_name} method.\n"\
|
78
|
-
"Sent: #{url}\n"\
|
79
|
-
"Expected: \"#{wanted}\" as part of the url.",
|
80
|
-
"status" => 400
|
81
|
-
}
|
82
|
-
end
|
83
|
-
|
84
|
-
def responder(response)
|
85
|
-
{
|
86
|
-
"response" => JSON.parse(response.body),
|
87
|
-
"status" => response.code.to_i
|
88
|
-
}
|
89
|
-
end
|
90
|
-
end
|
34
|
+
# Code in lib/rockette
|
91
35
|
end
|
data/rockette.gemspec
CHANGED
@@ -32,10 +32,15 @@ Gem::Specification.new do |spec|
|
|
32
32
|
# Dev dependencies
|
33
33
|
spec.add_development_dependency "pry", "~> 0.0"
|
34
34
|
|
35
|
-
#
|
35
|
+
# Dependencies
|
36
|
+
spec.add_dependency "oci", "~> 2.0"
|
37
|
+
spec.add_dependency "pastel", "~> 0.0"
|
36
38
|
spec.add_dependency "rest-client", "~> 2.0"
|
37
39
|
spec.add_dependency "thor", "~> 1.0"
|
38
|
-
|
39
|
-
|
40
|
-
|
40
|
+
spec.add_dependency "tty-config", "~> 0.0"
|
41
|
+
spec.add_dependency "tty-editor", "~> 0.0"
|
42
|
+
spec.add_dependency "tty-font", "~> 0.0"
|
43
|
+
spec.add_dependency "tty-prompt", "~> 0.0"
|
44
|
+
spec.add_dependency "tty-spinner", "~> 0.0"
|
45
|
+
spec.add_dependency "tty-table", "~> 0.0"
|
41
46
|
end
|
data/templates/config.yml.erb
CHANGED
@@ -1,17 +1,8 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
file_mimetype: text/plain
|
10
|
-
file_name: ''
|
11
|
-
file_type: sql
|
12
|
-
tags: ''
|
13
|
-
target_use: push_deployment
|
14
|
-
deploy_body:
|
15
|
-
app_id_src: ''
|
16
|
-
app_id_tgt: ''
|
17
|
-
blob_url: https://local.data_loader
|
2
|
+
token_body:
|
3
|
+
grant_type: client_credentials
|
4
|
+
token_hdrs:
|
5
|
+
Content-Type: application/x-www-form-urlencoded
|
6
|
+
rockette:
|
7
|
+
check_for_url: true
|
8
|
+
controller_url: ''
|