def save(self): """ Save attachment. @return (mixed) @raises (Exception) """ if not self.document: raise Exception("Attachment document is not defined!") docId = self.document.getId() docRev = self.document.getRev() if not docId: raise Exception("Attachment document _id is required!") if not docRev: raise Exception("Attachment document _rev is required!") if not self.fileName: raise Exception("Attachment file name is required!") # read file data self.readFile(False) headers = {} headers["If-Match"] = docRev headers["Content-Type"] = self.contentType database = self.document.getDatabase() return database.client.put( "%s/%s/%s" % (database.name, util.urlEncode(docId), util.urlEncode(self.fileName)), None, self.data, headers).getBodyData()
def save(self): """ Save attachment. @return (mixed) @raises (Exception) """ if not self.document: raise Exception("Attachment document is not defined!") docId = self.document.getId() docRev = self.document.getRev() if not docId: raise Exception("Attachment document _id is required!") if not docRev: raise Exception("Attachment document _rev is required!") if not self.fileName: raise Exception("Attachment file name is required!") # read file data self.readFile(False) headers = {} headers["If-Match"] = docRev headers["Content-Type"] = self.contentType database = self.document.getDatabase() return database.client.put("%s/%s/%s" % (database.name, util.urlEncode(docId), util.urlEncode(self.fileName)), None, self.data, headers).getBodyData()
def remove(self, batch = False, fullCommit = False): """ Remove attachment. @param (bool) batch @param (bool) fullCommit @return (mixed) @raises (Exception) """ if not self.document: raise Exception("Attachment document is not defined!") docId = self.document.getId() docRev = self.document.getRev() if not docId: raise Exception("Attachment document _id is required!") if not docRev: raise Exception("Attachment document _rev is required!") if not self.fileName: raise Exception("Attachment file name is required!") batch = "?batch=ok" if batch else "" headers = {} headers["If-Match"] = docRev if fullCommit: headers["X-Couch-Full-Commit"] = "true" database = self.document.getDatabase() return database.client.delete("%s/%s/%s%s" % (database.name, util.urlEncode(docId), util.urlEncode(self.fileName), batch), None, headers).getBodyData()
def ping(self, *args): """ Ping a document attachment. @param (int) *args Expected status code(s). @return (bool) @raises (Exception) """ if not self.document: raise Exception("Attachment document is not defined!") docId = self.document.getId() docRev = self.document.getRev() if not docId: raise Exception("Attachment document _id is required!") if not self.fileName: raise Exception("Attachment file name is required!") query, headers = {}, {} if docRev: query["rev"] = docRev if self.digest: headers["If-None-Match"] = '"%s"' % (self.digest) database = self.document.getDatabase() response = database.client.head("%s/%s/%s" % (database.name, util.urlEncode(docId), util.urlEncode(self.fileName)), query, headers) return response.getStatusCode() in (args or [200])
def ping(self, *args): """ Ping a document attachment. @param (int) *args Expected status code(s). @return (bool) @raises (Exception) """ if not self.document: raise Exception("Attachment document is not defined!") docId = self.document.getId() docRev = self.document.getRev() if not docId: raise Exception("Attachment document _id is required!") if not self.fileName: raise Exception("Attachment file name is required!") query, headers = {}, {} if docRev: query["rev"] = docRev if self.digest: headers["If-None-Match"] = '"%s"' % (self.digest) database = self.document.getDatabase() response = database.client.head( "%s/%s/%s" % (database.name, util.urlEncode(docId), util.urlEncode(self.fileName)), query, headers) return response.getStatusCode() in (args or [200])
def getBodyData(self, key=None): """ Get body data (parsed). @param (str) key """ bodyData = {} # should parsed? if self.getHeader("Content-Type") == "application/json": bodyData = util.jsonDecode(self.body or "") if key != None: return util.dig(key, bodyData) return bodyData
def find(self): """ Find attachment. @return (mixed|dict|None) @raises (Exception) """ if not self.document: raise Exception("Attachment document is not defined!") docId = self.document.getId() docRev = self.document.getRev() if not docId: raise Exception("Attachment document _id is required!") if not self.fileName: raise Exception("Attachment file name is required!") query = {} if docRev: query["rev"] = docRev headers = {} headers["Accept"] = "*/*" headers["Content-Type"] = None if self.digest: headers["If-None-Match"] = '"%s"' % (self.digest) database = self.document.getDatabase() response = database.client.get( "%s/%s/%s" % (database.name, util.urlEncode(docId), util.urlEncode(self.fileName)), query, headers) # check response status code if response.getStatusCode() in [200, 304]: ret = {} ret["content"] = response.getBody() ret["content_type"] = response.getHeader("Content-Type") ret["content_length"] = response.getHeader("Content-Length") md5 = response.getHeader("Content-MD5") if md5: ret["digest"] = "md5-" + md5 else: ret["digest"] = "md5-" + (response.getHeader("ETag") or "").strip('"') return ret
def findRevisions(self): """ Find a document's revisions. @return (dict|None) """ return util.dig("_revisions", self.find({"revs": True}))
def copy(self, dest, batch = False, fullCommit = False): """ Copy a document to a destination. @param (str) dest @param (bool) batch @param (bool) fullCommit @return (mixed) @raises (Exception) """ if not self.id: raise Exception("_id field could not be empty!") if not dest: raise Exception("Destination could not be empty!") batch = "?batch=ok" if batch else "" headers = {} headers["Destination"] = dest if fullCommit: headers["X-Couch-Full-Commit"] = "true" return self.database.client.copy(self.database.name +"/"+ util.urlEncode(self.id) + batch, None, headers).getBodyData()
def copyTo(self, dest, destRev, batch = False, fullCommit = False): """ Copy a (this) document to an existing document. @param (str) dest @param (str) destRev @param (bool) batch @param (bool) fullCommit @return (mixed) @raises (Exception) """ if not self.id or not self.rev: raise Exception("Both _id & _rev fields could not be empty!") if not dest or not destRev: raise Exception("Destination & destination revision could not be empty!") batch = "?batch=ok" if batch else "" headers = {} headers["If-Match"] = self.rev headers["Destination"] = "%s?rev=%s" % (dest, destRev) if fullCommit: headers["X-Couch-Full-Commit"] = "true" return self.database.client.copy(self.database.name +"/"+ util.urlEncode(self.id) + batch, None, headers).getBodyData()
def copy(self, dest, batch=False, fullCommit=False): """ Copy a document to a destination. @param (str) dest @param (bool) batch @param (bool) fullCommit @return (mixed) @raises (Exception) """ if not self.id: raise Exception("_id field could not be empty!") if not dest: raise Exception("Destination could not be empty!") batch = "?batch=ok" if batch else "" headers = {} headers["Destination"] = dest if fullCommit: headers["X-Couch-Full-Commit"] = "true" return self.database.client.copy( self.database.name + "/" + util.urlEncode(self.id) + batch, None, headers).getBodyData()
def toJson(self, encode=True): """ Get attachment data as json string that CouchDB expects. @param (bool) encode @return (str) """ return util.jsonEncode(self.toArray(encode))
def __init__(self, client): """ Object constructor. @param (couch.Client) client """ self.type = Stream.TYPE_REQUEST self.httpVersion = "1.0" self.client = client # reset headers (for each "extends" operation, interesting..) self.headers = {} # set default headers self.headers["Host"] = "%s:%s" % (self.client.host, self.client.port) self.headers["Connection"] = "close" self.headers["Accept"] = "application/json" self.headers["Content-Type"] = "application/json" self.headers["User-Agent"] = "%s/v%s (+http://github.com/yay-couch/couch-py)" % \ (couch.Couch.NAME, couch.Couch.VERSION) # set basic authorization header if self.client.username and self.client.password: self.headers["Authorization"] = "Basic "+ \ util.base64Encode(self.client.username +":"+ self.client.password)
def findRevisionsExtended(self): """ Find a document's revisions as extended result. @return (dict|None) """ return util.dig("_revs_info", self.find({"revs_info": True}))
def copyTo(self, dest, destRev, batch=False, fullCommit=False): """ Copy a (this) document to an existing document. @param (str) dest @param (str) destRev @param (bool) batch @param (bool) fullCommit @return (mixed) @raises (Exception) """ if not self.id or not self.rev: raise Exception("Both _id & _rev fields could not be empty!") if not dest or not destRev: raise Exception( "Destination & destination revision could not be empty!") batch = "?batch=ok" if batch else "" headers = {} headers["If-Match"] = self.rev headers["Destination"] = "%s?rev=%s" % (dest, destRev) if fullCommit: headers["X-Couch-Full-Commit"] = "true" return self.database.client.copy( self.database.name + "/" + util.urlEncode(self.id) + batch, None, headers).getBodyData()
def toJson(self, encode = True): """ Get attachment data as json string that CouchDB expects. @param (bool) encode @return (str) """ return util.jsonEncode(self.toArray(encode))
def find(self): """ Find attachment. @return (mixed|dict|None) @raises (Exception) """ if not self.document: raise Exception("Attachment document is not defined!") docId = self.document.getId() docRev = self.document.getRev() if not docId: raise Exception("Attachment document _id is required!") if not self.fileName: raise Exception("Attachment file name is required!") query = {} if docRev: query["rev"] = docRev headers = {} headers["Accept"] = "*/*" headers["Content-Type"] = None if self.digest: headers["If-None-Match"] = '"%s"' % (self.digest) database = self.document.getDatabase() response = database.client.get("%s/%s/%s" % (database.name, util.urlEncode(docId), util.urlEncode(self.fileName)), query, headers) # check response status code if response.getStatusCode() in [200, 304]: ret = {} ret["content"] = response.getBody() ret["content_type"] = response.getHeader("Content-Type") ret["content_length"] = response.getHeader("Content-Length") md5 = response.getHeader("Content-MD5") if md5: ret["digest"] = "md5-"+ md5 else: ret["digest"] = "md5-"+ (response.getHeader("ETag") or "").strip('"') return ret
def toString(self): """ String wrap. @return (str) """ url = util.urlParse(self.uri) return super(Request, self).toString( "%s %s?%s HTTP/%s\r\n" % (self.method, url.path, url.query, self.httpVersion))
def findAttachments(self, attEncInfo = False, attsSince = []): """ Find a document's attachments. @param (bool) attEncInfo @param (dict|None) attsSince @return (dict|None) """ query = {} query["attachments"] = True query["att_encoding_info"] = attEncInfo if attsSince: attsSinceArray = [] for attsSinceValue in attsSince: attsSinceArray.append('"%s"' % util.quote(attsSinceValue)) query["atts_since"] = "[%s]" % ",".join(attsSinceArray) return util.dig("_attachments", self.find(query))
def findAttachments(self, attEncInfo=False, attsSince=[]): """ Find a document's attachments. @param (bool) attEncInfo @param (dict|None) attsSince @return (dict|None) """ query = {} query["attachments"] = True query["att_encoding_info"] = attEncInfo if attsSince: attsSinceArray = [] for attsSinceValue in attsSince: attsSinceArray.append('"%s"' % util.quote(attsSinceValue)) query["atts_since"] = "[%s]" % ",".join(attsSinceArray) return util.dig("_attachments", self.find(query))
def getData(self, key=None): """ Get document data value. @param (str) key @return (mixed) """ if key != None: return util.dig(key, self.data) return self.data
def getData(self, key = None): """ Get document data value. @param (str) key @return (mixed) """ if key != None: return util.dig(key, self.data) return self.data
def readFile(self, encode=True): """ Read file contents, set attachment data, data length and content type. @param (bool) encode @return (None) @raises (Exception) """ if not self.file: raise Exception("Attachment file is empty!") # detect content type info = util.fileInfo(self.file) self.contentType = info["mime"] data = util.fileGetContents(self.file) self.data = data if encode: self.data = util.base64Encode(data) self.dataLength = len(data)
def request(self, uri, uriParams = {}, body = None, headers = {}): """ Make a HTTP request using Request and return Response. @param (str) uri @param (dict) uriParams @param (mixed) body @param (dict) headers @return (couch.http.Response) @throws (Exception) """ # match for a valid request i.e: HEAD /foo r = re.match("^([A-Z]+)\s+(/.*)", uri) if not r: raise Exception("Usage: <REQUEST METHOD> <REQUEST URI>!") m = r.groups() if len(m) < 2: raise Exception("Usage: <REQUEST METHOD> <REQUEST URI>!") self.Request = Request(self) self.Response = Response() # merge host, port and uri uri = "%s:%s/%s" % (self.host, self.port, m[1].strip(" /")) # set request method, uri, body self.Request \ .setMethod(m[0]) \ .setUri(uri, uriParams) \ .setBody(body) # set request headers (if any) for key, value in headers.items(): self.Request.setHeader(key, value) result = self.Request.send() if result != "": headers, body = result.split("\r\n\r\n", 1) headers = util.parseHeaders(headers) if headers: for key, value in headers.items(): # status line if key == "0": self.Response.setStatus(value) self.Response.setHeader(key, value) self.Response.setBody(body) # is error? if self.Response.getStatusCode() >= 400: self.Response.setError() return self.Response
def readFile(self, encode = True): """ Read file contents, set attachment data, data length and content type. @param (bool) encode @return (None) @raises (Exception) """ if not self.file: raise Exception("Attachment file is empty!") # detect content type info = util.fileInfo(self.file) self.contentType = info["mime"] data = util.fileGetContents(self.file) self.data = data if encode: self.data = util.base64Encode(data) self.dataLength = len(data)
def request(self, uri, uriParams={}, body=None, headers={}): """ Make a HTTP request using Request and return Response. @param (str) uri @param (dict) uriParams @param (mixed) body @param (dict) headers @return (couch.http.Response) @throws (Exception) """ # match for a valid request i.e: HEAD /foo r = re.match("^([A-Z]+)\s+(/.*)", uri) if not r: raise Exception("Usage: <REQUEST METHOD> <REQUEST URI>!") m = r.groups() if len(m) < 2: raise Exception("Usage: <REQUEST METHOD> <REQUEST URI>!") self.Request = Request(self) self.Response = Response() # merge host, port and uri uri = "%s:%s/%s" % (self.host, self.port, m[1].strip(" /")) # set request method, uri, body self.Request \ .setMethod(m[0]) \ .setUri(uri, uriParams) \ .setBody(body) # set request headers (if any) for key, value in headers.items(): self.Request.setHeader(key, value) result = self.Request.send() if result != "": headers, body = result.split("\r\n\r\n", 1) headers = util.parseHeaders(headers) if headers: for key, value in headers.items(): # status line if key == "0": self.Response.setStatus(value) self.Response.setHeader(key, value) self.Response.setBody(body) # is error? if self.Response.getStatusCode() >= 400: self.Response.setError() return self.Response
def setError(self, body=None): """ Set error. @param (mixed) body @return (None) """ body = util.jsonDecode(body or self.body or "") if type(body) is dict and ("error" in body) and ("reason" in body): self.error = "Stream Error >> error: '%s', reason: '%s'" % (body["error"], body["reason"]) self.errorData["error"] = body["error"] self.errorData["reason"] = body["reason"]
def __init__(self, document = None, file = None, fileName = None): """ Object constructor. @param (couch.Document) document @param (str) file @param (str) fileName """ if document: self.setDocument(document) if file: self.file = file self.fileName = fileName if fileName else util.basename(file)
def __init__(self, document=None, file=None, fileName=None): """ Object constructor. @param (couch.Document) document @param (str) file @param (str) fileName """ if document: self.setDocument(document) if file: self.file = file self.fileName = fileName if fileName else util.basename(file)
def getDocument(self, key): """ Get a document by given key (docid). @param (str) key @return (mixed) """ data = self.client.get(self.name +"/_all_docs", { "include_docs": True, "key" : "\"%s\"" % util.quote(key), }).getBodyData() try: return data["rows"][0] except: pass
def remove(self, batch=False, fullCommit=False): """ Remove attachment. @param (bool) batch @param (bool) fullCommit @return (mixed) @raises (Exception) """ if not self.document: raise Exception("Attachment document is not defined!") docId = self.document.getId() docRev = self.document.getRev() if not docId: raise Exception("Attachment document _id is required!") if not docRev: raise Exception("Attachment document _rev is required!") if not self.fileName: raise Exception("Attachment file name is required!") batch = "?batch=ok" if batch else "" headers = {} headers["If-Match"] = docRev if fullCommit: headers["X-Couch-Full-Commit"] = "true" database = self.document.getDatabase() return database.client.delete( "%s/%s/%s%s" % (database.name, util.urlEncode(docId), util.urlEncode(self.fileName), batch), None, headers).getBodyData()
def getDocument(self, key): """ Get a document by given key (docid). @param (str) key @return (mixed) """ data = self.client.get(self.name + "/_all_docs", { "include_docs": True, "key": "\"%s\"" % util.quote(key), }).getBodyData() try: return data["rows"][0] except: pass
def send(self, body=None): """ Send. @param (str) method @return (str) """ url = util.urlParse(self.uri) sock, err = None, None send, recv = "", "" send += "%s %s?%s HTTP/%s\r\n" % (self.method, url.path, url.query, self.httpVersion) for key, value in self.headers.items(): if value != None: send += "%s: %s\r\n" % (key, value) send += "\r\n" send += self.getBody() or "" try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((self.client.host, self.client.port)) try: sock.sendall(send) except: sock.sendall(bytes(send, "utf8")) while True: buff = sock.recv(1024) if not buff: # eof break recv += str(buff, "utf8") except Exception as e: err = e finally: if sock: sock.close() # dump whole http messages (request/response) if self.client.couch.DEBUG == True: print(send) print(recv) if err: raise err return recv
def find(self, query = {}): """ Find a document. @param (dict) query @return (mixed) @raises (Exception) """ if not self.id: raise Exception("_id field is could not be empty!") query = query or {} if "rev" not in query and self.rev: query["rev"] = self.rev return self.database.client.get(self.database.name +"/"+ util.urlEncode(self.id), query).getBodyData()
def setBody(self, body=None): """ Set body. @param (mixed) body @return (self) """ if (body != None and self.method != Request.METHOD_HEAD and self.method != Request.METHOD_GET): # decode if provided if self.getHeader("Content-Type") == "application/json": body = util.jsonEncode(body) self.body = body self.headers["Content-Length"] = len(body) return self
def __setattr__(self, name, value): """ Setter for magic actions. @param (str) name @param (str) value @return (None) @raises (Exception) """ if not hasattr(self, name): raise Exception("`%s` property does not exists on this object!" % name) # file is exception if name == "file": super.__setattr__(self, "file", value) super.__setattr__(self, "fileName", util.basename(value)) else: super.__setattr__(self, name, value)
def setBody(self, body = None): """ Set body. @param (mixed) body @return (self) """ if (body != None and self.method != Request.METHOD_HEAD and self.method != Request.METHOD_GET): # decode if provided if self.getHeader("Content-Type") == "application/json": body = util.jsonEncode(body) self.body = body self.headers["Content-Length"] = len(body) return self
def find(self, query={}): """ Find a document. @param (dict) query @return (mixed) @raises (Exception) """ if not self.id: raise Exception("_id field is could not be empty!") query = query or {} if "rev" not in query and self.rev: query["rev"] = self.rev return self.database.client.get( self.database.name + "/" + util.urlEncode(self.id), query).getBodyData()
def ping(self, *args): """ Ping document. @param (int) *args Expected status code(s). @return (bool) @raises (Exception) """ if not self.id: raise Exception("_id field is could not be empty!") headers = {} if self.rev != None: headers["If-None-Match"] = '"%s"' % (self.rev) response = self.database.client.head( self.database.name + "/" + util.urlEncode(self.id), None, headers) return response.getStatusCode() in (args or [200])
def ping(self, *args): """ Ping document. @param (int) *args Expected status code(s). @return (bool) @raises (Exception) """ if not self.id: raise Exception("_id field is could not be empty!") headers = {} if self.rev != None: headers["If-None-Match"] = '"%s"' % (self.rev) response = self.database.client.head(self.database.name +"/"+ util.urlEncode(self.id), None, headers) return response.getStatusCode() in (args or [200])
def send(self, body = None): """ Send. @param (str) method @return (str) """ url = util.urlParse(self.uri) sock, err = None, None send, recv = "", "" send += "%s %s?%s HTTP/%s\r\n" % (self.method, url.path, url.query, self.httpVersion) for key, value in self.headers.items(): if value != None: send += "%s: %s\r\n" % (key, value) send += "\r\n" send += self.getBody() or "" try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((self.client.host, self.client.port)) sock.sendall(send) while True: buff = sock.recv(1024) if not buff: # eof break recv += buff except Exception as e: err = e finally: if sock: sock.close() # dump whole http messages (request/response) if self.client.couch.DEBUG == True: print send print recv if err: raise err return recv
def remove(self, batch = False, fullCommit = False): """ Remove a document. @param (bool) batch @param (bool) fullCommit @return (mixed) @raises (Exception) """ if not self.id and not self.rev: raise Exception("Both _id & _rev fields could not be empty!") batch = "?batch=ok" if batch else "" headers = {} headers["If-Match"] = self.rev if fullCommit: headers["X-Couch-Full-Commit"] = "true" return self.database.client.delete(self.database.name +"/"+ util.urlEncode(self.id) + batch, None, headers).getBodyData()
def save(self, batch=False, fullCommit=False): """ Create or update a document. @param (bool) batch @param (bool) fullCommit @return (mixed) """ batch = "?batch=ok" if batch else "" headers = {} if fullCommit: headers["X-Couch-Full-Commit"] = "true" if self.rev: headers["If-Match"] = self.rev data = self.getData() if self.attachments: data["_attachments"] = {} for name, attachment in self.attachments.items(): data["_attachments"][name] = attachment.toArray() # insert action if not self.id: ret = self.database.client.post(self.database.name + batch, None, data, headers).getBodyData() if ret and ("id" in ret): self.setId(ret["id"]) # update action else: ret = self.database.client.put( self.database.name + "/" + util.urlEncode(self.id) + batch, None, data, headers).getBodyData() # for next instant call(s) if ret and ("rev" in ret): self.setRev(ret["rev"]) return ret
def remove(self, batch=False, fullCommit=False): """ Remove a document. @param (bool) batch @param (bool) fullCommit @return (mixed) @raises (Exception) """ if not self.id and not self.rev: raise Exception("Both _id & _rev fields could not be empty!") batch = "?batch=ok" if batch else "" headers = {} headers["If-Match"] = self.rev if fullCommit: headers["X-Couch-Full-Commit"] = "true" return self.database.client.delete( self.database.name + "/" + util.urlEncode(self.id) + batch, None, headers).getBodyData()
def save(self, batch = False, fullCommit = False): """ Create or update a document. @param (bool) batch @param (bool) fullCommit @return (mixed) """ batch = "?batch=ok" if batch else "" headers = {} if fullCommit: headers["X-Couch-Full-Commit"] = "true" if self.rev: headers["If-Match"] = self.rev data = self.getData() if self.attachments: data["_attachments"] = {} for name, attachment in self.attachments.items(): data["_attachments"][name] = attachment.toArray() # insert action if not self.id: ret = self.database.client.post(self.database.name + batch, None, data, headers).getBodyData() if ret and ("id" in ret): self.setId(ret["id"]) # update action else: ret = self.database.client.put(self.database.name +"/"+ util.urlEncode(self.id) + batch, None, data, headers).getBodyData() # for next instant call(s) if ret and ("rev" in ret): self.setRev(ret["rev"]) return ret