Source file service_endpoints_rest_xml.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
open! Core
open! Import
let to_request data =
let loc = !Ast_helper.default_loc in
let e =
data
|> Endpoint.cases ~f:(fun endpoint ->
match Endpoint.payload endpoint with
| None -> [%expr Awsm.Http.Request.make (method_of_endpoint endp)]
| Some payload ->
Payload.convert_rest_xml
payload
~endpoint_name:(Endpoint.name endpoint)
[%expr req])
|> Ast_helper.Exp.match_ [%expr endp]
in
let loc = !Ast_helper.default_loc in
[%stri
let to_request (type i o e) (endp : (i, o, e) t) (req : i) =
let _req = req in
[%e e]
;;]
;;
let%expect_test "to_request" =
let optional_blob =
Payload.create
~is_blob:true
~payload_module:"OptionalBlobModule"
~field_name:"optional_blob_field"
~is_required:false
in
let optional_xml =
Payload.create
~is_blob:false
~payload_module:"OptionalXmlModule"
~field_name:"optional_xml_field"
~is_required:false
in
let required_blob =
Payload.create
~is_blob:true
~payload_module:"RequiredBlobModule"
~field_name:"required_blob_field"
~is_required:true
in
let required_xml =
Payload.create
~is_blob:false
~payload_module:"RequiredXmlModule"
~field_name:"required_xml_field"
~is_required:true
in
[ Endpoint.create_test "NoPayload" ~payload:None
; Endpoint.create_test "OptionalBlobPayload" ~payload:(Some optional_blob)
; Endpoint.create_test "OptionalXmlPayload" ~payload:(Some optional_xml)
; Endpoint.create_test "RequiredBlobPayload" ~payload:(Some required_blob)
; Endpoint.create_test "RequiredXmlPayload" ~payload:(Some required_xml)
]
|> to_request
|> List.return
|> Util.structure_to_string
|> printf "%s%!";
[%expect
{|
let to_request (type i) (type o) (type e) (endp : (i, o, e) t) (req : i) =
let _req = req in
match endp with
| NoPayload -> Awsm.Http.Request.make (method_of_endpoint endp)
| OptionalBlobPayload ->
let body =
Option.map req.optional_blob_field ~f:OptionalBlobModule.to_header in
Awsm.Http.Request.make ?body (method_of_endpoint endp)
| OptionalXmlPayload ->
let body =
Option.map req.optional_xml_field
~f:(fun param ->
(((param |> OptionalXmlModule.to_value) |>
(Awsm.Xml.of_value "OptionalXmlPayload"))
|> (List.map ~f:Awsm.Xml.to_string))
|> (String.concat ~sep:"")) in
Awsm.Http.Request.make ?body (method_of_endpoint endp)
| RequiredBlobPayload ->
let body = RequiredBlobModule.to_header req.required_blob_field in
Awsm.Http.Request.make ~body (method_of_endpoint endp)
| RequiredXmlPayload ->
let body =
(fun param ->
(((param |> RequiredXmlModule.to_value) |>
(Awsm.Xml.of_value "RequiredXmlPayload"))
|> (List.map ~f:Awsm.Xml.to_string))
|> (String.concat ~sep:"")) req.required_xml_field in
Awsm.Http.Request.make ~body (method_of_endpoint endp) |}]
;;
let of_response (service : Botodata.service) data =
ignore service;
let loc = !Ast_helper.default_loc in
let body =
data
|> Endpoint.cases ~f:(fun endpoint ->
let error_of_xml =
Service_endpoints_common.make_error_expression
~loc
~label:"error_of_xml"
endpoint
in
match Endpoint.result_decoder endpoint with
| None -> [%expr return (Ok ())]
| Some Json -> assert false
| Some Xml ->
let of_xml =
Endpoint.in_result_module endpoint "of_xml"
|> Option.value_exn
~message:"no result module"
~error:(Error.create_s [%message (endpoint : Endpoint.t)])
in
[%expr
match resp with
| Error err -> handle_error err [%e error_of_xml]
| Ok resp -> response_to_xml resp >>| [%e of_xml] >>| ok]
| Some (Of_header_and_body payload_opt) -> (
let of_header_and_body =
Endpoint.in_result_module endpoint "of_header_and_body"
|> Option.value_exn
~message:"no result module"
~error:(Error.create_s [%message (endpoint : Endpoint.t)])
in
match payload_opt with
| Some payload ->
let payload =
let open Option.Let_syntax in
(let%bind op = Endpoint.op endpoint in
let%bind op_output = op.output in
let%bind shape_member =
match%bind
List.Assoc.find ~equal:String.equal service.shapes op_output.shape
with
| Structure_shape ss ->
List.Assoc.find ~equal:String.equal ss.members payload
| _ -> None
in
Some shape_member.shape)
|> Option.value ~default:payload
in
let of_string = Ast_convenience.evar (sprintf "%s.of_string" payload) in
[%expr
match resp with
| Error err -> handle_error err [%e error_of_xml]
| Ok resp ->
Awsm.Http.Response.body_to_string state resp
>>| [%e of_string]
>>= fun body ->
let headers =
Awsm.Http.Headers.to_list (Awsm.Http.Response.headers resp)
in
return (Ok ([%e of_header_and_body] (headers, body)))]
| None ->
[%expr
match resp with
| Error err -> handle_error err [%e error_of_xml]
| Ok resp ->
let headers =
Awsm.Http.Headers.to_list (Awsm.Http.Response.headers resp)
in
return (Ok ([%e of_header_and_body] (headers, ())))]))
|> Ast_helper.Exp.match_ [%expr endpoint]
in
[%stri
let of_response
(type s i o e)
(state : s Awsm.Http.Monad.t)
(endpoint : (i, o, e) t)
resp
: ( (o, [ `AWS of e | `Transport of Awsm.Http.Io.Error.call ]) result, s )
Awsm.Http.Monad.app
=
let ( >>= ) = state.Awsm.Http.Monad.bind in
let return = state.Awsm.Http.Monad.return in
let ( >>| ) x f = x >>= fun x -> return (f x) in
let ok x = Ok x in
let handle_error err error_of_xml =
let generic_error () = return (Error (`Transport err)) in
match err with
| `Too_many_redirects -> generic_error ()
| `Bad_response { Awsm.Http.Io.Error.code; body; x_amzn_error_type = _ } -> (
match error_of_xml, code >= 400 && code <= 599 with
| None, _ | _, false -> generic_error ()
| Some error_of_xml, true -> (
match Awsm.Xml.parse_response body with
| `Data _ -> generic_error ()
| `El (((_, "Error"), _), _) as xml -> (
try
let error_code =
match Awsm.Xml.child_exn xml "Code" with
| `Data error_code -> error_code
| `El (_, children) ->
List.map children ~f:(function
| `Data s -> s
| `El _ -> "")
|> String.concat ~sep:""
in
return (Error (`AWS (error_of_xml (String.strip error_code) xml)))
with
| Failure _ -> generic_error ())
| `El _ -> generic_error ()))
in
let response_to_xml resp =
Awsm.Http.Response.body_to_string state resp >>| Awsm.Xml.parse_response
in
[%e body]
;;]
;;
let%expect_test "of_response" =
[ Endpoint.create_test
"Of_header_and_no_body"
~result_module:(Some "Result")
~result_decoder:(Some (Of_header_and_body None))
; Endpoint.create_test
"Direct"
~result_module:(Some "DirectResult")
~result_decoder:(Some Xml)
; Endpoint.create_test
"Of_header_and_body"
~result_module:(Some "Result_of_header_and_body")
~result_decoder:(Some (Of_header_and_body (Some "Payload_module")))
; Endpoint.create_test "No_output" ~result_module:None ~result_decoder:None
]
|> of_response
{ metadata = Botodata.empty_metadata_for_tests
; documentation = None
; version = None
; operations = []
; shapes = []
}
|> List.return
|> Util.structure_to_string
|> printf "%s%!";
[%expect
{|
let of_response (type s) (type i) (type o) (type e)
(state : s Awsm.Http.Monad.t) (endpoint : (i, o, e) t) resp =
(let (>>=) = state.Awsm.Http.Monad.bind in
let return = state.Awsm.Http.Monad.return in
let (>>|) x f = x >>= (fun x -> return (f x)) in
let ok x = Ok x in
let handle_error err error_of_xml =
let generic_error () = return (Error (`Transport err)) in
match err with
| `Too_many_redirects -> generic_error ()
| `Bad_response
{ Awsm.Http.Io.Error.code = code; body; x_amzn_error_type = _ } ->
(match (error_of_xml, ((code >= 400) && (code <= 599))) with
| (None, _) | (_, false) -> generic_error ()
| (Some error_of_xml, true) ->
(match Awsm.Xml.parse_response body with
| `Data _ -> generic_error ()
| `El (((_, "Error"), _), _) as xml ->
(try
let error_code =
match Awsm.Xml.child_exn xml "Code" with
| `Data error_code -> error_code
| `El (_, children) ->
(List.map children
~f:(function | `Data s -> s | `El _ -> ""))
|> (String.concat ~sep:"") in
return
(Error
(`AWS (error_of_xml (String.strip error_code) xml)))
with | Failure _ -> generic_error ())
| `El _ -> generic_error ())) in
let response_to_xml resp =
(Awsm.Http.Response.body_to_string state resp) >>|
Awsm.Xml.parse_response in
match endpoint with
| Of_header_and_no_body ->
(match resp with
| Error err -> handle_error err None
| Ok resp ->
let headers =
Awsm.Http.Headers.to_list (Awsm.Http.Response.headers resp) in
return (Ok (Result.of_header_and_body (headers, ()))))
| Direct ->
(match resp with
| Error err -> handle_error err None
| Ok resp -> ((response_to_xml resp) >>| DirectResult.of_xml) >>| ok)
| Of_header_and_body ->
(match resp with
| Error err -> handle_error err None
| Ok resp ->
((Awsm.Http.Response.body_to_string state resp) >>|
Payload_module.of_string)
>>=
((fun body ->
let headers =
Awsm.Http.Headers.to_list
(Awsm.Http.Response.headers resp) in
return
(Ok
(Result_of_header_and_body.of_header_and_body
(headers, body))))))
| No_output -> return (Ok ()) : ((o,
[ `AWS of e
| `Transport of Awsm.Http.Io.Error.call ])
result,
s) Awsm.Http.Monad.app) |}]
;;
let make_structure_for_protocol service data =
[ to_request data; of_response service data ]
;;