Fixes. * Closes #44.
@@ -472,8 +472,11 @@ origTags.add(tag.str)
var data: JsonNode var origData: JsonNode if tags.contains("$subtype:json"): - origData = jdoc["data"].getStr.parseJson - data = origData.copy + try: + origData = jdoc["data"].getStr.parseJson + data = origData.copy + except: + discard var c = 1 for item in jbody.items: if item.hasKey("op") and item.hasKey("path"):
@@ -18,11 +18,14 @@ logLevel = "warn"
mount = false exOperation:string = nil exFile:string = nil + exBody:string = nil + exType:string = nil exUri:string = nil let usage* = appname & " v" & version & " - Lightweight REST Document Store" & """ - (c) 2015 Fabio Cevasco + +(c) 2015-2018 Fabio Cevasco Usage: litestore [command] [option1 option2 ...]@@ -30,7 +33,7 @@
Commands: run Start LiteStore server (default if no command specified). delete Delete a previously-imported specified directory (requires -d). - execute Execute an operation on data stored in the datastore (requires -o, -u, and in certain cases -f). + execute Execute an operation on data stored in the datastore (requires -o, -u, and in certain cases -f or -b and -t). import Import the specified directory into the datastore (requires -d). export Export the previously-imported specified directory to the current directory (requires -d). optimize Optimize search indexes.@@ -38,8 +41,9 @@ vacuum Vacuum datastore.
Options: -a, --address Specify server address (default: 127.0.0.1). + -b, --body Specify a string containing input data for an operation to be executed. -d, --directory Specify a directory to serve, import, export, delete, or mount. - -f, --file Specify a file to read containing input data for an operation to be executed. + -b, --body Specify a file containing input data for an operation to be executed. -h, --help Display this message. -l, --log Specify the log level: debug, info, warn, error, none (default: info) -m, --mount Mirror database changes to the specified directory on the filesystem.@@ -47,6 +51,7 @@ -o, --operation Specify an operation to execute via the execute command: get, put, delete, patch, post, head, options.
-p, --port Specify server port number (default: 9500). -r, --readonly Allow only data retrieval operations. -s, --store Specify a datastore file (default: data.db) + -t, --type Specify a content type for the body an operation to be executed via the execute command. -u, --uri Specify an uri to execute an operation through the execute command. -v, --version Display the program version. """@@ -116,6 +121,14 @@ of "uri", "u":
if val == "": fail(108, "URI not specified.") exUri = val + of "body", "b": + if val == "": + fail(112, "Body not specified.") + exBody = val + of "type", "t": + if val == "": + fail(113, "Content type not specified.") + exType = val of "mount", "m": mount = true of "version", "v":@@ -155,5 +168,7 @@ LS.favicon = favicon
LS.loglevel = loglevel LS.mount = mount LS.execution.file = exFile +LS.execution.body = exBody +LS.execution.ctype = exType LS.execution.uri = exUri LS.execution.operation = exOperation
@@ -233,6 +233,8 @@ var res = store.db.execAffectedRows(SQL_UPDATE_DOCUMENT, data, contenttype, binary, searchable, currentTime(), id)
if res > 0: if binary <= 0 and searchable >= 0: store.db.exec(SQL_UPDATE_SEARCHCONTENT, data.toPlainText, id) + store.destroyDocumentSystemTags(id) + store.addDocumentSystemTags(id, contenttype) if store.hasMirror and id.startsWith(store.mount): var filename = id.unixToNativePath if fileExists(filename):
@@ -39,9 +39,9 @@ elif info.resource.match(peg"^dir$"):
if LS.directory != nil: return api_v3.serveFile(req, LS, info.id) else: - return resError(Http400, "Bad request - Not serving any directory." % info.version) + return resError(Http400, "Bad Request - Not serving any directory." % info.version) else: - return resError(Http400, "Bad request - Invalid resource: $1" % info.resource) + return resError(Http400, "Bad Request - Invalid resource: $1" % info.resource) elif info.version == "v2": if info.resource.match(peg"^docs / info$"): return api_v2.route(req, LS, info.resource, info.id)@@ -49,9 +49,9 @@ elif info.resource.match(peg"^dir$"):
if LS.directory != nil: return api_v2.serveFile(req, LS, info.id) else: - return resError(Http400, "Bad request - Not serving any directory." % info.version) + return resError(Http400, "Bad Request - Not serving any directory." % info.version) else: - return resError(Http400, "Bad request - Invalid resource: $1" % info.resource) + return resError(Http400, "Bad Request - Invalid resource: $1" % info.resource) elif info.version == "v1": if info.resource.match(peg"^docs / info$"): return api_v1.route(req, LS, info.resource, info.id)@@ -59,17 +59,17 @@ elif info.resource.match(peg"^dir$"):
if LS.directory != nil: return api_v1.serveFile(req, LS, info.id) else: - return resError(Http400, "Bad request - Not serving any directory." % info.version) + return resError(Http400, "Bad Request - Not serving any directory." % info.version) else: - return resError(Http400, "Bad request - Invalid resource: $1" % info.resource) + return resError(Http400, "Bad Request - Invalid resource: $1" % info.resource) else: if info.version == "v1" or info.version == "v2" or info.version == "v3": - return resError(Http400, "Bad request - Invalid API version: $1" % info.version) + return resError(Http400, "Bad Request - Invalid API version: $1" % info.version) else: if info.resource.decodeURL.strip == "": - return resError(Http400, "Bad request - No resource specified." % info.resource) + return resError(Http400, "Bad Request - No resource specified." % info.resource) else: - return resError(Http400, "Bad request - Invalid resource: $1" % info.resource) + return resError(Http400, "Bad Request - Invalid resource: $1" % info.resource) proc process*(req: LSRequest, LS: LiteStore): LSResponse {.gcsafe.}= var matches = @["", "", ""]
@@ -19,6 +19,8 @@ uarray* {.unchecked.} [T] = array[0..0, T]
ExecutionData* = object operation*: string file*: string + body*: string + ctype*: string uri*: string Datastore* = object db*: DbConn
@@ -31,14 +31,16 @@ var select_tagged = "SELECT document_id FROM tags WHERE tag_id = '"
result = "" for tag in tags.split(','): if not tag.match(PEG_TAG): - raise newException(EInvalidTag, "Invalid tag '$1'" % tag) + raise newException(EInvalidTag, "Invalid Tag '$1'" % tag) result = result & "AND " & doc_id_col & " IN (" & select_tagged & tag & "') " proc prepareSelectDocumentsQuery*(options: var QueryOptions): string = var tables = options.tables result = "SELECT " if options.jsonFilter.len > 0 or options.jsonSelect.len > 0: - if not options.tags.contains("$subtype:json"): + if options.tags.len == 0: + options.tags = "$subtype:json" + elif not options.tags.contains("$subtype:json"): options.tags = options.tags.split(",").concat(@["$subtype:json"]).join(",") if options.search.len > 0: if options.select[0] != "COUNT(docid)":@@ -215,7 +217,7 @@ if req.reqMethod == HttpPost or req.reqMethod == HttpPut or req.reqMethod == HttpPatch:
var ct = "" let body = req.body.strip if body == "": - return resError(Http400, "Bad request: No content specified for document.") + return resError(Http400, "Bad Request: No content specified for document.") if req.headers.hasKey("Content-Type"): ct = req.headers["Content-Type"] case ct:
@@ -31,6 +31,8 @@ {.passC: "-DSQLITE_ENABLE_FTS3=1 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_JSON1".}
proc executeOperation*() = let file = LS.execution.file + let body = LS.execution.body + let ctype = LS.execution.ctype let uri = LS.execution.uri let operation = LS.execution.operation var req:LSRequest@@ -51,9 +53,13 @@ of "HEAD":
req.reqMethod = HttpHead else: fail(203, "Operation '$1' is not supported" % [operation]) - if not file.isNil: + if not body.isNil: + req.body = body + elif not file.isNil: req.body = file.readFile req.headers = newHttpHeaders() + if not ctype.isNil: + req.headers["Content-Type"] = ctype req.hostname = "<cli>" req.url = parseUri("$1://$2:$3/$4" % @["http", "localhost", "9500", uri]) let resp = req.process(LS)
@@ -1,6 +1,6 @@
[Package] name = "litestore" -version = "1.3.1" +version = "1.4.0" author = "Fabio Cevasco" description = "Self-contained, lightweight, RESTful document store." license = "MIT"