-
Notifications
You must be signed in to change notification settings - Fork 0
/
RPC.ml
261 lines (196 loc) · 7.72 KB
/
RPC.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
(**************************************************************************)
(* *)
(* Copyright (c) 2014 - 2016. *)
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
(* *)
(* All rights reserved. No warranty, explicit or implicit, provided. *)
(* *)
(**************************************************************************)
open Lwt.Infix
module Arg = Resto.Arg
module Path = Resto.Path
(* Services *)
(* HTTP methods as defined in Cohttp.Code *)
type meth = [
| `GET
| `POST
| `HEAD
| `DELETE
| `PATCH
| `PUT
| `OPTIONS
| `TRACE
| `CONNECT
| `Other of string
]
type ('prefix, 'params, 'input, 'output) service =
meth * ('prefix, 'params, 'input, 'output) Resto.service
(* The default HTTP method for services *)
let default_meth = `POST
(* Commonly used REST HTTP methods *)
let rest_meths = [`GET; `POST; `HEAD; `DELETE; `PATCH; `PUT; `OPTIONS]
let string_of_method = function
| `GET -> "GET"
| `POST -> "POST"
| `HEAD -> "HEAD"
| `DELETE -> "DELETE"
| `PATCH -> "PATCH"
| `PUT -> "PUT"
| `OPTIONS -> "OPTIONS"
| `TRACE -> "TRACE"
| `CONNECT -> "CONNECT"
| `Other s -> s
let service ?(meth = default_meth) ?description ~input ~output path =
(meth,
Resto.service
?description
~input:(Data_encoding.Json.convert input)
~output:(Data_encoding.Json.convert output)
path)
(* REST services *)
(* GET service: no input body *)
let get_service ?description ~output path =
service ~meth:`GET ?description
~input:Data_encoding.empty ~output
path
(* HEAD service: same as GET, but without output body *)
let head_service ?description path =
service ~meth:`HEAD ?description
~input:Data_encoding.empty ~output:Data_encoding.empty
path
let post_service ?description ~input ~output path =
service ~meth:`POST ?description ~input ~output path
let put_service ?description ~input ~output path =
service ~meth:`PUT ?description ~input ~output path
let delete_service ?description ~input ~output path =
service ~meth:`DELETE ?description ~input ~output path
let prefix p (meth, s) = (meth, RestoDirectory.prefix p s)
let forge_request (meth, service) params input =
let path, arg = Resto.forge_request service params input in
meth, path, arg
let read_answer (_meth, service) json =
Resto.read_answer service json
module Description = struct
include Resto.Description
let service ?(meth = default_meth) ?description path =
(meth, Resto.Description.service ?description path)
end
module Answer = struct
include RestoDirectory.Answer
let answer ?(code = 200) json = { code; body = Single json }
let return ?code json = Lwt.return (answer ?code json)
end
type step = RestoDirectory.step =
| Static of string
| Dynamic of Arg.descr
type conflict = RestoDirectory.conflict =
| CService
| CDir
| CBuilder
| CCustom
| CTypes of Arg.descr * Arg.descr
| CType of Arg.descr * string list
exception Conflict = RestoDirectory.Conflict
exception Cannot_parse = RestoDirectory.Cannot_parse
(* Dispatch *)
type 'prefix directory = (meth * 'prefix RestoDirectory.directory) list
let empty = []
let map_dirs f dirs =
List.map (fun (meth, dir) -> (meth, f ~meth dir)) dirs
let map f dirs =
map_dirs (fun ~meth:_ dir -> RestoDirectory.map f dir) dirs
let prefix path dirs =
map_dirs (fun ~meth:_ dir -> RestoDirectory.prefix path dir) dirs
let merge dirs1 dirs2 =
let compare (meth1, _dir1) (meth2, _dir2) = compare meth1 meth2 in
let f (meth1, dir1) (_, dir2) = (meth1, RestoDirectory.merge dir1 dir2) in
Utils.merge_list2 ~compare ~f dirs1 dirs2
(*****************************************************************************
* Registration
****************************************************************************)
(** [replace_assoc ~init ~f k l] searches for value corresponding to [k] in an
association list, and replaces it with [f value]. If not found, a new pair
[(k, f init)] is added to the list. *)
(* TODO: move to Utils? *)
let replace_assoc ?(finalize = List.rev) ~init ~f key l =
let rec aux acc = function
| [] -> finalize ((key, f init) :: acc)
| (k, v) :: tl when k = key -> finalize ((key, f v) :: acc) @ tl
| hd :: tl -> aux (hd :: acc) tl
in
aux [] l
(* Register [service] to the directory with corresponding [meth] using [reg] *)
let register dirs (meth, service) handler =
let init = RestoDirectory.empty in
let f dir = RestoDirectory.register dir service handler in
replace_assoc ~init ~f meth dirs
(* Register dynamic directory *)
(* By default, the [builder] function of dynamic directory is registered for
HTTP methods listed in [rest_meths] *)
let register_dynamic_directory
?(meths = rest_meths) ?descr init_dirs path builder =
let builder' ~meth prefix =
builder prefix >>= fun dirs ->
Lwt.return (List.assoc meth dirs)
in
let init = RestoDirectory.empty in
List.fold_left (fun dirs meth ->
let f dir =
RestoDirectory.register_dynamic_directory
?descr dir path (builder' ~meth)
in
replace_assoc ~init ~f meth dirs)
init_dirs meths
(* Register custom lookup *)
type custom_lookup = RestoDirectory.custom_lookup
let register_custom_lookup ?(meth = default_meth) ?descr dirs s f =
let init = RestoDirectory.empty in
let f dir = RestoDirectory.register_custom_lookup ?descr dir s f in
replace_assoc ~init ~f meth dirs
(* Register description service *)
let register_describe_directory_service dirs (meth, service) =
let init = RestoDirectory.empty in
let f dir = RestoDirectory.register_describe_directory_service dir service in
replace_assoc ~init ~f meth dirs
(*****************************************************************************
* Lookup
****************************************************************************)
let lookup dirs ?(meth = default_meth) args path =
let dir = List.assoc meth dirs in
RestoDirectory.lookup dir args path
(*****************************************************************************
* Currying
****************************************************************************)
(* Service registration *)
let register0 root s f =
register root s RestoDirectory.Internal.(curry Z f)
let register1 root s f =
register root s RestoDirectory.Internal.(curry (S Z) f)
let register2 root s f =
register root s RestoDirectory.Internal.(curry (S (S Z)) f)
let register3 root s f =
register root s RestoDirectory.Internal.(curry (S (S (S Z))) f)
let register4 root s f =
register root s RestoDirectory.Internal.(curry (S (S (S (S Z)))) f)
let register5 root s f =
register root s RestoDirectory.Internal.(curry (S (S (S (S (S Z))))) f)
(* Dynamic directory registration *)
let register_dynamic_directory1 ?descr root s f =
register_dynamic_directory
?descr root s RestoDirectory.Internal.(curry (S Z) f)
let register_dynamic_directory2 ?descr root s f =
register_dynamic_directory
?descr root s RestoDirectory.Internal.(curry (S (S Z)) f)
let register_dynamic_directory3 ?descr root s f =
register_dynamic_directory
?descr root s RestoDirectory.Internal.(curry (S (S (S Z))) f)
(* Custom lookup registration *)
let register_custom_lookup1 ?meth ?descr root s f =
register_custom_lookup ?meth ?descr root s
RestoDirectory.Internal.(curry (S Z) f)
let register_custom_lookup2 ?meth ?descr root s f =
register_custom_lookup ?meth ?descr root s
RestoDirectory.Internal.(curry (S (S Z)) f)
let register_custom_lookup3 ?meth ?descr root s f =
register_custom_lookup ?meth ?descr root s
RestoDirectory.Internal.(curry (S (S (S Z))) f)