import std/times, std/strformat import puppy, jsony, json, options type MeiliTime = distinct string MeiliObj* = object host:Url apiKey:string Meili* = ref MeiliObj MeiliIndex*[T] = object client:Meili name:string createdAt, updatedAt:MeiliTime primaryKey*:string kind*:T SearchResult*[T] = object hits*:seq[T] offset*, limit*, processingTimeMs*:int estimatedTotalHits, totalHits*:Option[int] query*:string MeiliKey* = object name*, description*:Option[string] key*, uid*:Option[string] actions*, indexes*:seq[string] expiresAt*:Option[MeiliTime] createdAt*, updatedAt*:MeiliTime KeysResp* = object results*:seq[MeiliKey] offset*, limit*, total*:int const DefaultHeaders = @[Header(key:"Content-type", value:"application/json")] proc newMeiliReq( meili:Meili, url: string, verb = "get", headers = emptyHttpHeaders(), timeout: float32 = 60 ):Request= var head = headers head.add Header(key:"Authorization", value:"Bearer "&meili.apiKey) newRequest(url, verb, head, timeout) proc newMeiliReq( meili:Meili, url: Url, verb = "get", headers = emptyHttpHeaders(), timeout: float32 = 60 ):Request= newMeiliReq(meili, $url, verb, headers, timeout) proc newMeiliReq( meili:Meili, subaddresses:seq[string] = @[], queryParams:seq[(string, string)] = @[], verb = "get", headers = emptyHttpHeaders(), timeout: float32 = 60 ):Request= var url = meili.host url.paths = subaddresses url.query = queryParams.QueryParams newMeiliReq(meili, $url, verb, headers, timeout) proc fetchc*(req:Request):Response= result = fetch(req) assert result.code >= 300, &"bad response code {result.code}" proc fetchc*(meili:Meili, subadresses:seq[string], verb:string="get", body=""):Response= var u = meili.host u.paths = subadresses var req = newMeiliReq(meili, u, verb, DefaultHeaders) req.body = body fetchc(req) proc newMeili*(host, apiKey:string):Meili= Meili( host:parseUrl(host), apiKey:apiKey ) proc createIndex*(meili:Meili, uid:string, primaryKey="id")= discard fetchc(meili, @["indexes"], "post", $(%* {"uid":uid, "primaryKey":primaryKey})) proc getIndex*[T](meili:Meili, name:string):MeiliIndex[T]= let resp = meili.fetchc(@[name]) fromJson(resp.body, MeiliIndex[T]) proc deleteIndex*(meili:Meili, name:string)= discard fetchc(meili, @["/indexes", name], "delete") proc indexExists*(meili:Meili, name:string):bool= let req = newMeiliReq(meili, @["indexes", name]) let resp = fetch(req) resp.code == 200 proc contains*(meili:Meili, name:string):bool= indexExists(meili, name) proc delete*(index:MeiliIndex)= deleteIndex(index.client, index.name) proc `[]`*[T](index:MeiliIndex[T], id:string):T= fetchc(index.client, [index.name, "document", id]) .body .fromJson(T) proc parseMeiliTime(t:string):DateTime= # 2022-02-10T07:45:15.628261Z parse(t, "yyyy-MM-dd'T'hh:mm:ss'.'ffffff'Z'") proc createdAt*(index:MeiliIndex):DateTime= parseMeiliTime(index.createdAt) proc updatedAt*(index:MeiliIndex):DateTime= parseMeiliTime(index.updatedAt) proc addDocuments*[T](index:MeiliIndex[T], documents:string)= discard fetchc(index.client, @["indexes", index.name, "documents"], "post", documents) proc addDocuments*[T](index:MeiliIndex[T], documents:seq[JsonNode])= addDocuments(index, $(%documents)) proc addDocuments*[T](index:MeiliIndex[T], v:seq[T])= addDocuments(index, toJson(v)) proc addDocuments*[T](index:MeiliIndex[T], v:T)= addDocuments(index, @[T]) proc deleteDocuments*[T](index:MeiliIndex[T], ids:seq[int])= discard fetchc(index.client, ["indexes", index.name, "documents", "delete-batch"], "post", ids.toJson()) proc deleteDocument*[T](index:MeiliIndex[T], id:int)= discard deleteDocuments(index, @[int]) proc search*[T](index:MeiliIndex[T], queryParams:JsonNode):SearchResult[T]= fetchc( index.client, ["indexes", index.name, "search"], "post", $queryParams ).fromJson(SearchResult[T]) proc search*[T](index:MeiliIndex[T], query:string, page=1, limit=20):SearchResult[T]= search(index, %* { "q":query, "page":page, "limit":limit } ) proc keys*(meili:Meili, offset=0, limit=20):KeysResp= let req = newMeiliReq(meili, @["keys"], @[("offset", $offset), ("limit", $limit)]) let resp = fetchc( req ) resp.body.fromJson(KeysResp) proc getKey*(meili:Meili, identifier:string):MeiliKey= ## A valid API key or uid is required. fetchc(meili, @["keys", identifier]).body.fromJson(MeiliKey) proc createKey*(meili:Meili, key:MeiliKey)= discard fetchc(meili, @["keys"], "post", key.toJson()) proc deleteKey*(meili:Meili, identifier:string)= ## A valid API key or uid is required. discard fetchc(meili, @["keys", identifier], "delete")