177 lines
No EOL
4.7 KiB
Nim
177 lines
No EOL
4.7 KiB
Nim
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") |