Implemented basic document retrieval (single and multiple).
h3rald h3rald@h3rald.com
Sat, 24 Jan 2015 20:53:42 +0100
6 files changed,
90 insertions(+),
56 deletions(-)
A
api.nim
@@ -0,0 +1,38 @@
+import asynchttpserver, asyncdispatch, strutils +from strtabs import StringTableRef, newStringTable +import types, core + +proc resError*(code: HttpCode, message: string): Response = + result.code = code + result.content = """{"code": $1, "message":\"$2\"}""" % [$code, message] + result.headers = CT_JSON.newStringTable + +proc resDocumentNotFound*(id): Response = + resError(Http404, "Document '$1' not found." % id) + +proc getRawDocument*(LS: LiteStore, id: string): Response = + let doc = LS.store.retrieveRawDocument(id) + result.headers = CT_JSON.newStringTable + if doc == "": + result = resDocumentNotFound(id) + else: + result.content = doc + result.code = Http200 + +proc getDocument*(LS: LiteStore, id: string): Response = + let doc = LS.store.retrieveDocument(id) + if doc.data == "": + result = resDocumentNotFound(id) + else: + result.headers = doc.contenttype.ctHeader + result.content = doc.data + result.code = Http200 + +proc getRawDocuments*(LS: LiteStore): Response = + let docs = LS.store.retrieveRawDocuments() # TODO Implement query options + if docs.len == 0: + result = resError(Http404, "No documents found.") + else: + result.headers = ctJsonHeader() + result.content = docs + result.code = Http200
M
cli.nim
→
cli.nim
@@ -11,7 +11,7 @@ usage* = " LiteStore v"& version & " - Lightweight REST Document Store" & """
(c) 2015 Fabio Cevasco Usage: - litestore [-p:<port> -a:<address>] [<file>] [--pack:<directory> | --unpack:<directory>] + LS [-p:<port> -a:<address>] [<file>] [--pack:<directory> | --unpack:<directory>] Options: -a, --address Specify address (default: 0.0.0.0).@@ -57,12 +57,12 @@ file = key
else: discard -var settings*: Settings +var LS*: LiteStore -settings.port = port -settings.address = address -settings.operation = operation -settings.file = file -settings.directory = directory -settings.appversion = version -settings.appname = "LiteStore" +LS.port = port +LS.address = address +LS.operation = operation +LS.file = file +LS.directory = directory +LS.appversion = version +LS.appname = "LiteStore"
M
core.nim
→
core.nim
@@ -103,7 +103,6 @@ return (data: raw_document[1].decode, contenttype: raw_document[2])
else: return (data: raw_document[1], contenttype: raw_document[2]) - proc retrieveRawDocuments*(store: Datastore, options: QueryOptions = newQueryOptions()): string = var select = prepareSelectDocumentsQuery(options) var raw_documents = store.db.getAllRows(select.sql)
M
litestore.nim
→
litestore.nim
@@ -48,26 +48,26 @@ echo store.destroyDocumentsByTag("$dir:nimcache")
when isMainModule: # Initialize Datastore - if not settings.file.fileExists: + if not LS.file.fileExists: try: - settings.file.createDatastore() + LS.file.createDatastore() except: - error(1, "Unable to create datastore '$1'" % [settings.file]) + error(1, "Unable to create datastore '$1'" % [LS.file]) try: - settings.store = settings.file.openDatastore() + LS.store = LS.file.openDatastore() except: - error(2, "Unable to open datastore '$1'" % [settings.file]) - case settings.operation: + error(2, "Unable to open datastore '$1'" % [LS.file]) + case LS.operation: of opPack: - settings.store.packDir(settings.directory) + LS.store.packDir(LS.directory) of opUnpack: - settings.store.unpackDir(settings.directory) + LS.store.unpackDir(LS.directory) of opRun: # STARTTEST - settings.file.destroyDatastore() - settings.file.createDatastore() - settings.store = settings.file.openDatastore() - settings.store.packDir("nimcache") + LS.file.destroyDatastore() + LS.file.createDatastore() + LS.store = LS.file.openDatastore() + LS.store.packDir("nimcache") # ENDTEST - settings.serve + LS.serve runForever()
M
server.nim
→
server.nim
@@ -1,59 +1,47 @@
import asynchttpserver, asyncdispatch, times, strutils, pegs from strtabs import StringTableRef, newStringTable -import types, core - +import types, api -const - CT_JSON = {"Content-type": "application/json"} - -proc getReqInfo(req): string = +proc getReqInfo(req: Request): string = return $getLocalTime(getTime()) & " - " & req.hostname & " " & req.reqMethod & " " & req.url.path proc handleCtrlC() {.noconv.} = echo "\nExiting..." quit() -proc resDocumentNotFound(id): Response = - result.content = """{"code": 404, "message": "Document '$1' not found."}""" % id - result.code = Http404 - result.headers = CT_JSON.newStringTable +proc rDocs(path: string, matches: var seq[string]): bool = + return path.find(peg"""^\/docs\/? {(.*)}""", matches) != -1 -proc getRawDocument(settings: Settings, id: string): Response = - let doc = settings.store.retrieveRawDocument(id) - result.headers = CT_JSON.newStringTable - if doc == "": - result = resDocumentNotFound(id) - else: - result.content = doc - result.code = Http200 +proc rTags(path: string, matches: var seq[string]): bool = + return path.find(peg"""^\/tags\/? {(.*)}(\/{.*})?""", matches) != -1 -proc route(req: Request, settings: Settings): Response = +proc route(req: Request, LS: LiteStore): Response = case req.reqMethod: of "GET": var matches = @[""] - if req.url.path.find(peg"""^\/docs\/? {(.*)}""", matches) != -1: + if req.url.path.rDocs(matches): if matches[0] != "": # Retrieve a single document - return settings.getRawDocument(matches[0]) + if req.url.query.contains("raw=true"): + return LS.getRawDocument(matches[0]) + else: + return LS.getDocument(matches[0]) else: - result = resDocumentNotFound("-") # TODO CHANGE + # Retrieve a multiple documents + return LS.getRawDocuments() else: - result.code = Http400 - result.content = """{"code": 400, "message": "Bad request: $1"}""" % req.url.path - result.headers = CT_JSON.newStringTable + return resError(Http400, "Bad request: $1" % req.url.path) else: - result.content = """{"code": 501, "message": "Method $1 not implemented."}""" % req.reqMethod - result.headers = CT_JSON.newStringTable - result.code = Http501 + return resError(Http501, "Method $1 not implemented" % req.reqMethod) setControlCHook(handleCtrlC) -proc serve*(settings: Settings) = +proc serve*(LS: LiteStore) = var server = newAsyncHttpServer() proc handleHttpRequest(req: Request): Future[void] {.async.} = echo getReqInfo(req) - let res = req.route(settings) + let res = req.route(LS) await req.respond(res.code, res.content, res.headers) - echo settings.appname, " v", settings.appversion, " started on ", settings.address, ":", settings.port, "." - asyncCheck server.serve(settings.port.Port, handleHttpRequest, settings.address) + echo LS.appname, " v", LS.appversion, " started on ", LS.address, ":", LS.port, "." + asyncCheck server.serve(LS.port.Port, handleHttpRequest, LS.address)
M
types.nim
→
types.nim
@@ -23,7 +23,7 @@ startswith*: bool
endswith*: bool negated*: bool Operation* = enum opRun, opPack, opUnpack - Settings* = object + LiteStore* = object store*: Datastore address*: string port*: int@@ -36,6 +36,15 @@ Response* = tuple[
code: HttpCode, content: string, headers: StringTableRef] + +const + CT_JSON* = {"Content-type": "application/json"} + +proc ctHeader*(ct: string): StringTableRef = + return {"Content-type": ct}.newStringTable + +proc ctJsonHeader*(): StringTableRef = + return CT_JSON.newStringTable proc newQueryOptions*(): QueryOptions = return QueryOptions(single: false, limit: 0, orderby: "", tags: "", search: "")