{ "description": "Paris", "coordinates": { "latitude": 48.8567, "longitude": 2.3508 } }
type coordinates = { latitude : float; longitude : float; } deriving (Yojson) type location = { description : string option; coordinates : coordinates; } deriving (Yojson) (* List of pairs (identifier * location) *) type locations = (string * location) list deriving (Yojson) type error = { error_message : string; } deriving (Yojson) let db : location Ocsipersist.table = Ocsipersist.open_table "locations"
let path = [] let get_params = Eliom_parameter.(suffix (neopt (string "id")))
let read_service = Eliom_service.Http.service ~path ~get_params () let create_service = Eliom_service.Http.post_service ~fallback:read_service ~post_params:Eliom_parameter.raw_post_data () let update_service = Eliom_service.Http.put_service ~path ~get_params () let delete_service = Eliom_service.Http.delete_service ~path ~get_params ()
let json_mime_type = "application/json" let send_json ~code json = Eliom_registration.String.send ~code (json, json_mime_type) let send_error ~code error_message = let json = Yojson.to_string<error> { error_message } in send_json ~code json let send_success () = Eliom_registration.String.send ~code:200 ("", "") let check_content_type ~mime_type content_type = match content_type with | Some ((type_, subtype), _) when (type_ ^ "/" ^ subtype) = mime_type -> true | _ -> false let read_raw_content ?(length = 4096) raw_content = let content_stream = Ocsigen_stream.get raw_content in Ocsigen_stream.string_of_stream length content_stream
let read_handler id_opt () = match id_opt with | None -> Ocsipersist.fold_step (fun id loc acc -> Lwt.return ((id, loc) :: acc)) db [] >>= fun locations -> let json = Yojson.to_string<locations> locations in send_json ~code:200 json | Some id -> catch (fun () -> Ocsipersist.find db id >>= fun location -> let json = Yojson.to_string<location> location in send_json ~code:200 json) (function | Not_found -> (* [id] hasn't been found, return a "Not found" message *) send_error ~code:404 ("Resource not found: " ^ id))
let edit_handler_aux ?(create = false) id_opt (content_type, raw_content_opt) = if not (check_content_type ~mime_type:json_mime_type content_type) then send_error ~code:400 "Content-type is wrong, it must be JSON" else match id_opt, raw_content_opt with | None, _ -> send_error ~code:400 "Location identifier is missing" | _, None -> send_error ~code:400 "Body content is missing" | Some id, Some raw_content -> read_raw_content raw_content >>= fun location_str -> catch (fun () -> (if create then Lwt.return_unit else Ocsipersist.find db id >>= fun _ -> Lwt.return_unit) >>= fun () -> let location = Yojson.from_string<location> location_str in Ocsipersist.add db id location >>= fun () -> send_success ()) (function | Not_found -> send_error ~code:404 ("Location not found: " ^ id) | Deriving_Yojson.Failed -> send_error ~code:400 "Provided JSON is not valid") let create_handler id_opt content = edit_handler_aux ~create:true id_opt content let update_handler id_opt content = edit_handler_aux ~create:false id_opt content
let delete_handler id_opt _ = match id_opt with | None -> send_error ~code:400 "An id must be provided to delete a location" | Some id -> Ocsipersist.remove db id >>= fun () -> send_success ()
let () = Eliom_registration.Any.register read_service read_handler; Eliom_registration.Any.register create_service create_handler; Eliom_registration.Any.register update_service update_handler; Eliom_registration.Any.register delete_service delete_handler; ()
open Lwt (**** Data types ****) type coordinates = { latitude : float; longitude : float; } deriving (Yojson) type location = { description : string option; coordinates : coordinates; } deriving (Yojson) (* List of pairs (identifier * location) *) type locations = (string * location) list deriving (Yojson) type error = { error_message : string; } deriving (Yojson) let db : location Ocsipersist.table = Ocsipersist.open_table "locations" (**** Services ****) let path = [] let get_params = Eliom_parameter.(suffix (neopt (string "id"))) let read_service = Eliom_service.Http.service ~path ~get_params () let create_service = Eliom_service.Http.post_service ~fallback:read_service ~post_params:Eliom_parameter.raw_post_data () let update_service = Eliom_service.Http.put_service ~path ~get_params () let delete_service = Eliom_service.Http.delete_service ~path ~get_params () (**** Handler helpers ****) let json_mime_type = "application/json" let send_json ~code json = Eliom_registration.String.send ~code (json, json_mime_type) let send_error ~code error_message = let json = Yojson.to_string<error> { error_message } in send_json ~code json let send_success () = Eliom_registration.String.send ~code:200 ("", "") let check_content_type ~mime_type content_type = match content_type with | Some ((type_, subtype), _) when (type_ ^ "/" ^ subtype) = mime_type -> true | _ -> false let read_raw_content ?(length = 4096) raw_content = let content_stream = Ocsigen_stream.get raw_content in Ocsigen_stream.string_of_stream length content_stream (**** Handlers ****) let read_handler id_opt () = match id_opt with | None -> Ocsipersist.fold_step (fun id loc acc -> Lwt.return ((id, loc) :: acc)) db [] >>= fun locations -> let json = Yojson.to_string<locations> locations in send_json ~code:200 json | Some id -> catch (fun () -> Ocsipersist.find db id >>= fun location -> let json = Yojson.to_string<location> location in send_json ~code:200 json) (function | Not_found -> (* [id] hasn't been found, return a "Not found" message *) send_error ~code:404 ("Resource not found: " ^ id)) let edit_handler_aux ?(create = false) id_opt (content_type, raw_content_opt) = if not (check_content_type ~mime_type:json_mime_type content_type) then send_error ~code:400 "Content-type is wrong, it must be JSON" else match id_opt, raw_content_opt with | None, _ -> send_error ~code:400 "Location identifier is missing" | _, None -> send_error ~code:400 "Body content is missing" | Some id, Some raw_content -> read_raw_content raw_content >>= fun location_str -> catch (fun () -> (if create then Lwt.return_unit else Ocsipersist.find db id >>= fun _ -> Lwt.return_unit) >>= fun () -> let location = Yojson.from_string<location> location_str in Ocsipersist.add db id location >>= fun () -> send_success ()) (function | Not_found -> send_error ~code:404 ("Location not found: " ^ id) | Deriving_Yojson.Failed -> send_error ~code:400 "Provided JSON is not valid") let create_handler id_opt content = edit_handler_aux ~create:true id_opt content let update_handler id_opt content = edit_handler_aux ~create:false id_opt content let delete_handler id_opt _ = match id_opt with | None -> send_error ~code:400 "An id must be provided to delete a location" | Some id -> Ocsipersist.remove db id >>= fun () -> send_success () (* Register services *) let () = Eliom_registration.Any.register read_service read_handler; Eliom_registration.Any.register create_service create_handler; Eliom_registration.Any.register update_service update_handler; Eliom_registration.Any.register delete_service delete_handler; ()
Source: https://habr.com/ru/post/336336/
All Articles