diff --git a/async_couch/__init__.py b/async_couch/__init__.py index f385ac5..90657e4 100644 --- a/async_couch/__init__.py +++ b/async_couch/__init__.py @@ -17,7 +17,9 @@ class CouchClient(DocEndpoint, DesignDocEndpoint, DesignViewEndpoint, DatabaseEndpoint): - pass + + async def close(self): + await self.http_client.aclose() def get_couch_client(https: bool = False, @@ -53,5 +55,9 @@ def get_couch_client(https: bool = False, if https: schema += 's' - http_client = request_adapter.get_client(f'{schema}://{host}:{port}', **kwargs) + url = f'{schema}://{host}' + if port: + url += f':{port}' + + http_client = request_adapter.get_client(url, **kwargs) return CouchClient(http_client=http_client) diff --git a/async_couch/clients/database/endpoints.py b/async_couch/clients/database/endpoints.py index 3cd6024..4b16606 100644 --- a/async_couch/clients/database/endpoints.py +++ b/async_couch/clients/database/endpoints.py @@ -1,7 +1,6 @@ -import typing - from async_couch import types from async_couch.clients.designs.responses import ExecuteViewResponse + from async_couch.http_clients.base_client import BaseEndpoint from . import responses as resp @@ -587,7 +586,7 @@ async def db_bulk_get(self, result['id'] = id return await self.http_client.make_request( - endpoint='/db/_bulk_get', + endpoint='/{db}/_bulk_get', method=types.HttpMethod.POST, statuses={ 200: 'Request completed successfully', @@ -648,7 +647,7 @@ async def db_bulk_docs(self, query['new_edits'] = new_edits return await self.http_client.make_request( - endpoint='/db/_bulk_docs', + endpoint='/{db}/_bulk_docs', method=types.HttpMethod.POST, statuses={ 201: 'Document(s) have been created or updated', @@ -745,39 +744,35 @@ async def db_find(self, exc.CouchResponseError: If server error occurred """ + json_data = dict() - query = dict() - + if selector: + json_data['selector'] = selector if limit: - query['limit'] = limit + json_data['limit'] = limit if skip: - query['skip'] = skip + json_data['skip'] = skip if sort: - query['sort'] = sort + json_data['sort'] = sort if fields: - query['fields'] = fields + json_data['fields'] = fields if use_index: - query['use_index'] = use_index + json_data['use_index'] = use_index if r: - query['r'] = r + json_data['r'] = r if bookmark: - query['bookmark'] = bookmark + json_data['bookmark'] = bookmark if update: - query['update'] = update + json_data['update'] = update if stable: - query['stable'] = stable + json_data['stable'] = stable if stale: - query['stale'] = stale + json_data['stale'] = stale if execution_stats: - query['execution_stats'] = execution_stats - - json_data = dict() - - if selector: - json_data['selector'] = selector + json_data['execution_stats'] = execution_stats return await self.http_client.make_request( - endpoint='/db/_find', + endpoint='/{db}/_find', method=types.HttpMethod.POST, statuses={ 200: 'Request completed successfully', @@ -786,9 +781,8 @@ async def db_find(self, 404: 'Requested database not found', 500: 'Query execution error' }, - query=query, path={'db': db}, json_data=json_data, - response_model=ExecuteViewResponse + response_model=resp.FindResponse ) diff --git a/async_couch/clients/database/responses.py b/async_couch/clients/database/responses.py index c2d8ecf..44044ab 100644 --- a/async_couch/clients/database/responses.py +++ b/async_couch/clients/database/responses.py @@ -1,4 +1,5 @@ from dataclasses import dataclass +import typing from async_couch.types import EmptyResponse @@ -53,3 +54,18 @@ class ServerResponse(EmptyResponse): # on this string for counting the number of updates props_partitioned: models.DatabaseProps + + +@dataclass +class FindResponse(EmptyResponse): + docs: typing.List[dict] + # Array of documents matching the search. In each matching document, the + # fields specified in the fields part of the request body are listed, along + # with their values. + + warning: str + # Execution warnings + + bookmark: str + # An opaque string used for paging. See the bookmark field in the request + # for usage details. diff --git a/async_couch/clients/designs/responses.py b/async_couch/clients/designs/responses.py index 7a9f5dd..1acc417 100644 --- a/async_couch/clients/designs/responses.py +++ b/async_couch/clients/designs/responses.py @@ -19,7 +19,7 @@ class SizeObj: @dataclass class DesignViewIndex: compact_running: bool - """Indicates whether a compaction routine is currently + """Indicates whether a compaction routine is currently running on the view""" sizes: SizeObj @@ -35,7 +35,7 @@ class DesignViewIndex: """MD5 signature of the views for the design document""" update_seq: int or str - """The update sequence of the corresponding database that has been + """The update sequence of the corresponding database that has been indexed""" updater_running: bool @@ -45,7 +45,7 @@ class DesignViewIndex: """Number of clients waiting on views from this design document""" waiting_commit: bool - """Indicates if there are outstanding commits to the underlying database + """Indicates if there are outstanding commits to the underlying database that need to processed""" @@ -71,7 +71,7 @@ class ExecuteViewResponse(EmptyResponse): """Offset where the document list started""" rows: typing.List[ExecuteViewRow] = None - """Array of view row objects. By default the information returned + """Array of view row objects. By default the information returned contains only the document ID and revision""" total_rows: int = None diff --git a/async_couch/clients/documents/endpoints.py b/async_couch/clients/documents/endpoints.py index 648a02e..b15a4a4 100644 --- a/async_couch/clients/documents/endpoints.py +++ b/async_couch/clients/documents/endpoints.py @@ -151,19 +151,19 @@ async def doc_get(self, query['local_seq'] = local_seq if meta: - query['local_seq'] = local_seq + query['meta'] = meta if open_revs: - query['open_revs'] = local_seq + query['open_revs'] = open_revs if rev: - query['rev'] = local_seq + query['rev'] = rev if revs: - query['revs'] = local_seq + query['revs'] = revs if revs_info: - query['revs_info'] = local_seq + query['revs_info'] = revs_info return await self.http_client.make_request( endpoint=self.__doc_endpoint__, @@ -485,7 +485,8 @@ async def attachment_get(self, db: str, doc_id: str, attachment_id: str, - rev: str = None) -> types.UniversalResponse: + rev: str = None, + bytes_range: tuple = None) -> types.UniversalResponse: """ Returns the file attachment associated with the document. The raw data of the associated attachment is returned (just as if you were @@ -508,6 +509,10 @@ async def attachment_get(self, rev: str = None Actual document’s revision + bytes_range: tuple = None + Range of bytes to get from attachment, works only with 'application/octet-stream' + Content-Type of attachment + Returns ---------- `UniversalResponse` @@ -523,16 +528,26 @@ async def attachment_get(self, if rev: query['rev'] = rev + if bytes_range: + if len(bytes_range) == 2: + headers = {'Range': f'bytes={bytes_range[0]}-{bytes_range[1]}'} + else: + headers = {'Range': f'bytes={bytes_range[0]}-'} + else: + headers = None + return await self.http_client.make_request( endpoint=self.__doc_attachment_endpoint__, method=types.HttpMethod.GET, statuses={ 200: 'Attachment exists', + 206: 'Attachment partially read', 401: 'Read privilege required', 404: 'Specified database, document or attachment was not found' }, path={'db': db, 'doc_id': doc_id, 'att_id': attachment_id}, - query=query + query=query, + headers=headers, ) async def attachment_upload(self, diff --git a/async_couch/clients/documents/responses.py b/async_couch/clients/documents/responses.py index 7f09d6d..1194076 100644 --- a/async_couch/clients/documents/responses.py +++ b/async_couch/clients/documents/responses.py @@ -10,6 +10,12 @@ class DocumentExistingResponse(EmptyResponse): e_tag: str # Document’s revision token + @classmethod + def load(cls, response): + e_tag = response.headers.get('ETag') + if e_tag: + return cls(e_tag=e_tag.replace('"', '')) + @dataclass class DocumentDetailedResponse(EmptyResponse):