1
1
# frozen_string_literal: true
2
2
3
+ require 'json'
4
+
3
5
class ExampleCode < BaseMustacheRenderer
4
6
self . template_file = "#{ __dir__ } /templates/example_code.mustache"
5
7
6
8
def initialize ( action , args )
7
9
super ( action , args )
8
10
end
9
11
10
- def query_params
11
- @args . raw [ 'query_params' ] &.split ( ',' ) &.map ( &:strip ) || [ ]
12
+ # Resolves the correct OpenSearch client method call
13
+ def client_method_call
14
+ segments = @action . full_name . to_s . split ( '.' )
15
+ return "client" if segments . empty?
16
+
17
+ if segments . size == 1
18
+ "client.#{ segments . first } "
19
+ else
20
+ "client.#{ se gments . first } .#{ segments [ 1 ] } "
21
+ end
22
+ end
23
+
24
+ def rest_lines
25
+ @args . raw [ 'rest' ] &.split ( "\n " ) &.map ( &:strip ) || [ ]
12
26
end
13
27
14
28
def rest_code
15
- method = @action . http_verbs . first . upcase rescue 'GET'
16
- path = @action . urls . first rescue '/'
17
- query = query_params
18
- query_string = query . any? ? "?#{ query . join ( '&' ) } " : ""
19
- "#{ method } #{ path } #{ query_string } "
29
+ rest_lines . join ( "\n " )
20
30
end
21
31
22
- def python_code
23
- query = query_params
24
- args = query . map { |k | "#{ k } : true" } . join ( ', ' )
25
- "response = client.#{ @action . full_name } (#{ args } )"
32
+ # Uses the declared HTTP method in the OpenAPI spec
33
+ def http_method
34
+ @action . http_verbs . first &.upcase || "GET"
26
35
end
27
36
37
+ # Converts OpenAPI-style path (/index/{id}) into Ruby-style interpolation (/index/#{id})
38
+ def path_only
39
+ url = @action . urls . first
40
+ return '' unless url
41
+ url . gsub ( /\{ (\w +)\} / , '#{\1}' )
42
+ end
28
43
def javascript_code
29
- "// TODO: add JS client call for #{ @action . full_name } "
44
+ "JavaScript example code not yet implemented"
45
+ end
46
+ # Assembles a query string from the declared query parameters
47
+ def query_string
48
+ return '' if @action . query_parameters . empty?
49
+ @action . query_parameters . map { |param | "#{ param . name } =example" } . join ( '&' )
50
+ end
51
+
52
+ # Combines path and query string for display
53
+ def path_with_query
54
+ qs = query_string
55
+ qs . empty? ? path_only : "#{ path_only } ?#{ qs } "
56
+ end
57
+
58
+ # Hash version of query params
59
+ def query_params
60
+ @action . query_parameters . to_h { |param | [ param . name , "example" ] }
61
+ end
62
+
63
+ # Parses the body from the REST example (only for preserving raw formatting)
64
+ def body
65
+ body_lines = rest_lines [ 1 ..]
66
+ return nil if body_lines . empty?
67
+ begin
68
+ JSON . parse ( body_lines . join ( "\n " ) )
69
+ rescue
70
+ nil
71
+ end
72
+ end
73
+
74
+ def action_expects_body? ( verb )
75
+ verb = verb . downcase
76
+ @action . operations . any? do |op |
77
+ op . http_verb . to_s . downcase == verb &&
78
+ op . spec &.requestBody &&
79
+ op . spec . requestBody . respond_to? ( :content )
80
+ end
81
+ end
82
+
83
+ def matching_spec_path
84
+ return @matching_spec_path if defined? ( @matching_spec_path )
85
+
86
+ # Extract raw request path from rest line
87
+ raw_line = rest_lines . first . to_s
88
+ _ , request_path = raw_line . split
89
+ request_segments = request_path . split ( '?' ) . first . split ( '/' ) . reject ( &:empty? )
90
+
91
+ # Choose the best matching spec URL
92
+ best = nil
93
+ best_score = -1
94
+
95
+ @action . urls . each do |spec_path |
96
+ spec_segments = spec_path . split ( '/' ) . reject ( &:empty? )
97
+ next unless spec_segments . size == request_segments . size
98
+
99
+ score = 0
100
+ spec_segments . each_with_index do |seg , i |
101
+ if seg . start_with? ( '{' )
102
+ score += 1 # parameter match
103
+ elsif seg == request_segments [ i ]
104
+ score += 2 # exact match
105
+ else
106
+ score = -1
107
+ break
108
+ end
109
+ end
110
+
111
+ if score > best_score
112
+ best = spec_path
113
+ best_score = score
114
+ end
115
+ end
116
+
117
+ @matching_spec_path = best
118
+ end
119
+
120
+ # Final Python code using action metadata
121
+ def python_code
122
+ return "# Invalid action" unless @action &.full_name
123
+
124
+ client_setup = <<~PYTHON
125
+ from opensearchpy import OpenSearch
126
+
127
+ host = 'localhost'
128
+ port = 9200
129
+ auth = ('admin', 'admin') # For testing only. Don't store credentials in code.
130
+ ca_certs_path = '/full/path/to/root-ca.pem' # Provide a CA bundle if you use intermediate CAs with your root CA.
131
+
132
+ # Create the client with SSL/TLS enabled, but hostname verification disabled.
133
+ client = OpenSearch(
134
+ hosts = [{'host': host, 'port': port}],
135
+ http_compress = True, # enables gzip compression for request bodies
136
+ http_auth = auth,
137
+ use_ssl = True,
138
+ verify_certs = True,
139
+ ssl_assert_hostname = False,
140
+ ssl_show_warn = False,
141
+ ca_certs = ca_certs_path
142
+ )
143
+
144
+ PYTHON
145
+
146
+ if @args . raw [ 'body' ] == '{"hello"}'
147
+ puts "# This is a debug example"
148
+ end
149
+
150
+ namespace , method = @action . full_name . split ( '.' )
151
+ client_call = "client"
152
+ client_call += ".#{ namespace } " if namespace
153
+ client_call += ".#{ method } "
154
+
155
+ args = [ ]
156
+
157
+ # Extract actual path and query from the first line of the REST input
158
+ raw_line = rest_lines . first . to_s
159
+ http_verb , full_path = raw_line . split
160
+ path_part , query_string = full_path . to_s . split ( '?' , 2 )
161
+
162
+ # Extract used path values from the path part
163
+ path_values = path_part . split ( '/' ) . reject ( &:empty? )
164
+
165
+ # Match spec path (e.g. /_cat/aliases/{name}) to determine which param this value belongs to
166
+ spec_path = matching_spec_path . to_s
167
+ spec_parts = spec_path . split ( '/' ) . reject ( &:empty? )
168
+
169
+ param_mapping = { }
170
+ spec_parts . each_with_index do |part , i |
171
+ if part =~ /\{ (.+?)\} / && path_values [ i ]
172
+ param_mapping [ $1] = path_values [ i ]
173
+ end
174
+ end
175
+
176
+ # Add path parameters if they were present in the example
177
+ @action . path_parameters . each do |param |
178
+ if param_mapping . key? ( param . name )
179
+ args << "#{ param . name } = \" #{ param_mapping [ param . name ] } \" "
180
+ end
181
+ end
182
+
183
+ # Add query parameters from query string
184
+ if query_string
185
+ query_pairs = query_string . split ( '&' ) . map { |s | s . split ( '=' , 2 ) }
186
+ query_hash = query_pairs . map do |k , v |
187
+ "#{ k } : #{ v ? "\" #{ v } \" " : "True" } "
188
+ end . join ( ', ' )
189
+ args << "params = { #{ query_hash } }" unless query_hash . empty?
190
+ end
191
+
192
+ # Add body if spec allows it AND it's present in REST
193
+ if action_expects_body? ( http_verb )
194
+ if @args . raw [ 'body' ]
195
+ begin
196
+ parsed = JSON . parse ( @args . raw [ 'body' ] )
197
+ pretty = JSON . pretty_generate ( parsed ) . gsub ( /^/ , ' ' )
198
+ args << "body = #{ pretty } "
199
+ rescue JSON ::ParserError
200
+ args << "body = #{ JSON . dump ( @args . raw [ 'body' ] ) } "
201
+ end
202
+ else
203
+ args << 'body = { "Insert body here" }'
204
+ end
205
+ end
206
+
207
+ # Final result
208
+ call_code = if args . empty?
209
+ "response = #{ client_call } ()"
210
+ else
211
+ final_args = args . map { |line | " #{ line } " } . join ( ",\n " )
212
+ <<~PYTHON
213
+ response = #{ client_call } (
214
+ #{ final_args }
215
+ )
216
+ PYTHON
217
+ end
218
+ # Prepend client if requested
219
+ if @args . raw [ 'include_client_setup' ]
220
+ client_setup + call_code
221
+ else
222
+ call_code
223
+ end
30
224
end
31
225
end
0 commit comments