elasticsearch-api 7.3.0 → 7.5.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 +4 -4
- data/Rakefile +16 -23
- data/elasticsearch-api.gemspec +1 -1
- data/lib/elasticsearch/api/actions/abort_benchmark.rb +1 -2
- data/lib/elasticsearch/api/actions/benchmark.rb +2 -3
- data/lib/elasticsearch/api/actions/bulk.rb +35 -68
- data/lib/elasticsearch/api/actions/cat/aliases.rb +30 -52
- data/lib/elasticsearch/api/actions/cat/allocation.rb +36 -50
- data/lib/elasticsearch/api/actions/cat/count.rb +25 -46
- data/lib/elasticsearch/api/actions/cat/fielddata.rb +31 -33
- data/lib/elasticsearch/api/actions/cat/health.rb +27 -37
- data/lib/elasticsearch/api/actions/cat/help.rb +14 -10
- data/lib/elasticsearch/api/actions/cat/indices.rb +45 -62
- data/lib/elasticsearch/api/actions/cat/master.rb +24 -34
- data/lib/elasticsearch/api/actions/cat/nodeattrs.rb +20 -16
- data/lib/elasticsearch/api/actions/cat/nodes.rb +34 -46
- data/lib/elasticsearch/api/actions/cat/params_registry.rb +0 -1
- data/lib/elasticsearch/api/actions/cat/pending_tasks.rb +29 -35
- data/lib/elasticsearch/api/actions/cat/plugins.rb +21 -18
- data/lib/elasticsearch/api/actions/cat/recovery.rb +40 -56
- data/lib/elasticsearch/api/actions/cat/repositories.rb +21 -24
- data/lib/elasticsearch/api/actions/cat/segments.rb +29 -22
- data/lib/elasticsearch/api/actions/cat/shards.rb +38 -59
- data/lib/elasticsearch/api/actions/cat/snapshots.rb +32 -27
- data/lib/elasticsearch/api/actions/cat/tasks.rb +25 -19
- data/lib/elasticsearch/api/actions/cat/templates.rb +26 -18
- data/lib/elasticsearch/api/actions/cat/thread_pool.rb +36 -46
- data/lib/elasticsearch/api/actions/clear_scroll.rb +21 -8
- data/lib/elasticsearch/api/actions/cluster/allocation_explain.rb +15 -13
- data/lib/elasticsearch/api/actions/cluster/get_settings.rb +18 -18
- data/lib/elasticsearch/api/actions/cluster/health.rb +42 -44
- data/lib/elasticsearch/api/actions/cluster/params_registry.rb +0 -1
- data/lib/elasticsearch/api/actions/cluster/pending_tasks.rb +15 -17
- data/lib/elasticsearch/api/actions/cluster/put_settings.rb +20 -17
- data/lib/elasticsearch/api/actions/cluster/remote_info.rb +8 -6
- data/lib/elasticsearch/api/actions/cluster/reroute.rb +25 -37
- data/lib/elasticsearch/api/actions/cluster/state.rb +37 -41
- data/lib/elasticsearch/api/actions/cluster/stats.rb +20 -12
- data/lib/elasticsearch/api/actions/count.rb +52 -39
- data/lib/elasticsearch/api/actions/create.rb +25 -26
- data/lib/elasticsearch/api/actions/delete.rb +43 -36
- data/lib/elasticsearch/api/actions/delete_by_query.rb +74 -62
- data/lib/elasticsearch/api/actions/delete_by_query_rethrottle.rb +39 -0
- data/lib/elasticsearch/api/actions/delete_by_rethrottle.rb +4 -3
- data/lib/elasticsearch/api/actions/delete_script.rb +19 -13
- data/lib/elasticsearch/api/actions/exists.rb +44 -34
- data/lib/elasticsearch/api/actions/exists_source.rb +43 -25
- data/lib/elasticsearch/api/actions/explain.rb +46 -51
- data/lib/elasticsearch/api/actions/field_caps.rb +27 -20
- data/lib/elasticsearch/api/actions/get.rb +42 -41
- data/lib/elasticsearch/api/actions/get_script.rb +19 -14
- data/lib/elasticsearch/api/actions/get_source.rb +38 -44
- data/lib/elasticsearch/api/actions/index.rb +60 -89
- data/lib/elasticsearch/api/actions/indices/analyze.rb +19 -48
- data/lib/elasticsearch/api/actions/indices/clear_cache.rb +29 -41
- data/lib/elasticsearch/api/actions/indices/clone.rb +50 -0
- data/lib/elasticsearch/api/actions/indices/close.rb +28 -32
- data/lib/elasticsearch/api/actions/indices/create.rb +22 -70
- data/lib/elasticsearch/api/actions/indices/delete.rb +23 -35
- data/lib/elasticsearch/api/actions/indices/delete_alias.rb +24 -20
- data/lib/elasticsearch/api/actions/indices/delete_template.rb +18 -20
- data/lib/elasticsearch/api/actions/indices/exists.rb +32 -34
- data/lib/elasticsearch/api/actions/indices/exists_alias.rb +33 -29
- data/lib/elasticsearch/api/actions/indices/exists_template.rb +21 -16
- data/lib/elasticsearch/api/actions/indices/exists_type.rb +29 -29
- data/lib/elasticsearch/api/actions/indices/flush.rb +28 -28
- data/lib/elasticsearch/api/actions/indices/flush_synced.rb +23 -12
- data/lib/elasticsearch/api/actions/indices/forcemerge.rb +26 -41
- data/lib/elasticsearch/api/actions/indices/freeze.rb +7 -8
- data/lib/elasticsearch/api/actions/indices/get.rb +25 -19
- data/lib/elasticsearch/api/actions/indices/get_alias.rb +31 -23
- data/lib/elasticsearch/api/actions/indices/get_field_mapping.rb +42 -38
- data/lib/elasticsearch/api/actions/indices/get_mapping.rb +39 -31
- data/lib/elasticsearch/api/actions/indices/get_settings.rb +34 -37
- data/lib/elasticsearch/api/actions/indices/get_template.rb +21 -23
- data/lib/elasticsearch/api/actions/indices/get_upgrade.rb +48 -0
- data/lib/elasticsearch/api/actions/indices/open.rb +24 -21
- data/lib/elasticsearch/api/actions/indices/params_registry.rb +0 -1
- data/lib/elasticsearch/api/actions/indices/put_alias.rb +25 -27
- data/lib/elasticsearch/api/actions/indices/put_mapping.rb +40 -39
- data/lib/elasticsearch/api/actions/indices/put_settings.rb +30 -37
- data/lib/elasticsearch/api/actions/indices/put_template.rb +23 -23
- data/lib/elasticsearch/api/actions/indices/recovery.rb +20 -23
- data/lib/elasticsearch/api/actions/indices/refresh.rb +22 -26
- data/lib/elasticsearch/api/actions/indices/rollover.rb +28 -19
- data/lib/elasticsearch/api/actions/indices/segments.rb +24 -17
- data/lib/elasticsearch/api/actions/indices/shard_stores.rb +27 -15
- data/lib/elasticsearch/api/actions/indices/shrink.rb +23 -17
- data/lib/elasticsearch/api/actions/indices/split.rb +23 -17
- data/lib/elasticsearch/api/actions/indices/stats.rb +53 -104
- data/lib/elasticsearch/api/actions/indices/unfreeze.rb +7 -7
- data/lib/elasticsearch/api/actions/indices/update_aliases.rb +15 -32
- data/lib/elasticsearch/api/actions/indices/upgrade.rb +26 -16
- data/lib/elasticsearch/api/actions/indices/validate_query.rb +46 -59
- data/lib/elasticsearch/api/actions/info.rb +9 -6
- data/lib/elasticsearch/api/actions/ingest/delete_pipeline.rb +18 -12
- data/lib/elasticsearch/api/actions/ingest/get_pipeline.rb +21 -11
- data/lib/elasticsearch/api/actions/ingest/params_registry.rb +0 -1
- data/lib/elasticsearch/api/actions/ingest/processor_grok.rb +12 -13
- data/lib/elasticsearch/api/actions/ingest/put_pipeline.rb +19 -14
- data/lib/elasticsearch/api/actions/ingest/simulate.rb +23 -13
- data/lib/elasticsearch/api/actions/mget.rb +37 -44
- data/lib/elasticsearch/api/actions/msearch.rb +54 -54
- data/lib/elasticsearch/api/actions/msearch_template.rb +46 -35
- data/lib/elasticsearch/api/actions/mtermvectors.rb +39 -35
- data/lib/elasticsearch/api/actions/nodes/hot_threads.rb +30 -25
- data/lib/elasticsearch/api/actions/nodes/info.rb +29 -63
- data/lib/elasticsearch/api/actions/nodes/params_registry.rb +0 -1
- data/lib/elasticsearch/api/actions/nodes/reload_secure_settings.rb +24 -22
- data/lib/elasticsearch/api/actions/nodes/shutdown.rb +4 -4
- data/lib/elasticsearch/api/actions/nodes/stats.rb +43 -36
- data/lib/elasticsearch/api/actions/nodes/usage.rb +31 -10
- data/lib/elasticsearch/api/actions/params_registry.rb +0 -1
- data/lib/elasticsearch/api/actions/ping.rb +12 -13
- data/lib/elasticsearch/api/actions/put_script.rb +26 -30
- data/lib/elasticsearch/api/actions/rank_eval.rb +24 -15
- data/lib/elasticsearch/api/actions/reindex.rb +25 -48
- data/lib/elasticsearch/api/actions/reindex_rethrottle.rb +18 -12
- data/lib/elasticsearch/api/actions/remote/info.rb +1 -2
- data/lib/elasticsearch/api/actions/render_search_template.rb +17 -15
- data/lib/elasticsearch/api/actions/scripts_painless_execute.rb +8 -6
- data/lib/elasticsearch/api/actions/scroll.rb +28 -51
- data/lib/elasticsearch/api/actions/search.rb +104 -165
- data/lib/elasticsearch/api/actions/search_shards.rb +31 -28
- data/lib/elasticsearch/api/actions/search_template.rb +48 -40
- data/lib/elasticsearch/api/actions/snapshot/cleanup_repository.rb +44 -0
- data/lib/elasticsearch/api/actions/snapshot/create.rb +22 -29
- data/lib/elasticsearch/api/actions/snapshot/create_repository.rb +19 -24
- data/lib/elasticsearch/api/actions/snapshot/delete.rb +20 -22
- data/lib/elasticsearch/api/actions/snapshot/delete_repository.rb +16 -18
- data/lib/elasticsearch/api/actions/snapshot/get.rb +22 -31
- data/lib/elasticsearch/api/actions/snapshot/get_repository.rb +21 -25
- data/lib/elasticsearch/api/actions/snapshot/params_registry.rb +0 -1
- data/lib/elasticsearch/api/actions/snapshot/restore.rb +21 -32
- data/lib/elasticsearch/api/actions/snapshot/status.rb +23 -24
- data/lib/elasticsearch/api/actions/snapshot/verify_repository.rb +18 -13
- data/lib/elasticsearch/api/actions/tasks/cancel.rb +19 -13
- data/lib/elasticsearch/api/actions/tasks/get.rb +16 -13
- data/lib/elasticsearch/api/actions/tasks/list.rb +20 -18
- data/lib/elasticsearch/api/actions/tasks/params_registry.rb +0 -1
- data/lib/elasticsearch/api/actions/termvectors.rb +52 -70
- data/lib/elasticsearch/api/actions/update.rb +53 -98
- data/lib/elasticsearch/api/actions/update_by_query.rb +77 -66
- data/lib/elasticsearch/api/actions/update_by_query_rethrottle.rb +17 -11
- data/lib/elasticsearch/api/version.rb +1 -1
- data/spec/elasticsearch/api/actions/cat/segments_spec.rb +9 -11
- data/spec/elasticsearch/api/actions/cluster/put_settings_spec.rb +1 -1
- data/spec/elasticsearch/api/actions/count_spec.rb +8 -8
- data/spec/elasticsearch/api/actions/delete_by_query_spec.rb +12 -7
- data/spec/elasticsearch/api/actions/delete_script_spec.rb +0 -14
- data/spec/elasticsearch/api/actions/exists_document_spec.rb +1 -1
- data/spec/elasticsearch/api/actions/get_document_source_spec.rb +1 -1
- data/spec/elasticsearch/api/actions/index_document_spec.rb +6 -15
- data/spec/elasticsearch/api/actions/indices/analyze_spec.rb +0 -4
- data/spec/elasticsearch/api/actions/indices/clear_cache_spec.rb +1 -5
- data/spec/elasticsearch/api/actions/indices/clone_spec.rb +109 -0
- data/spec/elasticsearch/api/actions/indices/delete_alias_spec.rb +2 -2
- data/spec/elasticsearch/api/actions/indices/get_spec.rb +0 -11
- data/spec/elasticsearch/api/actions/indices/put_alias_spec.rb +3 -3
- data/spec/elasticsearch/api/actions/indices/put_mapping_spec.rb +3 -3
- data/spec/elasticsearch/api/actions/indices/stats_spec.rb +0 -1
- data/spec/elasticsearch/api/actions/nodes/hot_threads_spec.rb +3 -3
- data/spec/elasticsearch/api/actions/put_script_spec.rb +20 -15
- data/spec/elasticsearch/api/actions/render_search_template_spec.rb +23 -7
- data/spec/elasticsearch/api/actions/scroll_spec.rb +37 -0
- data/spec/elasticsearch/api/actions/search_spec.rb +0 -15
- data/spec/elasticsearch/api/actions/tasks/list_spec.rb +0 -11
- data/spec/elasticsearch/api/actions/update_document_spec.rb +3 -7
- data/spec/elasticsearch/api/rest_api_yaml_spec.rb +1 -1
- data/spec/rest_yaml_tests_helper.rb +12 -0
- data/utils/Gemfile +1 -0
- data/utils/Thorfile +0 -1
- data/utils/thor/generate_source.rb +192 -85
- data/utils/thor/generator/endpoint_specifics.rb +157 -0
- data/utils/thor/generator/files_helper.rb +37 -0
- data/utils/thor/lister.rb +3 -4
- data/utils/thor/templates/_documentation_top.erb +27 -0
- data/utils/thor/templates/_method_setup.erb +35 -0
- data/utils/thor/templates/_params_registry.erb +12 -0
- data/utils/thor/templates/_perform_request.erb +37 -0
- data/utils/thor/templates/method.erb +59 -0
- data/utils/thor/templates/{ruby/test.erb → test.erb} +0 -0
- data/utils/thor/templates/{ruby/test_helper.rb → test_helper.rb} +0 -0
- metadata +31 -14
- data/spec/elasticsearch/api/actions/scoll_spec.rb +0 -21
- data/utils/thor/generate_api.rb +0 -193
- data/utils/thor/templates/ruby/method.erb +0 -62
|
@@ -53,16 +53,12 @@ describe 'client#update' do
|
|
|
53
53
|
'foo/bar/1/_update'
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
let(:params) do
|
|
57
|
-
{ version: 100 }
|
|
58
|
-
end
|
|
59
|
-
|
|
60
56
|
let(:body) do
|
|
61
57
|
{}
|
|
62
58
|
end
|
|
63
59
|
|
|
64
60
|
it 'performs the request' do
|
|
65
|
-
expect(client_double.update(index: 'foo', type: 'bar', id: '1',
|
|
61
|
+
expect(client_double.update(index: 'foo', type: 'bar', id: '1', body: {}))
|
|
66
62
|
end
|
|
67
63
|
end
|
|
68
64
|
|
|
@@ -98,14 +94,14 @@ describe 'client#update' do
|
|
|
98
94
|
|
|
99
95
|
it 'raises it to the user' do
|
|
100
96
|
expect {
|
|
101
|
-
client.update(index: 'foo', type: 'bar', id: 'XXX')
|
|
97
|
+
client.update(index: 'foo', type: 'bar', id: 'XXX', body: {})
|
|
102
98
|
}.to raise_exception(NotFound)
|
|
103
99
|
end
|
|
104
100
|
|
|
105
101
|
context 'when the :ignore parameter is specified' do
|
|
106
102
|
|
|
107
103
|
it 'does not raise the error to the user' do
|
|
108
|
-
expect(client.update(index: 'foo', type: 'bar', id: 'XXX', ignore: 404)).to eq(false)
|
|
104
|
+
expect(client.update(index: 'foo', type: 'bar', id: 'XXX', body: {}, ignore: 404)).to eq(false)
|
|
109
105
|
end
|
|
110
106
|
end
|
|
111
107
|
end
|
|
@@ -18,7 +18,7 @@ describe 'Rest API YAML tests' do
|
|
|
18
18
|
context "#{test.description}" do
|
|
19
19
|
|
|
20
20
|
if test.skip_test?(ADMIN_CLIENT)
|
|
21
|
-
skip 'Test contains feature(s) not yet
|
|
21
|
+
skip 'Test contains feature(s) not yet supported or version is not satisfied'
|
|
22
22
|
|
|
23
23
|
else
|
|
24
24
|
|
|
@@ -72,6 +72,18 @@ skipped_tests << { file: 'delete/70_mix_typeless_typeful.yml',
|
|
|
72
72
|
skipped_tests << { file: 'cat.templates/10_basic.yml',
|
|
73
73
|
description: '*' }
|
|
74
74
|
|
|
75
|
+
# node_selector is not supported yet
|
|
76
|
+
skipped_tests << { file: 'cat.aliases/10_basic.yml',
|
|
77
|
+
description: '*' }
|
|
78
|
+
|
|
79
|
+
# Responses are there but not equal (eg.: yellow status)
|
|
80
|
+
skipped_tests << { file: 'cluster.health/10_basic.yml',
|
|
81
|
+
description: 'cluster health with closed index (pre 7.2.0)' }
|
|
82
|
+
|
|
83
|
+
# Regular expression not catching exact match:
|
|
84
|
+
skipped_tests << { file: 'cat.indices/10_basic.yml',
|
|
85
|
+
description: 'Test cat indices output for closed index (pre 7.2.0)' }
|
|
86
|
+
|
|
75
87
|
SKIPPED_TESTS = skipped_tests
|
|
76
88
|
|
|
77
89
|
# The directory of rest api YAML files.
|
data/utils/Gemfile
CHANGED
data/utils/Thorfile
CHANGED
|
@@ -2,6 +2,5 @@
|
|
|
2
2
|
# Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
|
|
3
3
|
# See the LICENSE file in the project root for more information
|
|
4
4
|
|
|
5
|
-
require File.expand_path('./thor/generate_api')
|
|
6
5
|
require File.expand_path('./thor/generate_source')
|
|
7
6
|
require File.expand_path('./thor/lister')
|
|
@@ -5,138 +5,245 @@
|
|
|
5
5
|
# encoding: UTF-8
|
|
6
6
|
|
|
7
7
|
require 'thor'
|
|
8
|
-
|
|
9
8
|
require 'pathname'
|
|
10
9
|
require 'active_support/core_ext/hash/deep_merge'
|
|
11
10
|
require 'active_support/inflector'
|
|
12
11
|
require 'multi_json'
|
|
13
12
|
require 'coderay'
|
|
14
13
|
require 'pry'
|
|
14
|
+
require_relative 'generator/files_helper'
|
|
15
|
+
require_relative 'generator/endpoint_specifics'
|
|
15
16
|
|
|
16
17
|
module Elasticsearch
|
|
17
|
-
|
|
18
18
|
module API
|
|
19
|
-
|
|
20
19
|
# A command line application based on [Thor](https://github.com/wycats/thor),
|
|
21
20
|
# which will read the JSON API spec file(s), and generate
|
|
22
21
|
# the Ruby source code (one file per API endpoint) with correct
|
|
23
22
|
# module namespace, method names, and RDoc documentation,
|
|
24
23
|
# as well as test files for each endpoint.
|
|
25
24
|
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
25
|
+
# Specific exceptions and code snippets that need to be included are written
|
|
26
|
+
# in EndpointSpecifics (generator/endpoint_specifics) and the module is included
|
|
27
|
+
# here.
|
|
29
28
|
#
|
|
30
29
|
class SourceGenerator < Thor
|
|
31
30
|
namespace 'api:code'
|
|
32
|
-
|
|
33
31
|
include Thor::Actions
|
|
32
|
+
include EndpointSpecifics
|
|
34
33
|
|
|
35
|
-
__root = Pathname(
|
|
36
|
-
|
|
37
|
-
desc "generate", "Generate source code and tests from the REST API JSON specification"
|
|
38
|
-
method_option :language, default: 'ruby', desc: 'Programming language'
|
|
39
|
-
method_option :force, type: :boolean, default: false, desc: 'Overwrite the output'
|
|
40
|
-
method_option :verbose, type: :boolean, default: false, desc: 'Output more information'
|
|
41
|
-
method_option :input, default: File.expand_path('../../../../tmp/elasticsearch/rest-api-spec/api/**/*.json', __FILE__), desc: 'Path to directory with JSON API specs'
|
|
42
|
-
method_option :output, default: File.expand_path('../../../tmp/out', __FILE__), desc: 'Path to output directory'
|
|
43
|
-
|
|
44
|
-
def generate(*files)
|
|
45
|
-
self.class.source_root File.expand_path('../', __FILE__)
|
|
46
|
-
|
|
47
|
-
@input = Pathname(options[:input])
|
|
48
|
-
@output = Pathname(options[:output])
|
|
34
|
+
__root = Pathname(File.expand_path('../../..', __FILE__))
|
|
49
35
|
|
|
50
|
-
|
|
51
|
-
|
|
36
|
+
desc 'generate', 'Generate source code and tests from the REST API JSON specification'
|
|
37
|
+
method_option :verbose, type: :boolean, default: false, desc: 'Output more information'
|
|
38
|
+
method_option :tests, type: :boolean, default: false, desc: 'Generate test files'
|
|
39
|
+
method_option :xpack, type: :boolean, default: false, desc: 'Generate X-Pack'
|
|
52
40
|
|
|
53
|
-
|
|
41
|
+
def generate
|
|
42
|
+
self.class.source_root File.expand_path(__dir__)
|
|
43
|
+
@xpack = options[:xpack]
|
|
54
44
|
|
|
55
|
-
|
|
56
|
-
|
|
45
|
+
@input = FilesHelper.input_dir(@xpack)
|
|
46
|
+
@output = FilesHelper.output_dir(@xpack)
|
|
57
47
|
|
|
58
|
-
|
|
59
|
-
@
|
|
60
|
-
@
|
|
48
|
+
FilesHelper.files(@xpack).each do |filepath|
|
|
49
|
+
@path = Pathname(@input.join(filepath))
|
|
50
|
+
@json = MultiJson.load(File.read(@path))
|
|
51
|
+
@spec = @json.values.first
|
|
61
52
|
say_status 'json', @path, :yellow
|
|
62
53
|
|
|
63
54
|
@spec['url'] ||= {}
|
|
64
|
-
@spec['url']['parts'] ||= []
|
|
65
|
-
@spec['url']['params'] ||= {}
|
|
66
55
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
@
|
|
70
|
-
@namespace_depth = @full_namespace.size > 0 ? @full_namespace.size-1 : 0
|
|
56
|
+
@endpoint_name = @json.keys.first
|
|
57
|
+
@full_namespace = __full_namespace
|
|
58
|
+
@namespace_depth = @full_namespace.size > 0 ? @full_namespace.size - 1 : 0
|
|
71
59
|
@module_namespace = @full_namespace[0, @namespace_depth]
|
|
72
60
|
@method_name = @full_namespace.last
|
|
73
|
-
@
|
|
74
|
-
@
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
else
|
|
83
|
-
@spec['url']['path'].gsub(/^\//, '')
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# -- Ruby files
|
|
87
|
-
|
|
88
|
-
@path_to_file = @output.join('api').join( @module_namespace.join('/') ).join("#{@method_name}.rb")
|
|
89
|
-
|
|
90
|
-
empty_directory @output.join('api').join( @module_namespace.join('/') )
|
|
91
|
-
|
|
92
|
-
template "templates/#{options[:language]}/method.erb", @path_to_file
|
|
93
|
-
|
|
94
|
-
if options[:verbose]
|
|
95
|
-
colorized_output = CodeRay.scan_file(@path_to_file, :ruby).terminal
|
|
96
|
-
lines = colorized_output.split("\n")
|
|
97
|
-
say_status options[:language].downcase,
|
|
98
|
-
lines.first + "\n" + lines[1, lines.size].map { |l| ' '*14 + l }.join("\n"),
|
|
99
|
-
:yellow
|
|
100
|
-
end
|
|
61
|
+
@parts = __endpoint_parts
|
|
62
|
+
@params = @spec['params'] || {}
|
|
63
|
+
@specific_params = specific_params(@module_namespace.first) # See EndpointSpecifics
|
|
64
|
+
@http_method = __http_method
|
|
65
|
+
@paths = @spec['url']['paths'].map { |b| b['path'] }
|
|
66
|
+
# Using Ruby's safe operator on array:
|
|
67
|
+
@deprecation_note = @spec['url']['paths'].last&.[]('deprecated')
|
|
68
|
+
@http_path = __http_path
|
|
69
|
+
@required_parts = __required_parts
|
|
101
70
|
|
|
102
|
-
|
|
71
|
+
@module_namespace.shift if @module_namespace.first == 'xpack'
|
|
72
|
+
@path_to_file = @output.join(@module_namespace.join('/')).join("#{@method_name}.rb")
|
|
73
|
+
dir = @output.join(@module_namespace.join('/'))
|
|
74
|
+
empty_directory(dir, verbose: false)
|
|
103
75
|
|
|
104
|
-
|
|
105
|
-
@
|
|
76
|
+
# Write the file with the ERB template:
|
|
77
|
+
template('templates/method.erb', @path_to_file, force: true)
|
|
106
78
|
|
|
107
|
-
|
|
108
|
-
template "templates/#{options[:language]}/test.erb", @test_file
|
|
79
|
+
print_source_code(@path_to_file) if options[:verbose]
|
|
109
80
|
|
|
110
|
-
if options[:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
say_status options[:language].downcase,
|
|
114
|
-
lines.first + "\n" + lines[1, lines.size].map { |l| ' '*14 + l }.join("\n"),
|
|
115
|
-
:yellow
|
|
116
|
-
say '▬'*terminal_width
|
|
117
|
-
end
|
|
81
|
+
generate_tests if options[:tests]
|
|
82
|
+
|
|
83
|
+
puts
|
|
118
84
|
end
|
|
119
85
|
|
|
120
|
-
|
|
86
|
+
run_rubocop
|
|
121
87
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
say_status 'tree',
|
|
125
|
-
lines.first + "\n" + lines[1, lines.size].map { |l| ' '*14 + l }.join("\n")
|
|
126
|
-
end
|
|
88
|
+
# -- Tree output
|
|
89
|
+
print_tree if options[:verbose]
|
|
127
90
|
end
|
|
128
91
|
|
|
129
92
|
private
|
|
130
93
|
|
|
94
|
+
def __full_namespace
|
|
95
|
+
names = @endpoint_name.split('.')
|
|
96
|
+
if @xpack
|
|
97
|
+
names = (names.first == 'xpack' ? names : ['xpack', names].flatten)
|
|
98
|
+
# Return an array with 'ml' renamed to 'machine_learning' and 'ilm' to
|
|
99
|
+
# 'index_lifecycle_management'
|
|
100
|
+
names.map do |name|
|
|
101
|
+
name
|
|
102
|
+
.gsub(/^ml$/, 'machine_learning')
|
|
103
|
+
.gsub(/^ilm$/, 'index_lifecycle_management')
|
|
104
|
+
end
|
|
105
|
+
else
|
|
106
|
+
names
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
131
110
|
# Create the hierarchy of directories based on API namespaces
|
|
132
111
|
#
|
|
133
112
|
def __create_directories(key, value)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
113
|
+
return if value['documentation']
|
|
114
|
+
|
|
115
|
+
empty_directory @output.join(key)
|
|
116
|
+
create_directory_hierarchy * value.to_a.first
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Extract parts from each path
|
|
120
|
+
#
|
|
121
|
+
def __endpoint_parts
|
|
122
|
+
parts = @spec['url']['paths'].select do |a|
|
|
123
|
+
a.keys.include?('parts')
|
|
124
|
+
end.map do |path|
|
|
125
|
+
path&.[]('parts')
|
|
126
|
+
end
|
|
127
|
+
(parts.inject(&:merge) || [])
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def __http_method
|
|
131
|
+
case @endpoint_name
|
|
132
|
+
when 'index'
|
|
133
|
+
'_id ? Elasticsearch::API::HTTP_PUT : Elasticsearch::API::HTTP_POST'
|
|
134
|
+
when 'count'
|
|
135
|
+
<<~SRC
|
|
136
|
+
if arguments[:body]
|
|
137
|
+
Elasticsearch::API::HTTP_POST
|
|
138
|
+
else
|
|
139
|
+
Elasticsearch::API::HTTP_GET
|
|
140
|
+
end
|
|
141
|
+
SRC
|
|
142
|
+
else
|
|
143
|
+
"Elasticsearch::API::HTTP_#{@spec['url']['paths'].map { |a| a['methods'] }.flatten.first}"
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def __http_path
|
|
148
|
+
return "\"#{__parse_path(@paths.first)}\"" if @paths.size == 1
|
|
149
|
+
return termvectors_path if @method_name == 'termvectors'
|
|
150
|
+
|
|
151
|
+
result = ''
|
|
152
|
+
anchor_string = ''
|
|
153
|
+
@paths.sort { |a, b| b.length <=> a.length }.each_with_index do |path, i|
|
|
154
|
+
var_string = __extract_path_variables(path).map { |var| "_#{var}" }.join(' && ')
|
|
155
|
+
next if anchor_string == var_string
|
|
156
|
+
|
|
157
|
+
anchor_string = var_string
|
|
158
|
+
result += if i.zero?
|
|
159
|
+
"if #{var_string}\n"
|
|
160
|
+
elsif (i == @paths.size - 1) || var_string.empty?
|
|
161
|
+
"else\n"
|
|
162
|
+
else
|
|
163
|
+
"elsif #{var_string}\n"
|
|
164
|
+
end
|
|
165
|
+
result += "\"#{__parse_path(path)}\"\n"
|
|
137
166
|
end
|
|
167
|
+
result += 'end'
|
|
168
|
+
result
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def __parse_path(path)
|
|
172
|
+
path.gsub(/^\//, '')
|
|
173
|
+
.gsub(/\/$/, '')
|
|
174
|
+
.gsub('{', "\#{#{__utils}.__listify(_")
|
|
175
|
+
.gsub('}', ')}')
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def __path_variables
|
|
179
|
+
@paths.map do |path|
|
|
180
|
+
__extract_path_variables(path)
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# extract values that are in the {var} format:
|
|
185
|
+
def __extract_path_variables(path)
|
|
186
|
+
path.scan(/{(\w+)}/).flatten
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Find parts that are definitely required and should raise an error if
|
|
190
|
+
# they're not present
|
|
191
|
+
#
|
|
192
|
+
def __required_parts
|
|
193
|
+
required = []
|
|
194
|
+
return required if @endpoint_name == 'tasks.get'
|
|
195
|
+
|
|
196
|
+
required << 'body' if (@spec['body'] && @spec['body']['required'])
|
|
197
|
+
# Get required variables from paths:
|
|
198
|
+
req_variables = __path_variables.inject(:&) # find intersection
|
|
199
|
+
required << req_variables unless req_variables.empty?
|
|
200
|
+
required.flatten
|
|
138
201
|
end
|
|
139
202
|
|
|
203
|
+
def docs_helper(name, info)
|
|
204
|
+
info['type'] = 'String' if info['type'] == 'enum' # Rename 'enums' to 'strings'
|
|
205
|
+
tipo = info['type'] ? info['type'].capitalize : 'String'
|
|
206
|
+
description = info['description'] ? info['description'].strip : '[TODO]'
|
|
207
|
+
options = info['options'] ? "\n # (options: #{info['options'].join(', '.strip)})\n" : ''
|
|
208
|
+
required = info['required'] ? ' (*Required*)' : ''
|
|
209
|
+
deprecated = info['deprecated'] ? ' *Deprecated*' : ''
|
|
210
|
+
"# @option arguments [#{tipo}] :#{name} #{description} #{required} #{deprecated} #{options}\n"
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def generate_tests
|
|
214
|
+
copy_file 'templates/test_helper.rb', @output.join('test').join('test_helper.rb')
|
|
215
|
+
|
|
216
|
+
@test_directory = @output.join('test/api').join(@module_namespace.join('/'))
|
|
217
|
+
@test_file = @test_directory.join("#{@method_name}_test.rb")
|
|
218
|
+
|
|
219
|
+
empty_directory @test_directory
|
|
220
|
+
template 'templates/test.erb', @test_file
|
|
221
|
+
|
|
222
|
+
print_source_code(@test_file) if options[:verbose]
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def print_source_code(path_to_file)
|
|
226
|
+
colorized_output = CodeRay.scan_file(path_to_file, :ruby).terminal
|
|
227
|
+
lines = colorized_output.split("\n")
|
|
228
|
+
formatted = lines.first + "\n" + lines[1, lines.size].map { |l| ' ' * 14 + l }.join("\n")
|
|
229
|
+
|
|
230
|
+
say_status('ruby', formatted, :yellow)
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def print_tree
|
|
234
|
+
return unless `which tree > /dev/null 2>&1; echo $?`.to_i < 1
|
|
235
|
+
|
|
236
|
+
lines = `tree #{@output}`.split("\n")
|
|
237
|
+
say_status('tree', lines.first + "\n" + lines[1, lines.size].map { |l| ' ' * 14 + l }.join("\n"))
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def __utils
|
|
241
|
+
@xpack ? 'Elasticsearch::API::Utils' : 'Utils'
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def run_rubocop
|
|
245
|
+
system("rubocop --format autogenconf -x #{FilesHelper::output_dir(@xpack)}")
|
|
246
|
+
end
|
|
140
247
|
end
|
|
141
248
|
end
|
|
142
249
|
end
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Licensed to Elasticsearch B.V under one or more agreements.
|
|
4
|
+
# Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
|
|
5
|
+
# See the LICENSE file in the project root for more information
|
|
6
|
+
|
|
7
|
+
module Elasticsearch
|
|
8
|
+
module API
|
|
9
|
+
# Handles specific exceptional parameters and code snippets that need to be
|
|
10
|
+
# included in the generated code. This module is included in SourceGenerator
|
|
11
|
+
# so its methods can be used from the ERB template (method.erb). This will
|
|
12
|
+
# potentially be refactored into different templates.
|
|
13
|
+
module EndpointSpecifics
|
|
14
|
+
# Endpoints that need Utils.__rescue_from_not_found
|
|
15
|
+
IGNORE_404 = %w[
|
|
16
|
+
exists
|
|
17
|
+
indices.exists
|
|
18
|
+
indices.exists_alias
|
|
19
|
+
indices.exists_template
|
|
20
|
+
indices.exists_type
|
|
21
|
+
].freeze
|
|
22
|
+
|
|
23
|
+
# Endpoints that need Utils.__rescue_from_not_found if the ignore
|
|
24
|
+
# parameter is included
|
|
25
|
+
COMPLEX_IGNORE_404 = %w[
|
|
26
|
+
delete
|
|
27
|
+
get
|
|
28
|
+
indices.flush_synced
|
|
29
|
+
indices.delete_template
|
|
30
|
+
indices.delete
|
|
31
|
+
security.get_role
|
|
32
|
+
security.get_user
|
|
33
|
+
snapshot.status
|
|
34
|
+
snapshot.get
|
|
35
|
+
snapshot.get_repository
|
|
36
|
+
snapshot.delete_repository
|
|
37
|
+
snapshot.delete
|
|
38
|
+
update
|
|
39
|
+
watcher.delete_watch
|
|
40
|
+
].freeze
|
|
41
|
+
|
|
42
|
+
# Endpoints that need params[:h] listified
|
|
43
|
+
H_PARAMS = %w[aliases allocation count health indices nodes pending_tasks
|
|
44
|
+
recovery shards thread_pool].freeze
|
|
45
|
+
|
|
46
|
+
# Function that adds the listified h param code
|
|
47
|
+
def specific_params(namespace)
|
|
48
|
+
params = []
|
|
49
|
+
if H_PARAMS.include?(@method_name) && namespace == 'cat'
|
|
50
|
+
if @method_name == 'nodes'
|
|
51
|
+
params << 'params[:h] = Utils.__listify(params[:h], escape: false) if params[:h]'
|
|
52
|
+
else
|
|
53
|
+
params << 'params[:h] = Utils.__listify(params[:h]) if params[:h]'
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
params
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def needs_ignore_404?(endpoint)
|
|
60
|
+
IGNORE_404.include? endpoint
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def needs_complex_ignore_404?(endpoint)
|
|
64
|
+
COMPLEX_IGNORE_404.include? endpoint
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def termvectors_path
|
|
68
|
+
<<~SRC
|
|
69
|
+
if _index && _type && _id
|
|
70
|
+
"\#{Utils.__listify(_index)}/\#{Utils.__listify(_type)}/\#{Utils.__listify(_id)}/\#{endpoint}"
|
|
71
|
+
elsif _index && _type
|
|
72
|
+
"\#{Utils.__listify(_index)}/\#{Utils.__listify(_type)}/\#{endpoint}"
|
|
73
|
+
elsif _index && _id
|
|
74
|
+
"\#{Utils.__listify(_index)}/\#{endpoint}/\#{Utils.__listify(_id)}"
|
|
75
|
+
else
|
|
76
|
+
"\#{Utils.__listify(_index)}/\#{endpoint}"
|
|
77
|
+
end
|
|
78
|
+
SRC
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def ping_perform_request
|
|
82
|
+
<<~SRC
|
|
83
|
+
begin
|
|
84
|
+
perform_request(method, path, params, body).status == 200 ? true : false
|
|
85
|
+
rescue Exception => e
|
|
86
|
+
if e.class.to_s =~ /NotFound|ConnectionFailed/ || e.message =~ /Not\s*Found|404|ConnectionFailed/i
|
|
87
|
+
false
|
|
88
|
+
else
|
|
89
|
+
raise e
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
SRC
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def indices_stats_params_registry
|
|
96
|
+
<<~SRC
|
|
97
|
+
ParamsRegistry.register(:stats_params, [
|
|
98
|
+
#{@spec['params'].keys.map { |k| ":#{k}" }.join(",\n")}
|
|
99
|
+
].freeze)
|
|
100
|
+
|
|
101
|
+
ParamsRegistry.register(:stats_parts, [
|
|
102
|
+
#{@parts['metric']['options'].push('metric').map { |k| ":#{k}" }.join(",\n")}
|
|
103
|
+
].freeze)
|
|
104
|
+
SRC
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def msearch_body_helper
|
|
108
|
+
<<~SRC
|
|
109
|
+
case
|
|
110
|
+
when body.is_a?(Array) && body.any? { |d| d.has_key? :search }
|
|
111
|
+
payload = body.
|
|
112
|
+
inject([]) do |sum, item|
|
|
113
|
+
meta = item
|
|
114
|
+
data = meta.delete(:search)
|
|
115
|
+
|
|
116
|
+
sum << meta
|
|
117
|
+
sum << data
|
|
118
|
+
sum
|
|
119
|
+
end.
|
|
120
|
+
map { |item| Elasticsearch::API.serializer.dump(item) }
|
|
121
|
+
payload << "" unless payload.empty?
|
|
122
|
+
payload = payload.join("\n")
|
|
123
|
+
when body.is_a?(Array)
|
|
124
|
+
payload = body.map { |d| d.is_a?(String) ? d : Elasticsearch::API.serializer.dump(d) }
|
|
125
|
+
payload << "" unless payload.empty?
|
|
126
|
+
payload = payload.join("\n")
|
|
127
|
+
else
|
|
128
|
+
payload = body
|
|
129
|
+
end
|
|
130
|
+
SRC
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def msearch_template_body_helper
|
|
134
|
+
<<~SRC
|
|
135
|
+
case
|
|
136
|
+
when body.is_a?(Array)
|
|
137
|
+
payload = body.map { |d| d.is_a?(String) ? d : Elasticsearch::API.serializer.dump(d) }
|
|
138
|
+
payload << "" unless payload.empty?
|
|
139
|
+
payload = payload.join("\n")
|
|
140
|
+
else
|
|
141
|
+
payload = body
|
|
142
|
+
end
|
|
143
|
+
SRC
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def bulk_body_helper
|
|
147
|
+
<<~SRC
|
|
148
|
+
if body.is_a? Array
|
|
149
|
+
payload = Utils.__bulkify(body)
|
|
150
|
+
else
|
|
151
|
+
payload = body
|
|
152
|
+
end
|
|
153
|
+
SRC
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
|
|
3
|
+
module Elasticsearch
|
|
4
|
+
module API
|
|
5
|
+
module FilesHelper
|
|
6
|
+
OSS_SRC_PATH = '../../../../../tmp/elasticsearch/rest-api-spec/src/main/resources/rest-api-spec/api/'.freeze
|
|
7
|
+
OSS_OUTPUT_DIR = '../../elasticsearch-api/lib/elasticsearch/api/actions'.freeze
|
|
8
|
+
|
|
9
|
+
XPACK_SRC_PATH = '../../../../../tmp/elasticsearch/x-pack/plugin/src/test/resources/rest-api-spec/api'.freeze
|
|
10
|
+
XPACK_OUTPUT_DIR = '../../elasticsearch-xpack/lib/elasticsearch/xpack/api/actions'.freeze
|
|
11
|
+
|
|
12
|
+
# Path to directory with JSON API specs
|
|
13
|
+
def self.input_dir(xpack = false)
|
|
14
|
+
input_dir = if xpack
|
|
15
|
+
File.expand_path(XPACK_SRC_PATH, __FILE__)
|
|
16
|
+
else
|
|
17
|
+
File.expand_path(OSS_SRC_PATH, __FILE__)
|
|
18
|
+
end
|
|
19
|
+
Pathname(input_dir)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Path to directory to copy generated files
|
|
23
|
+
def self.output_dir(xpack = false)
|
|
24
|
+
xpack ? Pathname(XPACK_OUTPUT_DIR) : Pathname(OSS_OUTPUT_DIR)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Only get JSON files and remove hidden files
|
|
28
|
+
def self.files(xpack = false)
|
|
29
|
+
Dir.entries(input_dir(xpack).to_s).reject do |f|
|
|
30
|
+
f.start_with?('.') ||
|
|
31
|
+
f.start_with?('_') ||
|
|
32
|
+
File.extname(f) != '.json'
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
data/utils/thor/lister.rb
CHANGED
|
@@ -9,16 +9,16 @@ require 'thor'
|
|
|
9
9
|
require 'pathname'
|
|
10
10
|
|
|
11
11
|
module Elasticsearch
|
|
12
|
-
|
|
13
12
|
module API
|
|
14
|
-
|
|
15
13
|
class Lister < Thor
|
|
16
14
|
namespace 'api'
|
|
17
15
|
|
|
16
|
+
DEFAULT_PATH = '../../tmp/elasticsearch/rest-api-spec/src/main/resources/rest-api-spec/api/'.freeze
|
|
17
|
+
|
|
18
18
|
desc "list <PATH DIRECTORY WITH JSON SPEC FILES>", "List all the REST API endpoints from the JSON specification"
|
|
19
19
|
method_option :verbose, type: :boolean, default: false, desc: 'Output more information'
|
|
20
20
|
method_option :format, default: 'text', desc: 'Output format (text, json)'
|
|
21
|
-
def list(directory)
|
|
21
|
+
def list(directory = DEFAULT_PATH)
|
|
22
22
|
input = Pathname(directory).join('*.json')
|
|
23
23
|
apis = Dir[input.to_s].map do |f|
|
|
24
24
|
File.basename(f, '.json')
|
|
@@ -40,6 +40,5 @@ module Elasticsearch
|
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
|
-
|
|
44
43
|
end
|
|
45
44
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<%- if @spec['documentation']['description'] -%>
|
|
2
|
+
<%= ' '*(@namespace_depth+3) %># <%= @spec['documentation']['description'].gsub("\n", "\n#{' '*(@namespace_depth+3)}# ") -%>
|
|
3
|
+
<%- else %>
|
|
4
|
+
<%= ' '*(@namespace_depth+3) %># TODO: Description
|
|
5
|
+
<%- end %>
|
|
6
|
+
<%= ' '*(@namespace_depth+3) %>#
|
|
7
|
+
<%- unless @parts.nil? || @parts.empty? %><%# URL parts -%>
|
|
8
|
+
<%- @parts.each do |name, info| -%>
|
|
9
|
+
<%= docs_helper(name, info) -%>
|
|
10
|
+
<%- end -%>
|
|
11
|
+
<%- end -%><%# Body -%>
|
|
12
|
+
<%# URL parameters -%>
|
|
13
|
+
<%- @params.each do |name, info| -%>
|
|
14
|
+
<%= docs_helper(name, info) -%>
|
|
15
|
+
<%- end %>
|
|
16
|
+
<%= ' '*(@namespace_depth+3) + '# @option arguments [Hash] :body ' + (@spec['body']['description'] ? @spec['body']['description'].strip : 'TODO: Description') + (@spec['body']['required'] ? ' (*Required*)' : '') + "\n" if @spec['body'] -%>
|
|
17
|
+
<% if @deprecation_note -%>
|
|
18
|
+
#
|
|
19
|
+
# *Deprecation notice*:
|
|
20
|
+
# <%= @deprecation_note['description'] %>
|
|
21
|
+
# Deprecated since version <%= @deprecation_note['version'] %>
|
|
22
|
+
#
|
|
23
|
+
<% end -%>
|
|
24
|
+
<%= ' '*(@namespace_depth+3) -%>#
|
|
25
|
+
<%# Documentation link -%>
|
|
26
|
+
<%= ' '*(@namespace_depth+3) %># @see <%= @spec['documentation']['url'] ? @spec['documentation']['url'].gsub!(/\/(current|master)\//, '/7.5/') : "[TODO]" %>
|
|
27
|
+
<%= ' '*(@namespace_depth+3) %>#
|