├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── example └── README.md ├── lib ├── couchdb.dart └── src │ ├── clients │ └── couchdb_client.dart │ ├── databases.dart │ ├── design_documents.dart │ ├── documents.dart │ ├── entities │ ├── database_model_response.dart │ ├── db_response.dart │ ├── design_document_model_response.dart │ ├── document_model_response.dart │ ├── local_document_model_response.dart │ └── server_model_response.dart │ ├── exceptions │ └── couchdb_exception.dart │ ├── interfaces │ ├── client_interface.dart │ ├── databases_interface.dart │ ├── design_documents_interface.dart │ ├── documents_interface.dart │ ├── local_documents_interface.dart │ └── server_interface.dart │ ├── local_documents.dart │ ├── models │ ├── database_model.dart │ ├── design_document_model.dart │ ├── document_model.dart │ ├── local_document_model.dart │ └── server_model.dart │ ├── responses │ ├── api_response.dart │ ├── databases_response.dart │ ├── design_documents_response.dart │ ├── documents_response.dart │ ├── error_response.dart │ ├── local_documents_response.dart │ └── server_response.dart │ ├── server.dart │ └── utils │ └── includer_path.dart ├── pubspec.yaml └── test └── couchdb_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool 3 | .packages 4 | 5 | pubspec.lock 6 | 7 | /build 8 | /doc/api 9 | .vscode 10 | /.idea/ 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Unreleased 2 | - Delete deprecated classes. 3 | 4 | ## [0.7.0]- 2020-06-21 5 | ### Added 6 | - `query` method to `Databases` class. 7 | - New entities for working with CouchDb parts (thanks to [Philippe Jausions](https://github.com/jausions)). 8 | 9 | ### Changed 10 | - Lower constraints of SDK is `2.2.0` (thanks to [Philippe Jausions](https://github.com/jausions)). 11 | 12 | ### Deprecated 13 | - Old entities for working with CouchDb parts (thanks to [Philippe Jausions](https://github.com/jausions)). 14 | 15 | ## 0.6.0 16 | 17 | - Remove models classes and relative concept. Now `Component` is the base class for others derivative. And all transitive files was deleted (`DocumentModelBase` and so on...). `Model` word is removed from all classes. `DbResponse` is renamed into `Response`. 18 | - Fix: `docId` in `LocalDocument` now must accept exactly id of document without *_local/* appendix. 19 | - Update supported Dart SDK to `>=2.4.0 <3.0.0`. 20 | - Upgrade packages. 21 | - Update example. 22 | 23 | ## 0.5.5 24 | 25 | - Change own rules of `analysis_options.yaml` to `pedantic` package. 26 | 27 | ## 0.5.4 28 | 29 | - Convert `DbResponse` result object directly to `DatabaseModelResponse` in `changesIn()` and 30 | `postChangesIn()` methods of `DatabaseModel` class. 31 | - Update supported Dart SDK to `>=2.2.0 <3.0.0`. 32 | 33 | ## 0.5.3 34 | 35 | - Add missing parameters to `DatabaseModel.allDocs()`. Thanks to [dominickj-tdi](https://github.com/dominickj-tdi). 36 | - Fix **pana** warnings. 37 | 38 | ## 0.5.2 39 | 40 | - Added a path parameter to the CouchDbClient constructor. Thanks to [dominickj-tdi](https://github.com/dominickj-tdi). 41 | - Fixed bugs in `CouchDBClient.copy()` and `DocumentModel.copyDoc()`. Thanks to [dominickj-tdi](https://github.com/dominickj-tdi). 42 | - Update README. 43 | 44 | ## 0.5.1 45 | 46 | - Add possibility to connect to CouchDb as anonymous user. 47 | - Rewrite methods of model classes to return more concrete response. Thanks to [dominickj-tdi](https://github.com/dominickj-tdi). 48 | - Update README. 49 | 50 | ## 0.5.0 51 | 52 | - Make `username` and `password` parameters of `CouchDbClient` **@required** and add `scheme` parameter. 53 | - Add `CouchDbClient.fromString` and `CouchDbClient.fromUri` constructors. 54 | - Fix passing to `CouchDbClient(...)` constructor value of `host` parameter with protocol like `http://0.0.0.0` according to [this](https://github.com/YevhenKap/couchdb_dart/issues/8) issue. 55 | - Remove `origin` parameter from `CouchDbClient` constructors. 56 | - Add CONTRIBUTING. 57 | 58 | ## 0.4.3+1 59 | 60 | - Downgrade `meta` dependency to `^1.1.6` according to [this](https://github.com/YevhenKap/couchdb_dart/issues/7) issue. 61 | 62 | ## 0.4.3 63 | 64 | - Change signature of `CouchDbClient` to provide `origin` parameter. 65 | - Small fix of docs. 66 | 67 | ## 0.4.2 68 | 69 | - Fix `secret` encoding for non-proxy authentication. 70 | - Make most fields in `CouchDbClient` final. 71 | - Add `shards()`, `shard()` and `synchronizeShards()` methods to `DatabaseModel` class. 72 | - Fix `Origin` construction. 73 | 74 | ## 0.4.1 75 | 76 | - Add `secret` field to `CouchDbClient` class for proxy authentication. 77 | - Edit README. 78 | 79 | ## 0.4.0 80 | 81 | - Add cookie authentication. 82 | - Add proxy authentication. 83 | - Add `authenticate()`, `logout()` and `userInfo()` methods to `CouchDbClient` class. 84 | - Add related to authentication fields to `ServerModelResponse` class. 85 | 86 | ## 0.3.0 87 | 88 | - Unit `CouchDbWebClient` and `CouchDbServerClient` to `CouchDbClient` class. 89 | - Add `streamed()` method to `CouchDbClient` class. 90 | - Change `DatabaseModel.changesIn()` and `DatabaseModel.postChangesIn()` methods to return stream of event responses (fix for alive connection). 91 | - Makes all fields of `...Response` classes final. 92 | 93 | ## 0.2.1 94 | 95 | - Fix cast `revsDiff` property in `DbResponse` class. 96 | 97 | ## 0.2.0 98 | 99 | - Split `DbResponse` to five classes corresponds to categories of CouchDB: 100 | 101 | 1. Server 102 | 2. Database 103 | 3. Documents 104 | 4. Design documents 105 | 5. Local documents 106 | 107 | - Change signatures of some methods (check your code for error and reference to Docs). 108 | - Add `purgedInfosLimit()` and `setPurgedInfosLimit()` to `DatabaseModel` class. 109 | - Improve docs in API docs (add return's JSON example). 110 | - Move `headers` to separate field in `DbResponse` from json. 111 | - Improved README. 112 | 113 | ## 0.1.4+1 114 | 115 | - Downgrade required Dart SDK to `2.1.0-dev.9.4`. 116 | 117 | ## 0.1.4 118 | 119 | - Move `cors` field to `CouchDbWebClient` class. 120 | - Remove `cors` parameter from constructor of `CouchDbServerClient` and `CouchDbBaseClient` classes. 121 | 122 | ## 0.1.3 123 | 124 | - Add `_headers` field, `headers` getter and improve `modifyRequestHeaders()` method of `CouchDbBaseClient` class. 125 | - Move fields and getters from `CouchDb(Server/Web)Client` to `CouchDbBaseClient`. 126 | - Remove `_client` from `CouchDb(Server/Web)Client` and change constructors from being `factory` to usual. 127 | - Add `cors` field to `CouchDbBaseClient` class and its constructor. 128 | 129 | ## 0.1.2 130 | 131 | - Improve README. 132 | - Rename `getAllDocs()` to `allDocs()`, `getAllDesignDocs()` to `allDesignDocs()`, 133 | `getBulkDocs()` to `bulkDocs()`, `getDocsByKeys()` to `docsByKeys()` methods of `DatabaseModel` class. 134 | - Rename `getDesignDoc()` to `designDoc()`, `getAttachment()` to `attachment()` methods 135 | of `DesignDocumentModel` class. 136 | - Rename `getDoc()` to `doc()`, `getAttachment()` to `attachment()` methods of `DocumentModel` class. 137 | - Rename `getLocalDocs()` to `localDocs()`, `getLocalDocsWithKeys()` to `localDocsWithKeys()`, 138 | `getLocalDoc()` to `localDoc()` methods of `LocalDocumentModel` class. 139 | - Rename `getClusterSetup()` to `clusterSetupStatus()` method of `ServerModel` class. 140 | - Add `includeDocs` parameter to `allDocs()` method of `DatabaseModel` class. 141 | 142 | ## 0.1.1 143 | 144 | - Small changes that don't touch main classes. 145 | 146 | ## 0.1.0 147 | 148 | - Introducing beta-version of library. 149 | - Implement all methods of `ServerModel` class. 150 | - Implement all methods of `DesignDocumentModel` class. 151 | - Add library description to `CouchDb(Server/Web)Client` classes. 152 | - Add `raw` field to `DbResponse` class (prior `rawBody`). 153 | - Change `body` parameter of `post()` method in `CouchDb(Server/Web)Client` to `Object` type. 154 | 155 | ## 0.0.8 156 | 157 | - Implements `schedulerJobs()`, `schedulerDocs()`, `schedulerDocsWithReplicatorDbName()`, `schedulerDocsWithDocId()`, 158 | `nodeStats()`, `systemStatsForNode()`, `up()` and `uuids()` methods of `ServerModel` class. 159 | - Add some fields to `DbResponse` class. 160 | 161 | ## 0.0.7 162 | 163 | - Change `toString()` method of `CouchDbException` class - it shows error code, name error and reason. 164 | - Add `allNodes`, `clusterNodes`, `history`, `replicationIdVersion`, `sessionId` and `sourceLastSeq` fields to `DbResponse` class. 165 | - Implements `configureCouchDb()`, `dbUpdates()`, `membership()` and `replicate()` methods of `ServerModel` class. 166 | 167 | ## 0.0.6 168 | 169 | - Moving `CouchDbWebClient` and `CouchDbServerClient` to the separate export. 170 | - Delete `rawBody` field from `DbResponse` class. 171 | - Add `state` field to `DbResponse` class. 172 | - Implements `couchDbInfo()`, `activeTasks()`, `allDbs()`, `dbsInfo()` and `getClusterSetup()` methods of `ServerModel` class. 173 | - Small changes in `CouchDb(Server/Web)Client` classes. 174 | 175 | ## 0.0.5 176 | 177 | - Implement `CouchDbWebClient` class for interacting with CouchDB. 178 | - Moving all examples to `example/README.md`. 179 | 180 | ## 0.0.4 181 | 182 | - Implements `attachmentInfo()`, `getAttachment()`, `insertAttachment()` and `deleteAttachment()` methods of `DocumentModel` class. 183 | - Implement `setRevsLimit()` method of `DatabaseModel` class. 184 | - Method `put()` now accept any body type. 185 | - Non-json response body now available in `rawBody` field of `DbResponse` class. 186 | 187 | ## 0.0.3 188 | 189 | - Implements `insertDoc()`, `deleteDoc()` and `copyDoc()` methods of `DocumentModel` class. 190 | - All methods of `CouchDb(Server/Web)Client` can set custom headers. 191 | 192 | ## 0.0.2 193 | 194 | - Implements `docInfo()` and `getDoc()` methods of `DocumentModel` class. 195 | - Add documentation to `DatabaseModel` methods. 196 | - Add opportunity to add headers to `head()` method of `CouchDb(Server/Web)Client` classes. 197 | 198 | ## 0.0.1 199 | 200 | - Initial version. 201 | - Implement `CouchDbServerClient` for interacting with CouchDB. 202 | - Implements `DatabaseBaseModel` methods. -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Advices 2 | 3 | If you want contribute to this library, please: 4 | 5 | - Write code according to [Dart style](https://www.dartlang.org/guides/language/effective-dart). 6 | - Document all you write. 7 | - Be brief to what extent it is possible. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | A library for Dart developers for work with CouchDb 2 | Copyright (c) <2018> 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | 22 | Contact email: -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A CouchDB client written in Dart (Is not maintained!) 2 | 3 | Created under a MIT-style 4 | [license](https://github.com/YevhenKap/couchdb_dart/blob/master/LICENSE). 5 | 6 | ## Overview 7 | 8 | > CouchDB is a database system that completely embraces the web. Store your data as 9 | > JSON documents. Access them via HTTP. 10 | 11 | A basic understanding of CouchDB is required to use this library. Detailed 12 | information can be found at the [official documentation site](http://docs.couchdb.org/en/stable/api/basics.html). 13 | 14 | ### API 15 | 16 | The connection to the database, along with authentication, is handled via 17 | `CouchDbClient` for both web and server environments. 18 | 19 | Three types of authentication are available: 20 | 21 | - Basic 22 | - Cookie 23 | - Proxy 24 | 25 | For `Basic` authentication simply pass `username` and `password` to constructor: 26 | 27 | ```dart 28 | final c = CouchDbClient(username: 'name', password: 'pass'); 29 | ``` 30 | 31 | For `Cookie` authentication you also must provide `auth` parameter, then call 32 | `authenticate()` method (note that cookies are valid for 10 minutes by default, 33 | you may specify other expiration in the `Expiration` header): 34 | 35 | ```dart 36 | final c = CouchDbClient(username: 'name', password: 'pass', auth: 'cookie'); 37 | final res = await c.authenticate(); 38 | ``` 39 | 40 | > `authenticate()`, `logout()` are suitable only for `cookie` authentication. 41 | > `userInfo()` are suitable for all auth types. 42 | 43 | For `Proxy` authentication pass `username` to constructor and provide: 44 | 45 | - `X-Auth-CouchDB-UserName`: username (by default the `username` passed to the 46 | constructor is used, so it can be skipped); 47 | - `X-Auth-CouchDB-Roles`: comma-separated (,) list of user roles; 48 | - `X-Auth-CouchDB-Token`: authentication token. When `proxy_use_secret` is set 49 | (which is **strongly recommended!**), this header provides an HMAC of the username 50 | to authenticate and the secret token to prevent requests from untrusted sources 51 | (by default the `username` and `secret` passed to constructor are used, 52 | so it can be skipped). 53 | 54 | Headers: 55 | 56 | ```dart 57 | final c = CouchDbClient(username: 'name', auth: 'proxy', secret: '92de07df7e7a3fe14808cef90a7cc0d91'); 58 | c.modifyRequestHeaders({ 59 | 'X-Auth-CouchDB-Roles': 'users,blogger' 60 | }); 61 | ``` 62 | 63 | > Note that `X-Auth-CouchDB-Token` is not required if **proxy_use_secret** sets to `false`. 64 | 65 | [couch_httpd_auth] 66 | proxy_use_secret = false 67 | 68 | > Otherwise you may provide `secret` option which is used to generate token. 69 | > The secret key should be the same on the client and the CouchDB node: 70 | 71 | [couch_httpd_auth] 72 | secret = 92de07df7e7a3fe14808cef90a7cc0d91 73 | 74 | > To use this authentication method make sure that the {chttpd_auth, proxy_authentication_handler} 75 | > value in added to the list of the active chttpd/authentication_handlers: 76 | 77 | [chttpd] 78 | authentication_handlers = {chttpd_auth, cookie_authentication_handler}, {chttpd_auth, proxy_authentication_handler}, {chttpd_auth, default_authentication_handler} 79 | 80 | #### Anonymous user 81 | 82 | You can configure access to your database to anonymous users. 83 | To achieve this you must provide the following option (and don't set 84 | username and password to `CouchDbClient` constructor): 85 | 86 | [chttpd] 87 | require_valid_user = false 88 | 89 | Otherwise, no requests will be allowed from anonymous users. 90 | 91 | #### Client 92 | 93 | If you wish you can communicate with the server directly via the client's 94 | methods such as `get()` and `post()`, however, other classes provide functions 95 | which can abstract away the particulars of the protocol. Therefore using 96 | the client's methods directly is not the way you will typically use this library. 97 | 98 | Every supported HTTP method: `HEAD`, `GET`, `POST`, `PUT`, and `COPY` has an `Accept` 99 | header with a default value of `application/json`, and `POST` and `PUT` both have 100 | a `Content-Type` header with a default value of `application/json`. 101 | You can override this if you need. 102 | 103 | Most of the client's methods return a `Future` object. 104 | When the future completes normally, it will contain: 105 | - an `ApiResponse.json` property (`Map` type) containing JSON that was sent by CouchDB, 106 | - an `ApiResponse.raw` property (`String` type) for responses that are not 107 | a JSON object (numbers, lists, files,) 108 | - an `ApiResponse.headers` property that contains headers of the HTTP response. 109 | 110 | In case of failure, the exception payload `response` will be an `ErrorResponse` object. 111 | 112 | Because of the sheet number of response information, the package has been organized 113 | around categories, each providing a more specific `...Response` class. 114 | 115 | You can find more information below and in the [package API](https://pub.dev/documentation/couchdb/latest/). 116 | 117 | #### Categories 118 | 119 | The API is divided into five categories, or areas, each representing a 120 | different aspect of the database overall. These five categories are: 121 | 122 | 1. Server 123 | 2. Databases 124 | 3. Documents 125 | 4. Design documents 126 | 5. Local documents 127 | 128 | ##### 1: Server 129 | 130 | Represented by the `Server` class. This class provides server level interaction 131 | with CouchDB, such as managing replication or obtaining basic information about 132 | the server. It also includes info about authentication and current user 133 | (methods in `CouchDbClient` class). 134 | 135 | The `Server` class methods return a `Future`. 136 | 137 | ##### 2: Databases 138 | 139 | A Database in CouchDB is a single document store located on the given database 140 | server. This part of the API is represented by the `Databases` class. You use 141 | this class for interacting with your data on a database level; for example 142 | creating a new database or preforming a query to search for certain documents. 143 | 144 | The `Databases` class methods return a `Future`. 145 | 146 | ##### 3: Documents 147 | 148 | You use the `Documents` class to interact with the data on a document level. 149 | This would include functions such as fetching a specific document, adding a new 150 | document, or attaching a file to a document. Note that this class does not 151 | model the documents themselves, but rather your interactions with them. 152 | The documents themselves are represented as `Map`s. 153 | 154 | The `Documents` class methods return a `Future`. 155 | 156 | ##### 4: Design Documents 157 | 158 | Design documents provide views of data in the database. 159 | You interact with them with the `DesignDocuments` class. 160 | 161 | The `DesignDocuments` class methods return a `Future`. 162 | 163 | ##### 5: Local Documents 164 | 165 | Local documents are no different than normal documents, with the exception that 166 | they are not copied to other instances of CouchDB during replication. 167 | You interact with them via the `LocalDocuments` class. 168 | 169 | The `LocalDocuments` class methods return a `Future`. 170 | 171 | ### CORS 172 | 173 | CORS is a method of enabling a web app to talk to a server other than the server 174 | hosting it. It is only necessary if the application is running in the browser. 175 | 176 | #### CouchDB Server Configuration for CORS 177 | 178 | If the application is not on the same origin with CouchDB instance (or you using 179 | different ports on server), then the remote CouchDB must be configured with 180 | the following options: 181 | 182 | [httpd] 183 | enable_cors = true 184 | [cors] 185 | origins = * 186 | credentials = true 187 | methods = GET, PUT, POST, HEAD, DELETE, COPY 188 | headers = accept, authorization, content-type, origin, referer, x-csrf-token 189 | 190 | Change these settings either in Fauxton configuration utility or in the CouchDb 191 | _local.ini_ file. For better security, specify specific domains instead 192 | of * in the `origins` section. 193 | 194 | #### Browser Client Configuration for CORS 195 | 196 | Depending on the browser, you might also need to pass `cors=true` to the 197 | `CouchDbClient` constructor. However, most of the time the browser will handle 198 | this for you and this shouldn't be necessary. 199 | In fact, it might cause an "Unsafe Header" message in the browser console. 200 | 201 | ## Usage 202 | 203 | A simple usage example: 204 | 205 | ```dart 206 | import 'package:couchdb/couchdb.dart'; 207 | 208 | Future main() async { 209 | final client = CouchDbClient(username: 'name', password: 'password'); 210 | final dbs = Databases(client); 211 | final docs = Documents(client); 212 | 213 | try { 214 | final DatabasesResponse response1 = await dbs.allDocs('some_db'); 215 | 216 | for (var i in response1.rows) { 217 | // Some code here 218 | } 219 | 220 | final DocumentsResponse response2 = await docs.doc('another_db', 'some_id'); 221 | 222 | var thing = response2.doc['some_attribute']; 223 | 224 | } on CouchDbException catch (e) { 225 | print('$e - error'); 226 | } 227 | } 228 | ``` 229 | 230 | ## Features and bugs 231 | 232 | Please file feature requests and bugs at the [issue tracker][tracker]. 233 | 234 | [tracker]: https://github.com/YevhenKap/couchdb_dart/issues 235 | 236 | **With ❤️ to CouchDB** 237 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.yaml 2 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Server 2 | 3 | ```dart 4 | import 'package:couchdb/couchdb.dart'; 5 | 6 | Future main() async { 7 | final client = CouchDbClient(username: 'name', password: 'password'); 8 | final db = Databases(client); 9 | final doc = Documents(client) 10 | 11 | try { 12 | final DatabasesResponse response1 = await db.allDocs('some_db'); 13 | 14 | for (var i in response1.rows) { 15 | // Some code here 16 | } 17 | 18 | final DocumentsResponse response2 = await doc.doc('another_db', 'some_id'); 19 | 20 | var thing = response2.doc['some_attribute']; 21 | 22 | } on CouchDbException catch (e) { 23 | print('$e - error'); 24 | } 25 | } 26 | ``` 27 | 28 | ## Browser 29 | 30 | ```dart 31 | import 'dart:html'; 32 | 33 | import 'package:couchdb/couchdb.dart'; 34 | 35 | Future main(List args) async { 36 | final ButtonElement btn = querySelector('#data'); 37 | final DivElement output = querySelector('#output'); 38 | 39 | final c = CouchDbClient(username: 'name', password: 'pass', cors: true); 40 | final dm = Documents(c); 41 | 42 | btn.onClick.listen((event) async { 43 | try { 44 | final DocumentsResponse response1 = await dm.doc('some_db', 'some_doc_id'); 45 | 46 | final Map doc = response1.doc; 47 | 48 | // Some code here 49 | 50 | // There properties are extracted from [doc] in order to gets direct access 51 | final String id = response1.id; 52 | final String rev = response1.rev; 53 | final Object attachment = response1.attachment; 54 | 55 | // Another code here 56 | 57 | } on CouchDbException catch (e) { 58 | window.console.log('${e.code} - error'); 59 | } 60 | }); 61 | } 62 | ``` 63 | -------------------------------------------------------------------------------- /lib/couchdb.dart: -------------------------------------------------------------------------------- 1 | /// A library for interacting with CouchDB via server 2 | /// applications or browser-based clients 3 | /// 4 | /// It is making according to the CouchDB API. 5 | /// > Requests are made using HTTP and requests are used 6 | /// to request information from the database, 7 | /// > store new data, and perform views and formatting of 8 | /// the information stored within the documents. 9 | /// 10 | /// More detailed information about API is [here](http://docs.couchdb.org/en/stable/index.html). 11 | library couchdb; 12 | 13 | export 'src/clients/couchdb_client.dart'; 14 | 15 | export 'src/exceptions/couchdb_exception.dart'; 16 | 17 | export 'src/interfaces/client_interface.dart'; 18 | export 'src/interfaces/databases_interface.dart'; 19 | export 'src/interfaces/design_documents_interface.dart'; 20 | export 'src/interfaces/documents_interface.dart'; 21 | export 'src/interfaces/local_documents_interface.dart'; 22 | export 'src/interfaces/server_interface.dart'; 23 | 24 | export 'src/responses/api_response.dart'; 25 | export 'src/responses/databases_response.dart'; 26 | export 'src/responses/design_documents_response.dart'; 27 | export 'src/responses/documents_response.dart'; 28 | export 'src/responses/local_documents_response.dart'; 29 | export 'src/entities/server_model_response.dart'; 30 | 31 | export 'src/databases.dart'; 32 | export 'src/design_documents.dart'; 33 | export 'src/documents.dart'; 34 | export 'src/local_documents.dart'; 35 | export 'src/server.dart'; 36 | 37 | @Deprecated('Use classes above instead of the ones below') 38 | export 'src/entities/db_response.dart'; 39 | export 'src/entities/database_model_response.dart'; 40 | export 'src/entities/design_document_model_response.dart'; 41 | export 'src/entities/document_model_response.dart'; 42 | export 'src/entities/local_document_model_response.dart'; 43 | export 'src/entities/server_model_response.dart'; 44 | 45 | export 'src/models/database_model.dart'; 46 | export 'src/models/design_document_model.dart'; 47 | export 'src/models/document_model.dart'; 48 | export 'src/models/local_document_model.dart'; 49 | export 'src/models/server_model.dart'; 50 | -------------------------------------------------------------------------------- /lib/src/clients/couchdb_client.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:crypto/crypto.dart'; 4 | import 'package:http/http.dart' as http; 5 | 6 | import '../interfaces/client_interface.dart'; 7 | import '../responses/api_response.dart'; 8 | import '../exceptions/couchdb_exception.dart'; 9 | 10 | /// Client for interacting with database via server-side and web applications 11 | class CouchDbClient implements ClientInterface { 12 | /// Creates instance of client with [username], [password], [host], [port], 13 | /// [cors], [auth], [scheme] of the connection and 14 | /// [secret] (needed for proxy authentication) parameters. 15 | /// Make sure that CouchDb application have `CORS` enabled. 16 | /// 17 | /// [auth] may be one of: 18 | /// 19 | /// - basic (default) 20 | /// - cookie 21 | /// - proxy 22 | /// 23 | /// [scheme] may be one of: 24 | /// 25 | /// - http 26 | /// - https (if `SSL` set to `true`) 27 | CouchDbClient( 28 | {String username, 29 | String password, 30 | String scheme = 'http', 31 | String host = '0.0.0.0', 32 | int port = 5984, 33 | this.auth = 'basic', 34 | this.cors = false, 35 | String secret, 36 | String path}) 37 | : secret = utf8.encode(secret != null ? secret : '') { 38 | if (username == null && password != null) { 39 | throw CouchDbException(401, 40 | response: ApiResponse({ 41 | 'error': 'Authorization failed', 42 | 'reason': 'You must provide username if password is non null!' 43 | }).errorResponse()); 44 | } else if (username != null && password == null) { 45 | throw CouchDbException(401, 46 | response: ApiResponse({ 47 | 'error': 'Authorization failed', 48 | 'reason': 'You must provide password if username is non null!' 49 | }).errorResponse()); 50 | } 51 | 52 | final userInfo = 53 | username == null && password == null ? null : '$username:$password'; 54 | 55 | final regExp = RegExp(r'http[s]?://'); 56 | if (host.startsWith(regExp)) { 57 | host = host.replaceFirst(regExp, ''); 58 | } 59 | _connectUri = Uri( 60 | scheme: scheme, host: host, port: port, userInfo: userInfo, path: path); 61 | } 62 | 63 | /// Create [CouchDbClient] instance from [uri] and 64 | /// [auth], [cors] and [secret] params. 65 | CouchDbClient.fromUri(Uri uri, 66 | {this.auth = 'basic', this.cors = false, String secret}) 67 | : secret = utf8.encode(secret != null ? secret : '') { 68 | final properUri = Uri( 69 | scheme: uri.scheme == '' ? 'http' : uri.scheme, 70 | userInfo: uri.userInfo, 71 | host: uri.host == '' ? '0.0.0.0' : uri.host, 72 | port: uri.port == 0 || uri.port == 80 || uri.port == 443 73 | ? 5984 74 | : uri.port); 75 | _connectUri = properUri; 76 | } 77 | 78 | /// Create [CouchDbClient] instance from [uri] and 79 | /// [auth], [cors] and [secret] params. 80 | CouchDbClient.fromString(String uri, 81 | {String auth = 'basic', bool cors = false, String secret}) 82 | : this.fromUri(Uri.tryParse(uri), auth: auth, cors: cors, secret: secret); 83 | 84 | /// Host of database instance 85 | String get host => _connectUri.host; 86 | 87 | /// Port database listened to 88 | int get port => _connectUri.port; 89 | 90 | /// Username of database user 91 | String get username => _connectUri.userInfo.isNotEmpty 92 | ? _connectUri.userInfo.split(':')[0] 93 | : _connectUri.userInfo; 94 | 95 | /// Password of database user 96 | String get password => _connectUri.userInfo.isNotEmpty 97 | ? _connectUri.userInfo.split(':')[1] 98 | : _connectUri.userInfo; 99 | 100 | /// Origin to be sent in CORS header 101 | String get origin => _connectUri.origin; 102 | 103 | /// Base64 encoded [username] and [password] 104 | String get authCredentials => username.isNotEmpty && password.isNotEmpty 105 | ? const Base64Encoder().convert(_connectUri.userInfo.codeUnits) 106 | : ''; 107 | 108 | /// Gets unmodifiable request headers of this client 109 | Map get headers => Map.unmodifiable(_headers); 110 | 111 | /// Authentication type used in requests 112 | /// 113 | /// May be one of: 114 | /// 115 | /// - basic 116 | /// - cookie 117 | /// - proxy 118 | /// 119 | String auth; 120 | 121 | /// Holds authentication cookies 122 | String _cookies; 123 | 124 | /// Tells if CORS is enabled 125 | final bool cors; 126 | 127 | /// Holds secret for proxy authentication 128 | final List secret; 129 | 130 | /// Web Client for requests 131 | final http.Client _httpClient = http.Client(); 132 | 133 | /// Request headers 134 | /// 135 | /// Already contains `Accept` and `Content-Type` headers defaults to `application/json`. 136 | final Map _headers = { 137 | 'Accept': 'application/json', 138 | 'Content-Type': 'application/json' 139 | }; 140 | 141 | /// Store connection info about coonection like **scheme**, 142 | /// **host**, **port**, **userInfo** 143 | Uri _connectUri; 144 | 145 | /// Sets headers to [_headers] 146 | /// 147 | /// You can directly set your own headers as follows: 148 | /// ```dart 149 | /// final client = CouchDbWebClient(username: 'name', password: 'pass'); 150 | /// client.modifyRequestHeaders({ ... }) 151 | /// ``` 152 | /// or define it using methods [head], [get], [put], [post], 153 | /// [delete] and [copy]. 154 | void modifyRequestHeaders(Map reqHeaders) { 155 | // If [reqHeaders] is null addAll method takes empty Map 156 | _headers.addAll(reqHeaders ?? {}); 157 | 158 | switch (auth) { 159 | case 'cookie': 160 | if (_cookies != null) { 161 | _headers['Cookie'] = _cookies; 162 | } 163 | break; 164 | case 'proxy': 165 | _headers['X-Auth-CouchDB-UserName'] = username; 166 | if (secret != null) { 167 | final encodedUsername = utf8.encode(username); 168 | _headers['X-Auth-CouchDB-Token'] = 169 | Hmac(sha1, secret).convert(encodedUsername).toString(); 170 | } 171 | break; 172 | default: 173 | if (authCredentials.isNotEmpty) { 174 | _headers['Authorization'] = 'Basic $authCredentials'; 175 | } 176 | } 177 | if (cors) { 178 | _headers['Origin'] = origin; 179 | } 180 | } 181 | 182 | /// HEAD method 183 | Future head(String path, 184 | {Map reqHeaders}) async { 185 | modifyRequestHeaders(reqHeaders); 186 | 187 | final res = 188 | await _httpClient.head(Uri.parse('$origin/$path'), headers: headers); 189 | 190 | _checkForErrorStatusCode(res.statusCode); 191 | 192 | return ApiResponse(null, headers: res.headers); 193 | } 194 | 195 | /// GET method 196 | Future get(String path, {Map reqHeaders}) async { 197 | Map json; 198 | 199 | modifyRequestHeaders(reqHeaders); 200 | 201 | final uriString = path.isNotEmpty ? '$origin/$path' : '$origin'; 202 | final res = await _httpClient.get(Uri.parse(uriString), headers: headers); 203 | 204 | final bodyUTF8 = utf8.decode(res.bodyBytes); 205 | if (res.headers['content-type'] == 'application/json') { 206 | final resBody = jsonDecode(bodyUTF8); 207 | 208 | if (resBody is int) { 209 | json = {'limit': resBody}; 210 | } else if (resBody is List) { 211 | json = {'list': List.from(resBody)}; 212 | } else { 213 | json = Map.from(resBody); 214 | } 215 | } else { 216 | // When body isn't JSON-valid then ApiResponse try parse field from [json] 217 | // and if it is null - error is thrown 218 | json = {}; 219 | } 220 | 221 | _checkForErrorStatusCode(res.statusCode, 222 | body: bodyUTF8, headers: res.headers); 223 | 224 | return ApiResponse(json, raw: bodyUTF8, headers: res.headers); 225 | } 226 | 227 | /// PUT method 228 | Future put(String path, 229 | {Object body, Map reqHeaders}) async { 230 | modifyRequestHeaders(reqHeaders); 231 | 232 | Object encodedBody; 233 | if (body != null) { 234 | body is Map ? encodedBody = jsonEncode(body) : encodedBody = body; 235 | } 236 | 237 | final res = await _httpClient.put(Uri.parse('$origin/$path'), 238 | headers: headers, body: encodedBody); 239 | 240 | final bodyUTF8 = utf8.decode(res.bodyBytes); 241 | final resBody = jsonDecode(bodyUTF8); 242 | final json = Map.from(resBody); 243 | 244 | _checkForErrorStatusCode(res.statusCode, 245 | body: bodyUTF8, headers: res.headers); 246 | 247 | return ApiResponse(json, headers: res.headers); 248 | } 249 | 250 | /// POST method 251 | Future post(String path, 252 | {Object body, Map reqHeaders}) async { 253 | modifyRequestHeaders(reqHeaders); 254 | 255 | Object encodedBody; 256 | if (body != null) { 257 | body is Map ? encodedBody = jsonEncode(body) : encodedBody = body; 258 | } 259 | 260 | final res = await _httpClient.post(Uri.parse('$origin/$path'), 261 | headers: headers, body: encodedBody); 262 | 263 | final bodyUTF8 = utf8.decode(res.bodyBytes); 264 | final resBody = jsonDecode(bodyUTF8); 265 | 266 | Map json; 267 | if (resBody is List) { 268 | json = {'list': List.from(resBody)}; 269 | } else { 270 | json = Map.from(resBody); 271 | } 272 | 273 | _checkForErrorStatusCode(res.statusCode, 274 | body: bodyUTF8, headers: res.headers); 275 | 276 | return ApiResponse(json, headers: res.headers); 277 | } 278 | 279 | /// DELETE method 280 | Future delete(String path, 281 | {Map reqHeaders}) async { 282 | modifyRequestHeaders(reqHeaders); 283 | 284 | final res = 285 | await _httpClient.delete(Uri.parse('$origin/$path'), headers: headers); 286 | 287 | final bodyUTF8 = utf8.decode(res.bodyBytes); 288 | final resBody = jsonDecode(bodyUTF8); 289 | final json = Map.from(resBody); 290 | 291 | _checkForErrorStatusCode(res.statusCode, 292 | body: bodyUTF8, headers: res.headers); 293 | 294 | return ApiResponse(json, headers: res.headers); 295 | } 296 | 297 | /// COPY method 298 | Future copy(String path, 299 | {Map reqHeaders}) async { 300 | modifyRequestHeaders(reqHeaders); 301 | final request = http.Request('COPY', Uri.parse('$origin/$path')); 302 | request.headers.addAll(headers); 303 | 304 | final res = await _httpClient.send(request); 305 | 306 | final body = await res.stream.transform(utf8.decoder).join(); 307 | 308 | final resBody = jsonDecode(body); 309 | final json = Map.from(resBody); 310 | 311 | _checkForErrorStatusCode(res.statusCode, body: body, headers: res.headers); 312 | 313 | return ApiResponse(json, headers: res.headers); 314 | } 315 | 316 | /// Makes request with specific [method] and with long or 317 | /// continuous connection 318 | /// 319 | /// Returns undecoded response. 320 | Future> streamed(String method, String path, 321 | {Object body, Map reqHeaders}) async { 322 | modifyRequestHeaders(reqHeaders); 323 | 324 | final uriString = path.isNotEmpty ? '$origin/$path' : '$origin'; 325 | final request = http.Request(method, Uri.parse(uriString)); 326 | request.headers.addAll(headers); 327 | if (body != null && (method == 'post' || method == 'put')) { 328 | request.body = 329 | body is Map || body is List ? jsonEncode(body) : body.toString(); 330 | } 331 | final res = await _httpClient.send(request); 332 | 333 | final resStream = res.stream.asBroadcastStream().transform(utf8.decoder); 334 | _checkForErrorStatusCode(res.statusCode, 335 | body: await resStream.first, headers: res.headers); 336 | 337 | return resStream; 338 | } 339 | 340 | /// Checks if response is returned with status codes lower than 341 | /// `200` of greater than `202` 342 | /// 343 | /// Returns `CouchDbException` if status code is out of range `200-202`. 344 | void _checkForErrorStatusCode(int code, 345 | {String body, Map headers}) { 346 | if (code < 200 || code > 202) { 347 | throw CouchDbException(code, 348 | response: 349 | ApiResponse(jsonDecode(body), headers: headers).errorResponse()); 350 | } 351 | } 352 | 353 | /// Initiates new session for specified user credentials by 354 | /// providing `Cookie` value 355 | /// 356 | /// If [next] parameter was provided the response will trigger redirection 357 | /// to the specified location in case of successful authentication. 358 | /// 359 | /// Structured response is available in `ServerModelResponse`. 360 | /// 361 | /// Returns JSON like: 362 | /// ```json 363 | /// {'ok': true, 'name': 'root', 'roles': ['_admin']} 364 | /// ``` 365 | Future authenticate([String next]) async { 366 | ApiResponse res; 367 | final path = next != null ? '_session?next=$next' : '_session'; 368 | 369 | try { 370 | res = await post(path, 371 | body: {'name': username, 'password': password}); 372 | } on CouchDbException { 373 | rethrow; 374 | } 375 | _cookies = res.headers['set-cookie']; 376 | return res; 377 | } 378 | 379 | /// Closes user’s session by instructing the browser to clear the cookie 380 | /// 381 | /// Structured response is available in `ServerModelResponse`. 382 | /// 383 | /// Returns JSON like: 384 | /// ```json 385 | /// {'ok': true} 386 | /// ``` 387 | Future logout() async { 388 | ApiResponse res; 389 | 390 | try { 391 | res = await delete('_session'); 392 | } on CouchDbException { 393 | rethrow; 394 | } 395 | _cookies = null; 396 | return res; 397 | } 398 | 399 | /// Returns information about the authenticated user, including a 400 | /// User Context Object, the authentication method and database 401 | /// that were used, and a list of configured 402 | /// authentication handlers on the server 403 | /// 404 | /// Structured response is available in `ServerModelResponse`. 405 | /// 406 | /// Returns JSON like: 407 | /// ```json 408 | /// { 409 | /// 'info': { 410 | /// 'authenticated': 'cookie', 411 | /// 'authentication_db': '_users', 412 | /// 'authentication_handlers': [ 413 | /// 'cookie', 414 | /// 'default' 415 | /// ] 416 | /// }, 417 | /// 'ok': true, 418 | /// 'userCtx': { 419 | /// 'name': 'root', 420 | /// 'roles': [ 421 | /// '_admin' 422 | /// ] 423 | /// } 424 | /// } 425 | /// ``` 426 | Future userInfo({bool basic = false}) async { 427 | ApiResponse res; 428 | final prevAuth = auth; 429 | 430 | if (basic) { 431 | auth = 'basic'; 432 | } 433 | 434 | try { 435 | res = await get('_session'); 436 | } on CouchDbException { 437 | rethrow; 438 | } 439 | 440 | auth = prevAuth; 441 | return res; 442 | } 443 | } 444 | -------------------------------------------------------------------------------- /lib/src/databases.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import 'interfaces/client_interface.dart'; 6 | import 'responses/databases_response.dart'; 7 | import 'responses/api_response.dart'; 8 | import 'exceptions/couchdb_exception.dart'; 9 | import 'utils/includer_path.dart'; 10 | import 'interfaces/databases_interface.dart'; 11 | 12 | /// Class that implements methods for interacting with entire database 13 | /// in CouchDB 14 | class Databases implements DatabasesInterface { 15 | /// Instance of connected client 16 | final ClientInterface _client; 17 | 18 | /// Create Databases by accepting web-based or server-based client 19 | Databases(this._client); 20 | 21 | @override 22 | Future headDbInfo(String dbName) async { 23 | ApiResponse result; 24 | try { 25 | result = await _client.head(dbName); 26 | } on CouchDbException catch (e) { 27 | e.response = ApiResponse({ 28 | 'error': 'Not found', 29 | 'reason': 'Database doesn\'t exist.' 30 | }).errorResponse(); 31 | rethrow; 32 | } 33 | return DatabasesResponse.from(result); 34 | } 35 | 36 | @override 37 | Future query(String dbName, String dbDoc, String dbView, List keys) async { 38 | final body = >{'keys': keys}; 39 | var result = await _client.post('$dbName/_design/$dbDoc/_view/$dbView', body: body); 40 | return DatabasesResponse.from(result); 41 | } 42 | 43 | @override 44 | Future dbInfo(String dbName) async { 45 | var result = await _client.get(dbName); 46 | return DatabasesResponse.from(result); 47 | } 48 | 49 | @override 50 | Future createDb(String dbName, {int q = 8}) async { 51 | final regexp = RegExp(r'^[a-z][a-z0-9_$()+/-]*$'); 52 | 53 | if (!regexp.hasMatch(dbName)) { 54 | throw ArgumentError(r'''Incorrect db name! 55 | Name must be validating by this rules: 56 | - Name must begin with a lowercase letter (a-z) 57 | - Lowercase characters (a-z) 58 | - Digits (0-9) 59 | - Any of the characters _, $, (, ), +, -, and /.'''); 60 | } 61 | 62 | final path = '$dbName?q=$q'; 63 | ApiResponse result = await _client.put(path); 64 | return DatabasesResponse.from(result); 65 | } 66 | 67 | @override 68 | Future deleteDb(String dbName) async { 69 | ApiResponse result = await _client.delete(dbName); 70 | return DatabasesResponse.from(result); 71 | } 72 | 73 | @override 74 | Future createDocIn(String dbName, Map doc, 75 | {String batch, Map headers}) async { 76 | final path = '$dbName${includeNonNullParam('?batch', batch)}'; 77 | 78 | ApiResponse result = 79 | await _client.post(path, body: doc, reqHeaders: headers); 80 | return DatabasesResponse.from(result); 81 | } 82 | 83 | @override 84 | Future allDocs(String dbName, 85 | {bool conflicts = false, 86 | bool descending = false, 87 | Object endKey, 88 | String endKeyDocId, 89 | bool group = false, 90 | int groupLevel, 91 | bool includeDocs = false, 92 | bool attachments = false, 93 | bool altEncodingInfo = false, 94 | bool inclusiveEnd = true, 95 | Object key, 96 | List keys, 97 | int limit, 98 | bool reduce, 99 | int skip, 100 | bool sorted = true, 101 | bool stable = false, 102 | String stale, 103 | Object startKey, 104 | String startKeyDocId, 105 | String update, 106 | bool updateSeq = false}) async { 107 | 108 | ApiResponse result = await _client.get('$dbName/_all_docs' 109 | '?conflicts=$conflicts' 110 | '&descending=$descending' 111 | '&${includeNonNullJsonParam("endkey", endKey)}' 112 | '&${includeNonNullParam("endkey_docid", endKeyDocId)}' 113 | '&group=$group' 114 | '&${includeNonNullParam("group_level", groupLevel)}' 115 | '&include_docs=$includeDocs' 116 | '&attachments=$attachments' 117 | '&alt_encoding_info=$altEncodingInfo' 118 | '&inclusive_end=$inclusiveEnd' 119 | '&${includeNonNullJsonParam("key", key)}' 120 | '&${includeNonNullJsonParam("keys", keys)}' 121 | '&${includeNonNullParam("limit", limit)}' 122 | '&${includeNonNullParam("reduce", reduce)}' 123 | '&${includeNonNullParam("skip", skip)}' 124 | '&sorted=$sorted' 125 | '&stable=$stable' 126 | '&${includeNonNullParam("stale", stale)}' 127 | '&${includeNonNullJsonParam("startkey", startKey)}' 128 | '&${includeNonNullParam("startkey_docid", startKeyDocId)}' 129 | '&${includeNonNullParam("update", update)}' 130 | '&update_seq=$updateSeq'); 131 | return DatabasesResponse.from(result); 132 | } 133 | 134 | @override 135 | Future docsByKeys(String dbName, 136 | {List keys}) async { 137 | 138 | final body = >{'keys': keys}; 139 | 140 | ApiResponse result = keys == null 141 | ? await _client.post('$dbName/_all_docs') 142 | : await _client.post('$dbName/_all_docs', body: body); 143 | return DatabasesResponse.from(result); 144 | } 145 | 146 | @override 147 | Future allDesignDocs(String dbName, 148 | {bool conflicts = false, 149 | bool descending = false, 150 | String endKey, 151 | String endKeyDocId, 152 | bool includeDocs = false, 153 | bool inclusiveEnd = true, 154 | String key, 155 | String keys, 156 | int limit, 157 | int skip = 0, 158 | String startKey, 159 | String startKeyDocId, 160 | bool updateSeq = false}) async { 161 | final path = 162 | '$dbName/_design_docs?conflicts=$conflicts&descending=$descending&' 163 | '${includeNonNullParam('endkey', endKey)}&${includeNonNullParam('endkey_docid', endKeyDocId)}&' 164 | 'include_docs=$includeDocs&inclusive_end=$inclusiveEnd&${includeNonNullParam('key', key)}&' 165 | '${includeNonNullParam('keys', keys)}&${includeNonNullParam('limit', limit)}&' 166 | 'skip=$skip&${includeNonNullParam('startkey', startKey)}&${includeNonNullParam('startkey_docid', startKeyDocId)}&' 167 | 'update_seq=$updateSeq'; 168 | 169 | ApiResponse result = await _client.get(path); 170 | return DatabasesResponse.from(result); 171 | } 172 | 173 | @override 174 | Future designDocsByKeys( 175 | String dbName, List keys) async { 176 | final body = >{'keys': keys}; 177 | ApiResponse result = await _client.post('$dbName/_design_docs', body: body); 178 | return DatabasesResponse.from(result); 179 | } 180 | 181 | @override 182 | Future queriesDocsFrom( 183 | String dbName, List> queries) async { 184 | final body = >>{'queries': queries}; 185 | ApiResponse result = 186 | await _client.post('$dbName/_all_docs/queries', body: body); 187 | return DatabasesResponse.from(result); 188 | } 189 | 190 | @override 191 | Future bulkDocs(String dbName, List docs, 192 | {@required bool revs}) async { 193 | final body = >{'docs': docs}; 194 | ApiResponse result = await _client.post('$dbName?revs=$revs', body: body); 195 | return DatabasesResponse.from(result); 196 | } 197 | 198 | @override 199 | Future insertBulkDocs(String dbName, List docs, 200 | {bool newEdits = true, Map headers}) async { 201 | final body = {'docs': docs, 'new_edits': newEdits}; 202 | ApiResponse result = await _client.post('$dbName/_bulk_docs', 203 | body: body, reqHeaders: headers); 204 | return DatabasesResponse.from(result); 205 | } 206 | 207 | @override 208 | Future find(String dbName, Map selector, 209 | {int limit = 25, 210 | int skip, 211 | List sort, 212 | List fields, 213 | Object useIndex, 214 | int r = 1, 215 | String bookmark, 216 | bool update = true, 217 | bool stable, 218 | String stale = 'false', 219 | bool executionStats = false}) async { 220 | 221 | final body = { 222 | 'selector': selector, 223 | 'limit': limit, 224 | 'r': r, 225 | 'bookmark': bookmark, 226 | 'update': update, 227 | 'stale': stale, 228 | 'execution_stats': executionStats 229 | }; 230 | if (skip != null) { 231 | body['skip'] = skip; 232 | } 233 | if (sort != null) { 234 | body['sort'] = sort; 235 | } 236 | if (fields != null) { 237 | body['fields'] = fields; 238 | } 239 | if (useIndex != null) { 240 | body['use_index'] = useIndex; 241 | } 242 | if (stable != null) { 243 | body['stable'] = stable; 244 | } 245 | 246 | ApiResponse result = await _client.post('$dbName/_find', body: body); 247 | return DatabasesResponse.from(result); 248 | } 249 | 250 | @override 251 | Future createIndexIn(String dbName, 252 | {@required List indexFields, 253 | String ddoc, 254 | String name, 255 | String type = 'json', 256 | Map partialFilterSelector}) async { 257 | 258 | final body = { 259 | 'index': >{'fields': indexFields}, 260 | 'type': type 261 | }; 262 | if (ddoc != null) { 263 | body['ddoc'] = ddoc; 264 | } 265 | if (name != null) { 266 | body['name'] = name; 267 | } 268 | if (partialFilterSelector != null) { 269 | body['partial_filter_selector'] = partialFilterSelector; 270 | } 271 | 272 | ApiResponse result = await _client.post('$dbName/_index', body: body); 273 | return DatabasesResponse.from(result); 274 | } 275 | 276 | @override 277 | Future indexesAt(String dbName) async { 278 | ApiResponse result = await _client.get('$dbName/_index'); 279 | return DatabasesResponse.from(result); 280 | } 281 | 282 | @override 283 | Future deleteIndexIn( 284 | String dbName, String designDoc, String name) async { 285 | ApiResponse result = 286 | await _client.delete('$dbName/_index/$designDoc/json/$name'); 287 | return DatabasesResponse.from(result); 288 | } 289 | 290 | @override 291 | Future explain(String dbName, Map selector, 292 | {int limit = 25, 293 | int skip, 294 | List sort, 295 | List fields, 296 | Object useIndex, 297 | int r = 1, 298 | String bookmark, 299 | bool update = true, 300 | bool stable, 301 | String stale = 'false', 302 | bool executionStats = false}) async { 303 | 304 | final body = { 305 | 'selector': selector, 306 | 'limit': limit, 307 | 'r': r, 308 | 'bookmark': bookmark, 309 | 'update': update, 310 | 'stale': stale, 311 | 'execution_stats': executionStats 312 | }; 313 | if (skip != null) { 314 | body['skip'] = skip; 315 | } 316 | if (sort != null) { 317 | body['sort'] = sort; 318 | } 319 | if (fields != null) { 320 | body['fields'] = fields; 321 | } 322 | if (useIndex != null) { 323 | body['use_index'] = useIndex; 324 | } 325 | if (stable != null) { 326 | body['stable'] = stable; 327 | } 328 | 329 | ApiResponse result = await _client.post('$dbName/_explain', body: body); 330 | return DatabasesResponse.from(result); 331 | } 332 | 333 | @override 334 | Future shards(String dbName) async { 335 | ApiResponse result = await _client.get('$dbName/_shards'); 336 | return DatabasesResponse.from(result); 337 | } 338 | 339 | @override 340 | Future shard(String dbName, String docId) async { 341 | ApiResponse result = await _client.get('$dbName/_shards/$docId'); 342 | return DatabasesResponse.from(result); 343 | } 344 | 345 | @override 346 | Future synchronizeShards(String dbName) async { 347 | ApiResponse result = await _client.post('$dbName/_sync_shards'); 348 | return DatabasesResponse.from(result); 349 | } 350 | 351 | @override 352 | Future> changesIn(String dbName, 353 | {List docIds, 354 | bool conflicts = false, 355 | bool descending = false, 356 | String feed = 'normal', 357 | String filter, 358 | int heartbeat = 60000, 359 | bool includeDocs = false, 360 | bool attachments = false, 361 | bool attEncodingInfo = false, 362 | int lastEventId, 363 | int limit, 364 | String since = '0', 365 | String style = 'main_only', 366 | int timeout = 60000, 367 | String view, 368 | int seqInterval}) async { 369 | final path = 370 | '$dbName/_changes?${includeNonNullParam('doc_ids', docIds)}&conflicts=$conflicts&' 371 | 'descending=$descending&feed=$feed&${includeNonNullParam('filter', filter)}&heartbeat=$heartbeat&' 372 | 'include_docs=$includeDocs&attachments=$attachments&att_encoding_info=$attEncodingInfo&' 373 | '${includeNonNullParam('last-event-id', lastEventId)}&${includeNonNullParam('limit', limit)}&' 374 | 'since=$since&style=$style&timeout=$timeout&${includeNonNullParam('view', view)}&' 375 | '${includeNonNullParam('seq_interval', seqInterval)}'; 376 | 377 | final streamedRes = await _client.streamed('get', path); 378 | 379 | switch (feed) { 380 | case 'longpoll': 381 | var strRes = await streamedRes.join(); 382 | strRes = '{"result": [$strRes'; 383 | return Stream.fromFuture( 384 | Future.value( 385 | DatabasesResponse.from(ApiResponse(jsonDecode(strRes))))); 386 | 387 | case 'continuous': 388 | final mappedRes = streamedRes.map((v) => v.replaceAll('}\n{', '},\n{')); 389 | return mappedRes.map((v) => DatabasesResponse.from( 390 | ApiResponse(jsonDecode('{"result": [$v]}')))); 391 | 392 | case 'eventsource': 393 | final mappedRes = streamedRes 394 | .map((v) => v.replaceAll(RegExp('\n+data'), '},\n{data')) 395 | .map((v) => v.replaceAll('data', '"data"')) 396 | .map((v) => v.replaceAll('\nid', ',\n"id"')); 397 | return mappedRes.map((v) => DatabasesResponse.from( 398 | ApiResponse(jsonDecode('{"result": [{$v}]}')))); 399 | 400 | default: 401 | var strRes = await streamedRes.join(); 402 | strRes = '{"result": [$strRes'; 403 | return Stream.fromFuture( 404 | Future.value( 405 | DatabasesResponse.from(ApiResponse(jsonDecode(strRes))))); 406 | } 407 | } 408 | 409 | @override 410 | Future> postChangesIn(String dbName, 411 | {List docIds, 412 | bool conflicts = false, 413 | bool descending = false, 414 | String feed = 'normal', 415 | String filter = '_doc_ids', 416 | int heartbeat = 60000, 417 | bool includeDocs = false, 418 | bool attachments = false, 419 | bool attEncodingInfo = false, 420 | int lastEventId, 421 | int limit, 422 | String since = '0', 423 | String style = 'main_only', 424 | int timeout = 60000, 425 | String view, 426 | int seqInterval}) async { 427 | final path = '$dbName/_changes?conflicts=$conflicts&' 428 | 'descending=$descending&feed=$feed&filter=$filter&heartbeat=$heartbeat&' 429 | 'include_docs=$includeDocs&attachments=$attachments&att_encoding_info=$attEncodingInfo&' 430 | '${includeNonNullParam('last-event-id', lastEventId)}&${includeNonNullParam('limit', limit)}&' 431 | 'since=$since&style=$style&timeout=$timeout&${includeNonNullParam('view', view)}&' 432 | '${includeNonNullParam('seq_interval', seqInterval)}'; 433 | 434 | final body = >{'doc_ids': docIds}; 435 | 436 | final streamedRes = await _client.streamed('post', path, body: body); 437 | 438 | switch (feed) { 439 | case 'longpoll': 440 | var strRes = await streamedRes.join(); 441 | strRes = '{"result": [$strRes'; 442 | return Stream.fromFuture( 443 | Future.value( 444 | DatabasesResponse.from(ApiResponse(jsonDecode(strRes))))); 445 | 446 | case 'continuous': 447 | final mappedRes = streamedRes.map((v) => v.replaceAll('}\n{', '},\n{')); 448 | return mappedRes.map((v) => DatabasesResponse.from( 449 | ApiResponse(jsonDecode('{"result": [$v]}')))); 450 | 451 | case 'eventsource': 452 | final mappedRes = streamedRes 453 | .map((v) => v.replaceAll(RegExp('\n+data'), '},\n{data')) 454 | .map((v) => v.replaceAll('data', '"data"')) 455 | .map((v) => v.replaceAll('\nid', ',\n"id"')); 456 | return mappedRes.map((v) => DatabasesResponse.from( 457 | ApiResponse(jsonDecode('{"result": [{$v}]}')))); 458 | 459 | default: 460 | var strRes = await streamedRes.join(); 461 | strRes = '{"result": [$strRes'; 462 | return Stream.fromFuture( 463 | Future.value( 464 | DatabasesResponse.from(ApiResponse(jsonDecode(strRes))))); 465 | } 466 | } 467 | 468 | @override 469 | Future compact(String dbName) async { 470 | ApiResponse result = await _client.post('$dbName/_compact'); 471 | return DatabasesResponse.from(result); 472 | } 473 | 474 | @override 475 | Future compactViewIndexesWith( 476 | String dbName, String ddocName) async { 477 | ApiResponse result = await _client.post('$dbName/_compact/$ddocName'); 478 | return DatabasesResponse.from(result); 479 | } 480 | 481 | @override 482 | Future ensureFullCommit(String dbName) async { 483 | ApiResponse result = await _client.post('$dbName/_ensure_full_commit'); 484 | return DatabasesResponse.from(result); 485 | } 486 | 487 | @override 488 | Future viewCleanup(String dbName) async { 489 | ApiResponse result = await _client.post('$dbName/_view_cleanup'); 490 | return DatabasesResponse.from(result); 491 | } 492 | 493 | @override 494 | Future securityOf(String dbName) async { 495 | ApiResponse result = await _client.get('$dbName/_security'); 496 | return DatabasesResponse.from(result); 497 | } 498 | 499 | @override 500 | Future setSecurityFor( 501 | String dbName, Map>> security) async { 502 | ApiResponse result = await _client.put('$dbName/_security', body: security); 503 | return DatabasesResponse.from(result); 504 | } 505 | 506 | @override 507 | Future purge( 508 | String dbName, Map> docs) async { 509 | ApiResponse result = await _client.post('$dbName/_purge', body: docs); 510 | return DatabasesResponse.from(result); 511 | } 512 | 513 | @override 514 | Future purgedInfosLimit(String dbName) async { 515 | ApiResponse result = await _client.get('$dbName/_purged_infos_limit'); 516 | return DatabasesResponse.from(result); 517 | } 518 | 519 | @override 520 | Future setPurgedInfosLimit( 521 | String dbName, int limit) async { 522 | ApiResponse result = 523 | await _client.put('$dbName/_purged_infos_limit', body: limit); 524 | return DatabasesResponse.from(result); 525 | } 526 | 527 | @override 528 | Future missingRevs( 529 | String dbName, Map> revs) async { 530 | ApiResponse result = 531 | await _client.post('$dbName/_missing_revs', body: revs); 532 | return DatabasesResponse.from(result); 533 | } 534 | 535 | @override 536 | Future revsDiff( 537 | String dbName, Map> revs) async { 538 | ApiResponse result = await _client.post('$dbName/_revs_diff', body: revs); 539 | return DatabasesResponse.from(result); 540 | } 541 | 542 | @override 543 | Future revsLimitOf(String dbName) async { 544 | ApiResponse result = await _client.get('$dbName/_revs_limit'); 545 | return DatabasesResponse.from(result); 546 | } 547 | 548 | /// Sets the maximum number of document revisions that will be tracked by CouchDB, 549 | /// even after compaction has occurred 550 | @override 551 | Future setRevsLimit(String dbName, int limit) async { 552 | ApiResponse result = await _client.put('$dbName/_revs_limit', body: limit); 553 | return DatabasesResponse.from(result); 554 | } 555 | } 556 | -------------------------------------------------------------------------------- /lib/src/design_documents.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import 'interfaces/client_interface.dart'; 4 | import 'interfaces/design_documents_interface.dart'; 5 | import 'responses/api_response.dart'; 6 | import 'responses/design_documents_response.dart'; 7 | import 'utils/includer_path.dart'; 8 | 9 | /// Class that contains methods that allow operate with design documents 10 | class DesignDocuments implements DesignDocumentsInterface { 11 | /// Instance of connected client 12 | final ClientInterface _client; 13 | 14 | /// Create DesignDocument by accepting web-based or server-based client 15 | DesignDocuments(this._client); 16 | 17 | @override 18 | Future designDocHeaders(String dbName, String ddocId, 19 | {Map headers, 20 | bool attachments = false, 21 | bool attEncodingInfo = false, 22 | List attsSince, 23 | bool conflicts = false, 24 | bool deletedConflicts = false, 25 | bool latest = false, 26 | bool localSeq = false, 27 | bool meta = false, 28 | Object openRevs, 29 | String rev, 30 | bool revs = false, 31 | bool revsInfo = false}) async { 32 | final path = 33 | '$dbName/$ddocId?attachments=$attachments&att_encoding_info=$attEncodingInfo&' 34 | '${includeNonNullParam('atts_since', attsSince)}&conflicts=$conflicts&deleted_conflicts=$deletedConflicts&' 35 | 'latest=$latest&local_seq=$localSeq&meta=$meta&${includeNonNullParam('open_revs', openRevs)}&' 36 | '${includeNonNullParam('rev', rev)}&revs=$revs&revs_info=$revsInfo'; 37 | 38 | ApiResponse result = await _client.head(path, reqHeaders: headers); 39 | return DesignDocumentsResponse.from(result); 40 | } 41 | 42 | @override 43 | Future designDoc(String dbName, String ddocId, 44 | {Map headers, 45 | bool attachments = false, 46 | bool attEncodingInfo = false, 47 | List attsSince, 48 | bool conflicts = false, 49 | bool deletedConflicts = false, 50 | bool latest = false, 51 | bool localSeq = false, 52 | bool meta = false, 53 | Object openRevs, 54 | String rev, 55 | bool revs = false, 56 | bool revsInfo = false}) async { 57 | final path = 58 | '$dbName/$ddocId?attachments=$attachments&att_encoding_info=$attEncodingInfo&' 59 | '${includeNonNullParam('atts_since', attsSince)}&conflicts=$conflicts&deleted_conflicts=$deletedConflicts&' 60 | 'latest=$latest&local_seq=$localSeq&meta=$meta&${includeNonNullParam('open_revs', openRevs)}&' 61 | '${includeNonNullParam('rev', rev)}&revs=$revs&revs_info=$revsInfo'; 62 | 63 | ApiResponse result = await _client.get(path, reqHeaders: headers); 64 | return DesignDocumentsResponse.from(result); 65 | } 66 | 67 | @override 68 | Future insertDesignDoc( 69 | String dbName, String ddocId, Map body, 70 | {Map headers, 71 | String rev, 72 | String batch, 73 | bool newEdits = true}) async { 74 | final path = 75 | '$dbName/$ddocId?new_edits=$newEdits&${includeNonNullParam('rev', rev)}&' 76 | '${includeNonNullParam('batch', batch)}'; 77 | 78 | ApiResponse result = 79 | await _client.put(path, reqHeaders: headers, body: body); 80 | return DesignDocumentsResponse.from(result); 81 | } 82 | 83 | @override 84 | Future deleteDesignDoc( 85 | String dbName, String ddocId, String rev, 86 | {Map headers, String batch}) async { 87 | final path = 88 | '$dbName/$ddocId?rev=$rev&${includeNonNullParam('batch', batch)}'; 89 | 90 | ApiResponse result = await _client.delete(path, reqHeaders: headers); 91 | return DesignDocumentsResponse.from(result); 92 | } 93 | 94 | @override 95 | Future copyDesignDoc(String dbName, String ddocId, 96 | {Map headers, String rev, String batch}) async { 97 | final path = '$dbName/$ddocId?${includeNonNullParam('rev', rev)}&' 98 | '${includeNonNullParam('batch', batch)}'; 99 | 100 | ApiResponse result = await _client.copy(path, reqHeaders: headers); 101 | return DesignDocumentsResponse.from(result); 102 | } 103 | 104 | @override 105 | Future attachmentInfo( 106 | String dbName, String ddocId, String attName, 107 | {Map headers, String rev}) async { 108 | final path = '$dbName/$ddocId/$attName?${includeNonNullParam('rev', rev)}'; 109 | 110 | ApiResponse result = await _client.head(path, reqHeaders: headers); 111 | return DesignDocumentsResponse.from(result); 112 | } 113 | 114 | @override 115 | Future attachment( 116 | String dbName, String ddocId, String attName, 117 | {Map headers, String rev}) async { 118 | final path = '$dbName/$ddocId/$attName?${includeNonNullParam('rev', rev)}'; 119 | 120 | ApiResponse result = await _client.get(path, reqHeaders: headers); 121 | return DesignDocumentsResponse.from(result); 122 | } 123 | 124 | @override 125 | Future uploadAttachment( 126 | String dbName, String ddocId, String attName, Object body, 127 | {Map headers, String rev}) async { 128 | final path = '$dbName/$ddocId/$attName?${includeNonNullParam('rev', rev)}'; 129 | 130 | ApiResponse result = 131 | await _client.put(path, reqHeaders: headers, body: body); 132 | return DesignDocumentsResponse.from(result); 133 | } 134 | 135 | @override 136 | Future deleteAttachment( 137 | String dbName, String ddocId, String attName, 138 | {@required String rev, Map headers, String batch}) async { 139 | final path = '$dbName/$ddocId/$attName?rev=$rev&' 140 | '${includeNonNullParam('batch', batch)}'; 141 | 142 | ApiResponse result = await _client.delete(path, reqHeaders: headers); 143 | return DesignDocumentsResponse.from(result); 144 | } 145 | 146 | @override 147 | Future designDocInfo(String dbName, String ddocId, 148 | {Map headers}) async { 149 | final path = '$dbName/$ddocId/_info'; 150 | 151 | ApiResponse result = await _client.get(path, reqHeaders: headers); 152 | return DesignDocumentsResponse.from(result); 153 | } 154 | 155 | @override 156 | Future executeViewFunction( 157 | String dbName, String ddocId, String viewName, 158 | {bool conflicts = false, 159 | bool descending = false, 160 | Object endKey, 161 | String endKeyDocId, 162 | bool group = false, 163 | int groupLevel, 164 | bool includeDocs = false, 165 | bool attachments = false, 166 | bool attEncodingInfo = false, 167 | bool inclusiveEnd = true, 168 | Object key, 169 | List keys, 170 | int limit, 171 | bool reduce = false, // for solving conflict with [conflicts] 172 | int skip = 0, 173 | bool sorted = true, 174 | bool stable = false, 175 | String stale, 176 | Object startKey, 177 | String startKeyDocId, 178 | String update = 'true', 179 | bool updateSeq = false, 180 | Map headers}) async { 181 | String path; 182 | 183 | if (reduce == true) { 184 | path = '$dbName/$ddocId/_view/$viewName?&descending=$descending&' 185 | '${includeNonNullParam('endkey', endKey)}&${includeNonNullParam('endkey_docid', endKeyDocId)}&' 186 | 'group=$group&${includeNonNullParam('group_level', groupLevel)}&include_docs=$includeDocs&' 187 | 'attachments=$attachments&att_encoding_info=$attEncodingInfo&inclusive_end=$inclusiveEnd&' 188 | '${includeNonNullParam('key', key)}&${includeNonNullParam('keys', keys)}&' 189 | '${includeNonNullParam('limit', limit)}&${includeNonNullParam('reduce', reduce)}&' 190 | 'skip=$skip&sorted=$sorted&stable=$stable&${includeNonNullParam('stale', stale)}&' 191 | '${includeNonNullParam('startkey', startKey)}&${includeNonNullParam('startkey_docid', startKeyDocId)}&' 192 | 'update=$update&update_seq=$updateSeq'; 193 | } else { 194 | path = 195 | '$dbName/$ddocId/_view/$viewName?conflicts=$conflicts&descending=$descending&' 196 | '${includeNonNullParam('endkey', endKey)}&${includeNonNullParam('endkey_docid', endKeyDocId)}&' 197 | 'group=$group&${includeNonNullParam('group_level', groupLevel)}&include_docs=$includeDocs&' 198 | 'attachments=$attachments&att_encoding_info=$attEncodingInfo&inclusive_end=$inclusiveEnd&' 199 | '${includeNonNullParam('key', key)}&${includeNonNullParam('keys', keys)}&' 200 | '${includeNonNullParam('limit', limit)}&${includeNonNullParam('reduce', reduce)}&' 201 | 'skip=$skip&sorted=$sorted&stable=$stable&${includeNonNullParam('stale', stale)}&' 202 | '${includeNonNullParam('startkey', startKey)}&${includeNonNullParam('startkey_docid', startKeyDocId)}&' 203 | 'update=$update&update_seq=$updateSeq'; 204 | } 205 | 206 | ApiResponse result = await _client.get(path, reqHeaders: headers); 207 | return DesignDocumentsResponse.from(result); 208 | } 209 | 210 | @override 211 | Future executeViewFunctionWithKeys( 212 | String dbName, String ddocId, String viewName, 213 | {@required List keys, 214 | bool conflicts = false, 215 | bool descending = false, 216 | Object endKey, 217 | String endKeyDocId, 218 | bool group = false, 219 | int groupLevel, 220 | bool includeDocs = false, 221 | bool attachments = false, 222 | bool attEncodingInfo = false, 223 | bool inclusiveEnd = true, 224 | Object key, 225 | int limit, 226 | bool reduce = false, // Reason is the same as above 227 | int skip = 0, 228 | bool sorted = true, 229 | bool stable = false, 230 | String stale, 231 | Object startKey, 232 | String startKeyDocId, 233 | String update = 'true', 234 | bool updateSeq = false, 235 | Map headers}) async { 236 | String path; 237 | 238 | if (reduce == true) { 239 | path = '$dbName/$ddocId/_view/$viewName?&descending=$descending&' 240 | '${includeNonNullParam('endkey', endKey)}&${includeNonNullParam('endkey_docid', endKeyDocId)}&' 241 | 'group=$group&${includeNonNullParam('group_level', groupLevel)}&include_docs=$includeDocs&' 242 | 'attachments=$attachments&att_encoding_info=$attEncodingInfo&inclusive_end=$inclusiveEnd&' 243 | '${includeNonNullParam('key', key)}&' 244 | '${includeNonNullParam('limit', limit)}&${includeNonNullParam('reduce', reduce)}&' 245 | 'skip=$skip&sorted=$sorted&stable=$stable&${includeNonNullParam('stale', stale)}&' 246 | '${includeNonNullParam('startkey', startKey)}&${includeNonNullParam('startkey_docid', startKeyDocId)}&' 247 | 'update=$update&update_seq=$updateSeq'; 248 | } else { 249 | path = 250 | '$dbName/$ddocId/_view/$viewName?conflicts=$conflicts&descending=$descending&' 251 | '${includeNonNullParam('endkey', endKey)}&${includeNonNullParam('endkey_docid', endKeyDocId)}&' 252 | 'group=$group&${includeNonNullParam('group_level', groupLevel)}&include_docs=$includeDocs&' 253 | 'attachments=$attachments&att_encoding_info=$attEncodingInfo&inclusive_end=$inclusiveEnd&' 254 | '${includeNonNullParam('key', key)}&' 255 | '${includeNonNullParam('limit', limit)}&${includeNonNullParam('reduce', reduce)}&' 256 | 'skip=$skip&sorted=$sorted&stable=$stable&${includeNonNullParam('stale', stale)}&' 257 | '${includeNonNullParam('startkey', startKey)}&${includeNonNullParam('startkey_docid', startKeyDocId)}&' 258 | 'update=$update&update_seq=$updateSeq'; 259 | } 260 | 261 | final body = >{'keys': keys}; 262 | 263 | ApiResponse result = 264 | await _client.post(path, reqHeaders: headers, body: body); 265 | return DesignDocumentsResponse.from(result); 266 | } 267 | 268 | @override 269 | Future executeViewQueries(String dbName, 270 | String ddocId, String viewName, List queries) async { 271 | final body = >{'queries': queries}; 272 | 273 | ApiResponse result = await _client 274 | .post('$dbName/$ddocId/_view/$viewName/queries', body: body); 275 | return DesignDocumentsResponse.from(result); 276 | } 277 | 278 | @override 279 | Future executeShowFunctionForNull( 280 | String dbName, String ddocId, String funcName, 281 | {String format}) async { 282 | ApiResponse result = await _client.get( 283 | '$dbName/$ddocId/_show/$funcName?${includeNonNullParam('format', format)}'); 284 | return DesignDocumentsResponse.from(result); 285 | } 286 | 287 | @override 288 | Future executeShowFunctionForDocument( 289 | String dbName, String ddocId, String funcName, String docId, 290 | {String format}) async { 291 | ApiResponse result = await _client.get( 292 | '$dbName/$ddocId/_show/$funcName/$docId?${includeNonNullParam('format', format)}'); 293 | return DesignDocumentsResponse.from(result); 294 | } 295 | 296 | @override 297 | Future executeListFunctionForView( 298 | String dbName, String ddocId, String funcName, String view, 299 | {String format}) async { 300 | ApiResponse result = await _client.get( 301 | '$dbName/$ddocId/_list/$funcName/$view?${includeNonNullParam('format', format)}'); 302 | return DesignDocumentsResponse.from(result); 303 | } 304 | 305 | @override 306 | Future executeListFunctionForViewFromDoc( 307 | String dbName, 308 | String ddocId, 309 | String funcName, 310 | String otherDoc, 311 | String view, 312 | {String format}) async { 313 | ApiResponse result = await _client.get( 314 | '$dbName/$ddocId/_list/$funcName/$otherDoc/$view?${includeNonNullParam('format', format)}'); 315 | return DesignDocumentsResponse.from(result); 316 | } 317 | 318 | @override 319 | Future executeUpdateFunctionForNull( 320 | String dbName, String ddocId, String funcName, Object body) async { 321 | ApiResponse result = 322 | await _client.post('$dbName/$ddocId/_update/$funcName', body: body); 323 | return DesignDocumentsResponse.from(result); 324 | } 325 | 326 | @override 327 | Future executeUpdateFunctionForDocument( 328 | String dbName, 329 | String ddocId, 330 | String funcName, 331 | String docId, 332 | Object body) async { 333 | ApiResponse result = await _client 334 | .put('$dbName/$ddocId/_update/$funcName/$docId', body: body); 335 | return DesignDocumentsResponse.from(result); 336 | } 337 | 338 | @override 339 | Future rewritePath( 340 | String dbName, 341 | String ddocId, 342 | String path, 343 | ) async { 344 | ApiResponse result = await _client.put('$dbName/$ddocId/_rewrite/$path'); 345 | return DesignDocumentsResponse.from(result); 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /lib/src/documents.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import 'interfaces/client_interface.dart'; 4 | import 'interfaces/documents_interface.dart'; 5 | import 'responses/api_response.dart'; 6 | import 'responses/documents_response.dart'; 7 | import 'utils/includer_path.dart'; 8 | 9 | /// Class that implements methods for create, read, update and delete documents within a database 10 | class Documents implements DocumentsInterface { 11 | /// Instance of connected client 12 | final ClientInterface _client; 13 | 14 | /// Create Documents by accepting web-based or server-based client 15 | Documents(this._client); 16 | 17 | @override 18 | Future docInfo(String dbName, String docId, 19 | {Map headers, 20 | bool attachments = false, 21 | bool attEncodingInfo = false, 22 | List attsSince, 23 | bool conflicts = false, 24 | bool deletedConflicts = false, 25 | bool latest = false, 26 | bool localSeq = false, 27 | bool meta = false, 28 | Object openRevs, 29 | String rev, 30 | bool revs = false, 31 | bool revsInfo = false}) async { 32 | final path = 33 | '$dbName/$docId?attachments=$attachments&att_encoding_info=$attEncodingInfo&' 34 | '${includeNonNullParam('atts_since', attsSince)}&conflicts=$conflicts&deleted_conflicts=$deletedConflicts&' 35 | 'latest=$latest&local_seq=$localSeq&meta=$meta&${includeNonNullParam('open_revs', openRevs)}&' 36 | '${includeNonNullParam('rev', rev)}&revs=$revs&revs_info=$revsInfo'; 37 | 38 | ApiResponse result = await _client.head(path, reqHeaders: headers); 39 | return DocumentsResponse.from(result); 40 | } 41 | 42 | @override 43 | Future doc(String dbName, String docId, 44 | {Map headers, 45 | bool attachments = false, 46 | bool attEncodingInfo = false, 47 | List attsSince, 48 | bool conflicts = false, 49 | bool deletedConflicts = false, 50 | bool latest = false, 51 | bool localSeq = false, 52 | bool meta = false, 53 | Object openRevs, 54 | String rev, 55 | bool revs = false, 56 | bool revsInfo = false}) async { 57 | final path = 58 | '$dbName/$docId?attachments=$attachments&att_encoding_info=$attEncodingInfo&' 59 | '${includeNonNullParam('atts_since', attsSince)}&conflicts=$conflicts&deleted_conflicts=$deletedConflicts&' 60 | 'latest=$latest&local_seq=$localSeq&meta=$meta&${includeNonNullParam('open_revs', openRevs)}&' 61 | '${includeNonNullParam('rev', rev)}&revs=$revs&revs_info=$revsInfo'; 62 | 63 | ApiResponse result = await _client.get(path, reqHeaders: headers); 64 | return DocumentsResponse.from(result); 65 | } 66 | 67 | @override 68 | Future insertDoc( 69 | String dbName, String docId, Map body, 70 | {Map headers, 71 | String rev, 72 | String batch, 73 | bool newEdits = true}) async { 74 | final path = 75 | '$dbName/$docId?new_edits=$newEdits&${includeNonNullParam('rev', rev)}&' 76 | '${includeNonNullParam('batch', batch)}'; 77 | 78 | ApiResponse result = 79 | await _client.put(path, reqHeaders: headers, body: body); 80 | return DocumentsResponse.from(result); 81 | } 82 | 83 | @override 84 | Future deleteDoc(String dbName, String docId, String rev, 85 | {Map headers, String batch}) async { 86 | final path = 87 | '$dbName/$docId?rev=$rev&${includeNonNullParam('batch', batch)}'; 88 | 89 | ApiResponse result = await _client.delete(path, reqHeaders: headers); 90 | return DocumentsResponse.from(result); 91 | } 92 | 93 | @override 94 | Future copyDoc( 95 | String dbName, String docId, String destinationId, 96 | {Map headers, 97 | String rev, 98 | String destinationRev, 99 | String batch}) async { 100 | 101 | final path = '$dbName/$docId?${includeNonNullParam('rev', rev)}&' 102 | '${includeNonNullParam('batch', batch)}'; 103 | 104 | final destination = destinationRev == null 105 | ? destinationId 106 | : '$destinationId?rev=$destinationRev'; 107 | 108 | headers ??= {}; 109 | headers['Destination'] = destination; 110 | 111 | ApiResponse result = await _client.copy(path, reqHeaders: headers); 112 | return DocumentsResponse.from(result); 113 | } 114 | 115 | @override 116 | Future attachmentInfo( 117 | String dbName, String docId, String attName, 118 | {Map headers, String rev}) async { 119 | final path = '$dbName/$docId/$attName?${includeNonNullParam('rev', rev)}'; 120 | 121 | ApiResponse result = await _client.head(path, reqHeaders: headers); 122 | return DocumentsResponse.from(result); 123 | } 124 | 125 | @override 126 | Future attachment( 127 | String dbName, String docId, String attName, 128 | {Map headers, String rev}) async { 129 | final path = '$dbName/$docId/$attName?${includeNonNullParam('rev', rev)}'; 130 | 131 | ApiResponse result = await _client.get(path, reqHeaders: headers); 132 | return DocumentsResponse.from(result); 133 | } 134 | 135 | @override 136 | Future uploadAttachment( 137 | String dbName, String docId, String attName, Object body, 138 | {Map headers, String rev}) async { 139 | final path = '$dbName/$docId/$attName?${includeNonNullParam('rev', rev)}'; 140 | 141 | ApiResponse result = 142 | await _client.put(path, reqHeaders: headers, body: body); 143 | return DocumentsResponse.from(result); 144 | } 145 | 146 | @override 147 | Future deleteAttachment( 148 | String dbName, String docId, String attName, 149 | {@required String rev, Map headers, String batch}) async { 150 | final path = '$dbName/$docId/$attName?rev=$rev&' 151 | '${includeNonNullParam('batch', batch)}'; 152 | 153 | ApiResponse result = await _client.delete(path, reqHeaders: headers); 154 | return DocumentsResponse.from(result); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /lib/src/entities/database_model_response.dart: -------------------------------------------------------------------------------- 1 | import '../responses/api_response.dart'; 2 | import '../responses/databases_response.dart'; 3 | 4 | /// Only provided for backward compatibility 5 | @Deprecated('Use DatabasesResponse instead') 6 | class DatabaseModelResponse extends DatabasesResponse { 7 | DatabaseModelResponse.from(ApiResponse response) : super.from(response); 8 | 9 | DatabaseModelResponse( 10 | {Map cluster, 11 | bool compactRunning, 12 | String dbName, 13 | int diskFormatVersion, 14 | int docCount, 15 | int docDelCount, 16 | String purgeSeq, 17 | Map sizes, 18 | String updateSeq, 19 | bool ok, 20 | String id, 21 | String rev, 22 | int offset, 23 | List> rows, 24 | int totalRows, 25 | List> results, 26 | List> docs, 27 | String warning, 28 | Map executionStats, 29 | String bookmark, 30 | String result, 31 | String name, 32 | List> indexes, 33 | Map index, 34 | Map> selector, 35 | Map opts, 36 | int limit, 37 | int skip, 38 | List fields, 39 | Map> range, 40 | String lastSeq, 41 | int pending, 42 | Map> admins, 43 | Map> members, 44 | Map>> purged, 45 | Map> missedRevs, 46 | Map>> revsDiff, 47 | List> list, 48 | Map> shards, 49 | String shardRange, 50 | List nodes}) 51 | : super( 52 | cluster: cluster, 53 | compactRunning: compactRunning, 54 | dbName: dbName, 55 | diskFormatVersion: diskFormatVersion, 56 | docCount: docCount, 57 | docDelCount: docDelCount, 58 | purgeSeq: purgeSeq, 59 | sizes: sizes, 60 | updateSeq: updateSeq, 61 | ok: ok, 62 | id: id, 63 | rev: rev, 64 | offset: offset, 65 | rows: rows, 66 | totalRows: totalRows, 67 | results: results, 68 | docs: docs, 69 | warning: warning, 70 | executionStats: executionStats, 71 | bookmark: bookmark, 72 | result: result, 73 | name: name, 74 | indexes: indexes, 75 | index: index, 76 | selector: selector, 77 | opts: opts, 78 | limit: limit, 79 | skip: skip, 80 | fields: fields, 81 | range: range, 82 | lastSeq: lastSeq, 83 | pending: pending, 84 | admins: admins, 85 | members: members, 86 | purged: purged, 87 | missedRevs: missedRevs, 88 | revsDiff: revsDiff, 89 | list: list, 90 | shards: shards, 91 | shardRange: shardRange, 92 | nodes: nodes); 93 | } 94 | -------------------------------------------------------------------------------- /lib/src/entities/db_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:couchdb/couchdb.dart'; 2 | 3 | import '../responses/api_response.dart'; 4 | import '../responses/error_response.dart'; 5 | import 'database_model_response.dart'; 6 | import 'design_document_model_response.dart'; 7 | import 'document_model_response.dart'; 8 | import 'local_document_model_response.dart'; 9 | import 'server_model_response.dart'; 10 | 11 | /// Only provided for backward compatibility 12 | @Deprecated('Use ApiResponse instead') 13 | class DbResponse extends ApiResponse { 14 | /// Creates instance of [DbResponse] with [raw] and [json] 15 | DbResponse(Map json, 16 | {Map headers, String raw}) 17 | : super(json, headers: headers, raw: raw); 18 | 19 | /// Returns response with fields that may be returned by `ServerModel` 20 | /// request methods 21 | ServerModelResponse serverModelResponse() { 22 | return ServerModelResponse.from(this); 23 | } 24 | 25 | /// Returns response with fields that may be returned by `DatabaseModel` 26 | /// request methods 27 | DatabaseModelResponse databaseModelResponse() { 28 | return DatabaseModelResponse.from(this); 29 | } 30 | 31 | /// Returns response with fields that may be returned by `DocumentModel` 32 | /// request methods 33 | DocumentModelResponse documentModelResponse() { 34 | return DocumentModelResponse.from(this); 35 | } 36 | 37 | /// Returns response with fields that may be returned by `DesignDocumentModel` 38 | /// request methods 39 | DesignDocumentModelResponse designDocumentModelResponse() { 40 | return DesignDocumentModelResponse.from(this); 41 | } 42 | 43 | /// Returns response with fields that may be returned by `LocalDocumentModel` 44 | /// request methods 45 | LocalDocumentModelResponse localDocumentModelResponse() { 46 | return LocalDocumentModelResponse.from(this); 47 | } 48 | 49 | /// Returns error response if exists, otherwise returns `null` 50 | @override 51 | ErrorResponse errorResponse() { 52 | ErrorResponse e; 53 | if (isError()) { 54 | e = ErrorResponse(json['error'] as String, json['reason'] as String); 55 | } 56 | return e; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/src/entities/design_document_model_response.dart: -------------------------------------------------------------------------------- 1 | import '../responses/api_response.dart'; 2 | import '../responses/design_documents_response.dart'; 3 | 4 | /// Only provided for backward compatibility 5 | @Deprecated('Use DesignDocumentsResponse instead') 6 | class DesignDocumentModelResponse extends DesignDocumentsResponse { 7 | DesignDocumentModelResponse.from(ApiResponse response) : super.from(response); 8 | 9 | DesignDocumentModelResponse( 10 | {Map ddoc, 11 | bool ok, 12 | String id, 13 | String rev, 14 | Object attachment, 15 | List conflicts, 16 | bool deleted, 17 | List deletedConflicts, 18 | String localSeq, 19 | List> revsInfo, 20 | Map revisions, 21 | String name, 22 | Map viewIndex, 23 | int offset, 24 | List> rows, 25 | int totalRows, 26 | String updateSeq, 27 | List> results, 28 | String status, 29 | String raw}) 30 | : super( 31 | ddoc: ddoc, 32 | ok: ok, 33 | id: id, 34 | rev: rev, 35 | attachment: attachment, 36 | conflicts: conflicts, 37 | deleted: deleted, 38 | deletedConflicts: deletedConflicts, 39 | localSeq: localSeq, 40 | revsInfo: revsInfo, 41 | revisions: revisions, 42 | name: name, 43 | viewIndex: viewIndex, 44 | offset: offset, 45 | rows: rows, 46 | totalRows: totalRows, 47 | updateSeq: updateSeq, 48 | results: results, 49 | status: status, 50 | raw: raw); 51 | } 52 | -------------------------------------------------------------------------------- /lib/src/entities/document_model_response.dart: -------------------------------------------------------------------------------- 1 | import '../responses/api_response.dart'; 2 | import '../responses/documents_response.dart'; 3 | 4 | /// Only provided for backward compatibility 5 | @Deprecated('Use DocumentsResponse instead') 6 | class DocumentModelResponse extends DocumentsResponse { 7 | DocumentModelResponse.from(ApiResponse response) : super.from(response); 8 | 9 | DocumentModelResponse( 10 | {Map doc, 11 | bool ok, 12 | String id, 13 | String rev, 14 | Object attachment, 15 | List conflicts, 16 | bool deleted, 17 | List deletedConflicts, 18 | String localSeq, 19 | List> revsInfo, 20 | Map revisions}) 21 | : super( 22 | doc: doc, 23 | ok: ok, 24 | id: id, 25 | rev: rev, 26 | attachment: attachment, 27 | conflicts: conflicts, 28 | deleted: deleted, 29 | deletedConflicts: deletedConflicts, 30 | localSeq: localSeq, 31 | revsInfo: revsInfo, 32 | revisions: revisions); 33 | } 34 | -------------------------------------------------------------------------------- /lib/src/entities/local_document_model_response.dart: -------------------------------------------------------------------------------- 1 | import '../responses/api_response.dart'; 2 | import '../responses/local_documents_response.dart'; 3 | 4 | /// Only provided for backward compatibility 5 | @Deprecated('Use LocalDocumentsResponse instead') 6 | class LocalDocumentModelResponse extends LocalDocumentsResponse { 7 | LocalDocumentModelResponse.from(ApiResponse response) : super.from(response); 8 | 9 | LocalDocumentModelResponse( 10 | {int offset, 11 | List> rows, 12 | int totalRows, 13 | String updateSeq, 14 | Map doc, 15 | bool ok, 16 | String id, 17 | String rev, 18 | Object attachment, 19 | List conflicts, 20 | bool deleted, 21 | List deletedConflicts, 22 | String localSeq, 23 | List> revsInfo, 24 | Map revisions}) 25 | : super( 26 | offset: offset, 27 | rows: rows, 28 | totalRows: totalRows, 29 | updateSeq: updateSeq, 30 | doc: doc, 31 | ok: ok, 32 | id: id, 33 | rev: rev, 34 | attachment: attachment, 35 | conflicts: conflicts, 36 | deleted: deleted, 37 | deletedConflicts: deletedConflicts, 38 | localSeq: localSeq, 39 | revsInfo: revsInfo, 40 | revisions: revisions); 41 | } 42 | -------------------------------------------------------------------------------- /lib/src/entities/server_model_response.dart: -------------------------------------------------------------------------------- 1 | import '../responses/api_response.dart'; 2 | import '../responses/server_response.dart'; 3 | 4 | /// Only provided for backward compatibility 5 | @Deprecated('Use ServerResponse instead') 6 | class ServerModelResponse extends ServerResponse { 7 | ServerModelResponse.from(ApiResponse response) : super.from(response); 8 | 9 | ServerModelResponse( 10 | {String couchDbMessage, 11 | String uuid, 12 | Map vendor, 13 | String version, 14 | String state, 15 | List> results, 16 | String lastSeq, 17 | List allNodes, 18 | List clusterNodes, 19 | List> history, 20 | bool ok, 21 | int replicationIdVersion, 22 | String sessionId, 23 | int sourceLastSeq, 24 | int offset, 25 | int totalRows, 26 | String id, 27 | String database, 28 | String docId, 29 | String pid, 30 | String node, 31 | String source, 32 | String target, 33 | String startTime, 34 | String lastUpdate, 35 | Object info, 36 | int errorCount, 37 | Map>> fabric, 38 | Map> ddocCache, 39 | Map> couchDb, 40 | Map> pread, 41 | Map> couchReplicator, 42 | Map>> mem3, 43 | Map>> couchLog, 44 | Map> rexi, 45 | Map> globalChanges, 46 | int uptime, 47 | Map memory, 48 | int runQueue, 49 | int etsTableCount, 50 | int contextSwitches, 51 | int reductions, 52 | int garbageCollectionCount, 53 | int wordsReclaimed, 54 | int ioInput, 55 | int ioOutput, 56 | int osProcCount, 57 | int staleProcCount, 58 | int processCount, 59 | int processLimit, 60 | Map messageQueues, 61 | int internalReplicationJobs, 62 | Map distribution, 63 | String status, 64 | List uuids, 65 | List list, 66 | String name, 67 | List roles, 68 | Map userCtx}) 69 | : super( 70 | couchDbMessage: couchDbMessage, 71 | uuid: uuid, 72 | vendor: vendor, 73 | version: version, 74 | state: state, 75 | results: results, 76 | lastSeq: lastSeq, 77 | allNodes: allNodes, 78 | clusterNodes: clusterNodes, 79 | history: history, 80 | ok: ok, 81 | replicationIdVersion: replicationIdVersion, 82 | sessionId: sessionId, 83 | sourceLastSeq: sourceLastSeq, 84 | offset: offset, 85 | totalRows: totalRows, 86 | id: id, 87 | database: database, 88 | docId: docId, 89 | pid: pid, 90 | node: node, 91 | source: source, 92 | target: target, 93 | startTime: startTime, 94 | lastUpdate: lastUpdate, 95 | info: info, 96 | errorCount: errorCount, 97 | fabric: fabric, 98 | ddocCache: ddocCache, 99 | couchDb: couchDb, 100 | pread: pread, 101 | couchReplicator: couchReplicator, 102 | mem3: mem3, 103 | couchLog: couchLog, 104 | rexi: rexi, 105 | globalChanges: globalChanges, 106 | uptime: uptime, 107 | memory: memory, 108 | runQueue: runQueue, 109 | etsTableCount: etsTableCount, 110 | contextSwitches: contextSwitches, 111 | reductions: reductions, 112 | garbageCollectionCount: garbageCollectionCount, 113 | wordsReclaimed: wordsReclaimed, 114 | ioInput: ioInput, 115 | ioOutput: ioOutput, 116 | osProcCount: osProcCount, 117 | staleProcCount: staleProcCount, 118 | processCount: processCount, 119 | processLimit: processLimit, 120 | messageQueues: messageQueues, 121 | internalReplicationJobs: internalReplicationJobs, 122 | distribution: distribution, 123 | status: status, 124 | uuids: uuids, 125 | list: list, 126 | name: name, 127 | roles: roles, 128 | userCtx: userCtx); 129 | } 130 | -------------------------------------------------------------------------------- /lib/src/exceptions/couchdb_exception.dart: -------------------------------------------------------------------------------- 1 | import '../responses/error_response.dart'; 2 | 3 | /// Exception that triggers when database respond with code 300 and above 4 | class CouchDbException implements Exception { 5 | /// Returns instance with given error [code] and optional [response] 6 | CouchDbException(this.code, {this.response}); 7 | 8 | /// Status code 9 | int code; 10 | 11 | /// Error response 12 | ErrorResponse response; 13 | 14 | @override 15 | String toString() => ''' 16 | Code: $code 17 | Error: ${response.error} 18 | Reason: ${response.reason}'''; 19 | } 20 | -------------------------------------------------------------------------------- /lib/src/interfaces/client_interface.dart: -------------------------------------------------------------------------------- 1 | import '../responses/api_response.dart'; 2 | 3 | /// Client for interacting with CouchDB server 4 | abstract class ClientInterface { 5 | 6 | /// HEAD method 7 | Future head(String path, {Map reqHeaders}); 8 | 9 | /// GET method 10 | Future get(String path, {Map reqHeaders}); 11 | 12 | /// PUT method 13 | Future put(String path, 14 | {Object body, Map reqHeaders}); 15 | 16 | /// POST method 17 | Future post(String path, 18 | {Object body, Map reqHeaders}); 19 | 20 | /// DELETE method 21 | Future delete(String path, 22 | {Map reqHeaders}); 23 | 24 | /// COPY method 25 | Future copy(String path, {Map reqHeaders}); 26 | 27 | /// Makes request with specific [method] and with long or 28 | /// continuous connection 29 | /// 30 | /// Returns undecoded response. 31 | Future> streamed(String method, String path, 32 | {Object body, Map reqHeaders}); 33 | 34 | /// Initiates new session for specified user credentials by 35 | /// providing `Cookie` value 36 | /// 37 | /// If [next] parameter was provided the response will trigger redirection 38 | /// to the specified location in case of successful authentication. 39 | /// 40 | /// Structured response is available in `ServerModelResponse`. 41 | /// 42 | /// Returns JSON like: 43 | /// ```json 44 | /// {'ok': true, 'name': 'root', 'roles': ['_admin']} 45 | /// ``` 46 | Future authenticate([String next]); 47 | 48 | /// Closes user’s session by instructing the browser to clear the cookie 49 | /// 50 | /// Structured response is available in `ServerModelResponse`. 51 | /// 52 | /// Returns JSON like: 53 | /// ```json 54 | /// {'ok': true} 55 | /// ``` 56 | Future logout(); 57 | 58 | /// Returns information about the authenticated user, including a 59 | /// User Context Object, the authentication method and database 60 | /// that were used, and a list of configured 61 | /// authentication handlers on the server 62 | /// 63 | /// Structured response is available in `ServerModelResponse`. 64 | /// 65 | /// Returns JSON like: 66 | /// ```json 67 | /// { 68 | /// 'info': { 69 | /// 'authenticated': 'cookie', 70 | /// 'authentication_db': '_users', 71 | /// 'authentication_handlers': [ 72 | /// 'cookie', 73 | /// 'default' 74 | /// ] 75 | /// }, 76 | /// 'ok': true, 77 | /// 'userCtx': { 78 | /// 'name': 'root', 79 | /// 'roles': [ 80 | /// '_admin' 81 | /// ] 82 | /// } 83 | /// } 84 | /// ``` 85 | Future userInfo({bool basic = false}); 86 | } 87 | -------------------------------------------------------------------------------- /lib/src/interfaces/databases_interface.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../responses/databases_response.dart'; 4 | 5 | /// Class that define methods for interacting with entire database in CouchDB 6 | abstract class DatabasesInterface { 7 | /// Returns the HTTP Headers containing a minimal amount of information 8 | /// about the specified database 9 | Future headDbInfo(String dbName); 10 | 11 | /// Get the data using the Views 12 | Future query(String dbName, String dbDoc, String dbView, List keys); 13 | 14 | /// Gets information about the specified database 15 | /// 16 | /// Returns JSON like: 17 | /// ```json 18 | /// { 19 | /// "cluster": { 20 | /// "n": 3, 21 | /// "q": 8, 22 | /// "r": 2, 23 | /// "w": 2 24 | /// }, 25 | /// "compact_running": false, 26 | /// "data_size": 65031503, 27 | /// "db_name": "receipts", 28 | /// "disk_format_version": 6, 29 | /// "disk_size": 137433211, 30 | /// "doc_count": 6146, 31 | /// "doc_del_count": 64637, 32 | /// "instance_start_time": "0", 33 | /// "other": { 34 | /// "data_size": 66982448 35 | /// }, 36 | /// "purge_seq": 0, 37 | /// "sizes": { 38 | /// "active": 65031503, 39 | /// "external": 66982448, 40 | /// "file": 137433211 41 | /// }, 42 | /// "update_seq": "292786-g1AAAAF..." 43 | /// } 44 | /// ``` 45 | Future dbInfo(String dbName); 46 | 47 | /// Creates a new database 48 | /// 49 | /// Returns JSON like: 50 | /// ```json 51 | /// { 52 | /// "ok": true 53 | /// } 54 | /// ``` 55 | /// 56 | /// Otherwise error response is returned. 57 | Future createDb(String dbName, {int q = 8}); 58 | 59 | /// Deletes the specified database, and all the documents and attachments contained within it 60 | /// 61 | /// Returns JSON like: 62 | /// ```json 63 | /// { 64 | /// "ok": true 65 | /// } 66 | /// ``` 67 | /// 68 | /// Otherwise error response is returned. 69 | Future deleteDb(String dbName); 70 | 71 | /// Creates a new document in the specified database, using the supplied JSON document structure 72 | /// 73 | /// Returns JSON like: 74 | /// ```json 75 | /// { 76 | /// "id": "ab39fe0993049b84cfa81acd6ebad09d", 77 | /// "ok": true, 78 | /// "rev": "1-9c65296036141e575d32ba9c034dd3ee" 79 | /// } 80 | /// ``` 81 | Future createDocIn( 82 | String dbName, Map doc, 83 | {String batch, Map headers}); 84 | 85 | /// Executes the built-in _all_docs view, returning all of the documents in the database 86 | /// 87 | /// Returns JSON like: 88 | /// ```json 89 | /// { 90 | /// "offset": 0, 91 | /// "rows": [ 92 | /// { 93 | /// "id": "16e458537602f5ef2a710089dffd9453", 94 | /// "key": "16e458537602f5ef2a710089dffd9453", 95 | /// "value": { 96 | /// "rev": "1-967a00dff5e02add41819138abb3284d" 97 | /// } 98 | /// }, 99 | /// { 100 | /// "id": "a4c51cdfa2069f3e905c431114001aff", 101 | /// "key": "a4c51cdfa2069f3e905c431114001aff", 102 | /// "value": { 103 | /// "rev": "1-967a00dff5e02add41819138abb3284d" 104 | /// } 105 | /// } 106 | /// ], 107 | /// "total_rows": 2 108 | /// } 109 | /// ``` 110 | Future allDocs(String dbName, 111 | {bool conflicts = false, 112 | bool descending = false, 113 | Object endKey, 114 | String endKeyDocId, 115 | bool group = false, 116 | int groupLevel, 117 | bool includeDocs = false, 118 | bool attachments = false, 119 | bool altEncodingInfo = false, 120 | bool inclusiveEnd = true, 121 | Object key, 122 | List keys, 123 | int limit, 124 | bool reduce, 125 | int skip, 126 | bool sorted = true, 127 | bool stable = false, 128 | String stale, 129 | Object startKey, 130 | String startKeyDocId, 131 | String update, 132 | bool updateSeq = false}); 133 | 134 | /// Executes the built-in _all_docs view, returning specified documents in the database 135 | /// 136 | /// The POST to _all_docs allows to specify multiple [keys] to be selected from the database. 137 | /// This enables you to request multiple documents in a single request, in place of multiple [getDoc()] requests. 138 | /// 139 | /// Returns JSON like: 140 | /// ```json 141 | /// { 142 | /// "offset": 0, 143 | /// "rows": [ 144 | /// { 145 | /// "id": "16e458537602f5ef2a710089dffd9453", 146 | /// "key": "16e458537602f5ef2a710089dffd9453", 147 | /// "value": { 148 | /// "rev": "1-967a00dff5e02add41819138abb3284d" 149 | /// } 150 | /// }, 151 | /// { 152 | /// "id": "a4c51cdfa2069f3e905c431114001aff", 153 | /// "key": "a4c51cdfa2069f3e905c431114001aff", 154 | /// "value": { 155 | /// "rev": "1-967a00dff5e02add41819138abb3284d" 156 | /// } 157 | /// } 158 | /// ], 159 | /// "total_rows": 2453 160 | /// } 161 | /// ``` 162 | Future docsByKeys(String dbName, {List keys}); 163 | 164 | /// Returns a JSON structure of all of the design documents in a given database 165 | /// 166 | /// Returns JSON like: 167 | /// ```json 168 | /// { 169 | /// "offset": 0, 170 | /// "rows": [ 171 | /// { 172 | /// "id": "_design/16e458537602f5ef2a710089dffd9453", 173 | /// "key": "_design/16e458537602f5ef2a710089dffd9453", 174 | /// "value": { 175 | /// "rev": "1-967a00dff5e02add41819138abb3284d" 176 | /// } 177 | /// }, 178 | /// { 179 | /// "id": "_design/a4c51cdfa2069f3e905c431114001aff", 180 | /// "key": "_design/a4c51cdfa2069f3e905c431114001aff", 181 | /// "value": { 182 | /// "rev": "1-967a00dff5e02add41819138abb3284d" 183 | /// } 184 | /// } 185 | /// ], 186 | /// "total_rows": 2 187 | /// } 188 | /// ``` 189 | Future allDesignDocs(String dbName, 190 | {bool conflicts = false, 191 | bool descending = false, 192 | String endKey, 193 | String endKeyDocId, 194 | bool includeDocs = false, 195 | bool inclusiveEnd = true, 196 | String key, 197 | String keys, 198 | int limit, 199 | int skip = 0, 200 | String startKey, 201 | String startKeyDocId, 202 | bool updateSeq = false}); 203 | 204 | /// Returns a JSON structure of specified design documents in a given database 205 | /// 206 | /// The POST to _design_docs allows to specify multiple [keys] to be selected from the database. 207 | /// This enables you to request multiple design documents in a single request, in place of multiple [designDoc()] requests 208 | /// 209 | /// Returns JSON like: 210 | /// ```json 211 | /// { 212 | /// "offset": 0, 213 | /// "rows": [ 214 | /// { 215 | /// "id": "_design/16e458537602f5ef2a710089dffd9453", 216 | /// "key": "_design/16e458537602f5ef2a710089dffd9453", 217 | /// "value": { 218 | /// "rev": "1-967a00dff5e02add41819138abb3284d" 219 | /// } 220 | /// }, 221 | /// { 222 | /// "id": "_design/a4c51cdfa2069f3e905c431114001aff", 223 | /// "key": "_design/a4c51cdfa2069f3e905c431114001aff", 224 | /// "value": { 225 | /// "rev": "1-967a00dff5e02add41819138abb3284d" 226 | /// } 227 | /// } 228 | /// ], 229 | /// "total_rows": 6 230 | /// } 231 | /// ``` 232 | Future designDocsByKeys( 233 | String dbName, List keys); 234 | 235 | /// Executes multiple specified built-in view queries of all documents in this database 236 | /// 237 | /// Returns JSON like: 238 | /// ```json 239 | /// { 240 | /// "results" : [ 241 | /// { 242 | /// "rows": [ 243 | /// { 244 | /// "id": "SpaghettiWithMeatballs", 245 | /// "key": "meatballs", 246 | /// "value": 1 247 | /// }, 248 | /// { 249 | /// "id": "SpaghettiWithMeatballs", 250 | /// "key": "spaghetti", 251 | /// "value": 1 252 | /// }, 253 | /// { 254 | /// "id": "SpaghettiWithMeatballs", 255 | /// "key": "tomato sauce", 256 | /// "value": 1 257 | /// } 258 | /// ], 259 | /// "total_rows": 3 260 | /// }, 261 | /// { 262 | /// "offset" : 2, 263 | /// "rows" : [ 264 | /// { 265 | /// "id" : "Adukiandorangecasserole-microwave", 266 | /// "key" : "Aduki and orange casserole - microwave", 267 | /// "value" : [ 268 | /// null, 269 | /// "Aduki and orange casserole - microwave" 270 | /// ] 271 | /// }, 272 | /// { 273 | /// "id" : "Aioli-garlicmayonnaise", 274 | /// "key" : "Aioli - garlic mayonnaise", 275 | /// "value" : [ 276 | /// null, 277 | /// "Aioli - garlic mayonnaise" 278 | /// ] 279 | /// }, 280 | /// { 281 | /// "id" : "Alabamapeanutchicken", 282 | /// "key" : "Alabama peanut chicken", 283 | /// "value" : [ 284 | /// null, 285 | /// "Alabama peanut chicken" 286 | /// ] 287 | /// } 288 | /// ], 289 | /// "total_rows" : 2667 290 | /// } 291 | /// ] 292 | /// } 293 | /// ``` 294 | Future queriesDocsFrom( 295 | String dbName, List> queries); 296 | 297 | /// Queries several documents in bulk 298 | /// 299 | /// Returns JSON like: 300 | /// ```json 301 | /// { 302 | /// "results": [ 303 | /// { 304 | /// "id": "foo", 305 | /// "docs": [ 306 | /// { 307 | /// "ok": { 308 | /// "_id": "bbb", 309 | /// "_rev": "4-753875d51501a6b1883a9d62b4d33f91", 310 | /// "value": "this is foo", 311 | /// "_revisions": { 312 | /// "start": 4, 313 | /// "ids": [ 314 | /// "753875d51501a6b1883a9d62b4d33f91", 315 | /// "efc54218773c6acd910e2e97fea2a608", 316 | /// "2ee767305024673cfb3f5af037cd2729", 317 | /// "4a7e4ae49c4366eaed8edeaea8f784ad" 318 | /// ] 319 | /// } 320 | /// } 321 | /// } 322 | /// ] 323 | /// }, 324 | /// { 325 | /// "id": "foo", 326 | /// "docs": [ 327 | /// { 328 | /// "ok": { 329 | /// "_id": "bbb", 330 | /// "_rev": "1-4a7e4ae49c4366eaed8edeaea8f784ad", 331 | /// "value": "this is the first revision of foo", 332 | /// "_revisions": { 333 | /// "start": 1, 334 | /// "ids": [ 335 | /// "4a7e4ae49c4366eaed8edeaea8f784ad" 336 | /// ] 337 | /// } 338 | /// } 339 | /// } 340 | /// ] 341 | /// }, 342 | /// { 343 | /// "id": "bar", 344 | /// "docs": [ 345 | /// { 346 | /// "ok": { 347 | /// "_id": "bar", 348 | /// "_rev": "2-9b71d36dfdd9b4815388eb91cc8fb61d", 349 | /// "baz": true, 350 | /// "_revisions": { 351 | /// "start": 2, 352 | /// "ids": [ 353 | /// "9b71d36dfdd9b4815388eb91cc8fb61d", 354 | /// "309651b95df56d52658650fb64257b97" 355 | /// ] 356 | /// } 357 | /// } 358 | /// } 359 | /// ] 360 | /// } 361 | /// ] 362 | /// } 363 | /// ``` 364 | Future bulkDocs(String dbName, List docs, 365 | {@required bool revs}); 366 | 367 | /// Creates and updates multiple documents at the same time within a single request 368 | /// 369 | /// Returns JSON like: 370 | /// ```json 371 | /// [ 372 | /// { 373 | /// "ok": true, 374 | /// "id": "FishStew", 375 | /// "rev":" 1-967a00dff5e02add41819138abb3284d" 376 | /// }, 377 | /// { 378 | /// "ok": true, 379 | /// "id": "LambStew", 380 | /// "rev": "3-f9c62b2169d0999103e9f41949090807" 381 | /// } 382 | /// ] 383 | /// ``` 384 | Future insertBulkDocs(String dbName, List docs, 385 | {bool newEdits = true, Map headers}); 386 | 387 | /// Find documents using a declarative JSON querying syntax 388 | /// 389 | /// Returns JSON like: 390 | /// ```json 391 | /// { 392 | /// "docs": [ 393 | /// { 394 | /// "_id": "176694", 395 | /// "_rev": "1-54f8e950cc338d2385d9b0cda2fd918e", 396 | /// "year": 2011, 397 | /// "title": "The Tragedy of Man" 398 | /// }, 399 | /// { 400 | /// "_id": "780504", 401 | /// "_rev": "1-5f14bab1a1e9ac3ebdf85905f47fb084", 402 | /// "year": 2011, 403 | /// "title": "Drive" 404 | /// } 405 | /// ], 406 | /// "execution_stats": { 407 | /// "total_keys_examined": 0, 408 | /// "total_docs_examined": 200, 409 | /// "total_quorum_docs_examined": 0, 410 | /// "results_returned": 2, 411 | /// "execution_time_ms": 5.52 412 | /// } 413 | /// } 414 | /// ``` 415 | Future find( 416 | String dbName, Map selector, 417 | {int limit = 25, 418 | int skip, 419 | List sort, 420 | List fields, 421 | Object useIndex, 422 | int r = 1, 423 | String bookmark, 424 | bool update = true, 425 | bool stable, 426 | String stale = 'false', 427 | bool executionStats = false}); 428 | 429 | /// Create a new index on a database 430 | /// 431 | /// Returns JSON like: 432 | /// ```json 433 | /// { 434 | /// "result": "created", 435 | /// "id": "_design/a5f4711fc9448864a13c81dc71e660b524d7410c", 436 | /// "name": "foo-index" 437 | /// } 438 | /// ``` 439 | Future createIndexIn(String dbName, 440 | {@required List indexFields, 441 | String ddoc, 442 | String name, 443 | String type = 'json', 444 | Map partialFilterSelector}); 445 | 446 | /// Gets a list of all indexes in the database 447 | /// 448 | /// Returns JSON like: 449 | /// ```json 450 | /// { 451 | /// "total_rows": 2, 452 | /// "indexes": [ 453 | /// { 454 | /// "ddoc": null, 455 | /// "name": "_all_docs", 456 | /// "type": "special", 457 | /// "def": { 458 | /// "fields": [ 459 | /// { 460 | /// "_id": "asc" 461 | /// } 462 | /// ] 463 | /// } 464 | /// }, 465 | /// { 466 | /// "ddoc": "_design/a5f4711fc9448864a13c81dc71e660b524d7410c", 467 | /// "name": "foo-index", 468 | /// "type": "json", 469 | /// "def": { 470 | /// "fields": [ 471 | /// { 472 | /// "foo": "asc" 473 | /// } 474 | /// ] 475 | /// } 476 | /// } 477 | /// ] 478 | /// } 479 | /// ``` 480 | Future indexesAt(String dbName); 481 | 482 | /// Delets index in the database 483 | /// 484 | /// Returns JSON like: 485 | /// ```json 486 | /// { 487 | /// "ok": "true" 488 | /// } 489 | /// ``` 490 | Future deleteIndexIn( 491 | String dbName, String designDoc, String name); 492 | 493 | /// Shows which index is being used by the query 494 | /// 495 | /// Returns JSON like: 496 | /// ```json 497 | /// { 498 | /// "dbname": "movies", 499 | /// "index": { 500 | /// "ddoc": "_design/0d61d9177426b1e2aa8d0fe732ec6e506f5d443c", 501 | /// "name": "0d61d9177426b1e2aa8d0fe732ec6e506f5d443c", 502 | /// "type": "json", 503 | /// "def": { 504 | /// "fields": [ 505 | /// { 506 | /// "year": "asc" 507 | /// } 508 | /// ] 509 | /// } 510 | /// }, 511 | /// "selector": { 512 | /// "year": { 513 | /// "$gt": 2010 514 | /// } 515 | /// }, 516 | /// "opts": { 517 | /// "use_index": [], 518 | /// "bookmark": "nil", 519 | /// "limit": 2, 520 | /// "skip": 0, 521 | /// "sort": {}, 522 | /// "fields": [ 523 | /// "_id", 524 | /// "_rev", 525 | /// "year", 526 | /// "title" 527 | /// ], 528 | /// "r": [ 529 | /// 49 530 | /// ], 531 | /// "conflicts": false 532 | /// }, 533 | /// "limit": 2, 534 | /// "skip": 0, 535 | /// "fields": [ 536 | /// "_id", 537 | /// "_rev", 538 | /// "year", 539 | /// "title" 540 | /// ], 541 | /// "range": { 542 | /// "start_key": [ 543 | /// 2010 544 | /// ], 545 | /// "end_key": [ 546 | /// {} 547 | /// ] 548 | /// } 549 | /// } 550 | /// ``` 551 | Future explain( 552 | String dbName, Map selector, 553 | {int limit = 25, 554 | int skip, 555 | List sort, 556 | List fields, 557 | Object useIndex, 558 | int r = 1, 559 | String bookmark, 560 | bool update = true, 561 | bool stable, 562 | String stale = 'false', 563 | bool executionStats = false}); 564 | 565 | /// Returns a list of database shards. Each shard will have its internal 566 | /// database range, and the nodes on which replicas of those shards are stored 567 | /// 568 | /// Returns JSON like: 569 | /// ```dart 570 | /// { 571 | /// "shards": { 572 | /// "00000000-1fffffff": [ 573 | /// "couchdb@node1.example.com", 574 | /// "couchdb@node2.example.com", 575 | /// "couchdb@node3.example.com" 576 | /// ], 577 | /// "20000000-3fffffff": [ 578 | /// "couchdb@node1.example.com", 579 | /// "couchdb@node2.example.com", 580 | /// "couchdb@node3.example.com" 581 | /// ] 582 | /// } 583 | /// } 584 | /// ``` 585 | Future shards(String dbName); 586 | 587 | /// Returns information about the specific shard into which a given document 588 | /// has been stored, along with information about the nodes on which that 589 | /// shard has a replica 590 | /// 591 | /// Returns JSON like: 592 | /// ```dart 593 | /// { 594 | /// "range": "e0000000-ffffffff", 595 | /// "nodes": [ 596 | /// "node1@127.0.0.1", 597 | /// "node2@127.0.0.1", 598 | /// "node3@127.0.0.1" 599 | /// ] 600 | /// } 601 | /// ``` 602 | Future shard(String dbName, String docId); 603 | 604 | /// For the given database, force-starts internal shard synchronization 605 | /// for all replicas of all database shards 606 | /// 607 | /// This is typically only used when performing cluster maintenance, 608 | /// such as moving a shard. 609 | /// 610 | /// Returns JSON like: 611 | /// ```dart 612 | /// { 613 | /// "ok": true 614 | /// } 615 | /// ``` 616 | Future synchronizeShards(String dbName); 617 | 618 | /// Returns a sorted list of changes made to documents in the database 619 | /// 620 | /// [feed] may be one from: 621 | /// 622 | /// - `normal` 623 | /// - `longpoll` 624 | /// - `continuous` 625 | /// - `eventsource` 626 | /// 627 | /// For `eventsource` value of [feed] JSON response placed at `results` field 628 | /// with `Map` objects with two fields `data` and `id`. 629 | /// `last_seq` and `pending` fields are missing in returned JSON if [feed] 630 | /// have `eventsource` or `continuous` values. 631 | /// 632 | /// [Read more about difference between above values.](http://docs.couchdb.org/en/stable/api/database/changes.html#changes-feeds) 633 | /// 634 | /// Returns JSON like: 635 | /// ```json 636 | /// { 637 | /// "last_seq": "5-g1AAAAIreJyVkEsKwjAURZ-toI5cgq5A0sQ0OrI70XyppcaRY 638 | /// 92J7kR3ojupaSPUUgotgRd4yTlwbw4A0zRUMLdnpaMkwmyF3Ily9xBwEIuiKLI05KOT 639 | /// W0wkV4rruP29UyGWbordzwKVxWBNOGMKZhertDlarbr5pOT3DV4gudUC9-MPJX9tpEA 640 | /// Yx4TQASns2E24ucuJ7rXJSL1BbEgf3vTwpmedCZkYa7Pulck7Xt7x_usFU2aIHOD4e 641 | /// EfVTVA5KMGUkqhNZV-8_o5i", 642 | /// "pending": 0, 643 | /// "results": [ 644 | /// { 645 | /// "changes": [ 646 | /// { 647 | /// "rev": "2-7051cbe5c8faecd085a3fa619e6e6337" 648 | /// } 649 | /// ], 650 | /// "id": "6478c2ae800dfc387396d14e1fc39626", 651 | /// "seq": "3-g1AAAAG3eJzLYWBg4MhgTmHgz8tPSTV0MDQy1zMAQsMcoAR 652 | /// TIkOS_P___7MSGXAqSVIAkkn2IFUZzIkMuUAee5pRqnGiuXkKA2dpXkpqWmZeagpu_Q4 653 | /// g_fGEbEkAqaqH2sIItsXAyMjM2NgUUwdOU_JYgCRDA5ACGjQfn30QlQsgKvcjfGaQZma 654 | /// UmmZClM8gZhyAmHGfsG0PICrBPmQC22ZqbGRqamyIqSsLAAArcXo" 655 | /// }, 656 | /// { 657 | /// "changes": [ 658 | /// { 659 | /// "rev": "3-7379b9e515b161226c6559d90c4dc49f" 660 | /// } 661 | /// ], 662 | /// "deleted": true, 663 | /// "id": "5bbc9ca465f1b0fcd62362168a7c8831", 664 | /// "seq": "4-g1AAAAHXeJzLYWBg4MhgTmHgz8tPSTV0MDQy1zMAQsMcoAR 665 | /// TIkOS_P___7MymBMZc4EC7MmJKSmJqWaYynEakaQAJJPsoaYwgE1JM0o1TjQ3T2HgLM1L 666 | /// SU3LzEtNwa3fAaQ_HqQ_kQG3qgSQqnoUtxoYGZkZG5uS4NY8FiDJ0ACkgAbNx2cfROUCi 667 | /// Mr9CJ8ZpJkZpaaZEOUziBkHIGbcJ2zbA4hKsA-ZwLaZGhuZmhobYurKAgCz33kh" 668 | /// }, 669 | /// { 670 | /// "changes": [ 671 | /// { 672 | /// "rev": "6-460637e73a6288cb24d532bf91f32969" 673 | /// }, 674 | /// { 675 | /// "rev": "5-eeaa298781f60b7bcae0c91bdedd1b87" 676 | /// } 677 | /// ], 678 | /// "id": "729eb57437745e506b333068fff665ae", 679 | /// "seq": "5-g1AAAAIReJyVkE0OgjAQRkcwUVceQU9g-mOpruQm2tI2SLCuX 680 | /// OtN9CZ6E70JFmpCCCFCmkyTdt6bfJMDwDQNFcztWWkcY8JXyB2cu49AgFwURZGloRid3MMk 681 | /// EUoJHbXbOxVy6arc_SxQWQzRVHCuYHaxSpuj1aqbj0t-3-AlSrZakn78oeSvjRSIkIhSNiC 682 | /// FHbsKN3c50b02mURvEB-yD296eNOzzoRMRLRZ98rkHS_veGcC_nR-fGe1gaCaxihhjOI2lX 683 | /// 0BhniHaA" 684 | /// } 685 | /// ] 686 | /// } 687 | /// ``` 688 | Future> changesIn(String dbName, 689 | {List docIds, 690 | bool conflicts = false, 691 | bool descending = false, 692 | String feed = 'normal', 693 | String filter, 694 | int heartbeat = 60000, 695 | bool includeDocs = false, 696 | bool attachments = false, 697 | bool attEncodingInfo = false, 698 | int lastEventId, 699 | int limit, 700 | String since = '0', 701 | String style = 'main_only', 702 | int timeout = 60000, 703 | String view, 704 | int seqInterval}); 705 | 706 | /// Requests the database changes feed in the same way as [changesIn()] does, 707 | /// but is widely used with [filter]='_doc_ids' query parameter and allows 708 | /// one to pass a larger list of document IDs to [filter] 709 | /// 710 | /// Returns JSON like: 711 | /// ```json 712 | /// { 713 | /// "last_seq": "5-g1AAAAIreJyVkEsKwjAURZ-toI5cgq5A0sQ0OrI70XyppcaRY92J7kR3ojupaSPUUgotgRd4yTlwbw4A0zRUMLdnpaMkwmyF3Ily9xBwEIuiKLI05KOTW0wkV4rruP29UyGWbordzwKVxWBNOGMKZhertDlarbr5pOT3DV4gudUC9-MPJX9tpEAYx4TQASns2E24ucuJ7rXJSL1BbEgf3vTwpmedCZkYa7Pulck7Xt7x_usFU2aIHOD4eEfVTVA5KMGUkqhNZV8_o5i", 714 | /// "pending": 0, 715 | /// "results": [ 716 | /// { 717 | /// "changes": [ 718 | /// { 719 | /// "rev": "13-bcb9d6388b60fd1e960d9ec4e8e3f29e" 720 | /// } 721 | /// ], 722 | /// "id": "SpaghettiWithMeatballs", 723 | /// "seq": "5-g1AAAAIReJyVkE0OgjAQRkcwUVceQU9g-mOpruQm2tI2SLCuXOtN9CZ6E70JFmpCCCFCmkyTdt6bfJMDwDQNFcztWWkcY8JXyB2cu49AgFwURZGloRid3MMkEUoJHbXbOxVy6arc_SxQWQzRVHCuYHaxSpuj1aqbj0t-3-AlSrZakn78oeSvjRSIkIhSNiCFHbsKN3c50b02mURvEB-yD296eNOzzoRMRLRZ98rkHS_veGcC_nR-fGe1gaCaxihhjOI2lX0BhniHaA" 724 | /// } 725 | /// ] 726 | /// } 727 | /// ``` 728 | Future> postChangesIn(String dbName, 729 | {List docIds, 730 | bool conflicts = false, 731 | bool descending = false, 732 | String feed = 'normal', 733 | String filter = '_doc_ids', 734 | int heartbeat = 60000, 735 | bool includeDocs = false, 736 | bool attachments = false, 737 | bool attEncodingInfo = false, 738 | int lastEventId, 739 | int limit, 740 | String since = '0', 741 | String style = 'main_only', 742 | int timeout = 60000, 743 | String view, 744 | int seqInterval}); 745 | 746 | /// Request compaction of the specified database 747 | /// 748 | /// Returns JSON like: 749 | /// ```json 750 | /// { 751 | /// "ok": true 752 | /// } 753 | /// ``` 754 | Future compact(String dbName); 755 | 756 | /// Compacts the view indexes associated with the specified design document 757 | /// 758 | /// Returns JSON like: 759 | /// ```json 760 | /// { 761 | /// "ok": true 762 | /// } 763 | /// ``` 764 | Future compactViewIndexesWith( 765 | String dbName, String ddocName); 766 | 767 | /// Commits any recent changes to the specified database to disk 768 | /// 769 | /// Returns JSON like: 770 | /// ```json 771 | /// { 772 | /// // This property isn't listed in [DatabaseModelResponse] 773 | /// "instance_start_time": "0", 774 | /// "ok": true 775 | /// } 776 | /// ``` 777 | Future ensureFullCommit(String dbName); 778 | 779 | /// Removes view index files that are no longer required by CouchDB as a result of changed views within design documents 780 | /// 781 | /// Returns JSON like: 782 | /// ```json 783 | /// { 784 | /// "ok": true 785 | /// } 786 | /// ``` 787 | Future viewCleanup(String dbName); 788 | 789 | /// Returns the current security object from the specified database 790 | /// 791 | /// Returns JSON like: 792 | /// ```json 793 | /// { 794 | /// "admins": { 795 | /// "names": [ 796 | /// "superuser" 797 | /// ], 798 | /// "roles": [ 799 | /// "admins" 800 | /// ] 801 | /// }, 802 | /// "members": { 803 | /// "names": [ 804 | /// "user1", 805 | /// "user2" 806 | /// ], 807 | /// "roles": [ 808 | /// "developers" 809 | /// ] 810 | /// } 811 | /// } 812 | /// ``` 813 | Future securityOf(String dbName); 814 | 815 | /// Sets the security object for the given database 816 | /// 817 | /// Returns JSON like: 818 | /// ```json 819 | /// { 820 | /// "ok": true 821 | /// } 822 | /// ``` 823 | Future setSecurityFor( 824 | String dbName, Map>> security); 825 | 826 | /// Permanently removes the references to deleted documents from the database 827 | /// 828 | /// Returns JSON like: 829 | /// ```json 830 | /// { 831 | /// "purge_seq": null, 832 | /// "purged": { 833 | /// "c6114c65e295552ab1019e2b046b10e": { 834 | /// "purged": [ 835 | /// "3-c50a32451890a3f1c3e423334cc92745" 836 | /// ] 837 | /// } 838 | /// } 839 | /// } 840 | /// ``` 841 | Future purge( 842 | String dbName, Map> docs); 843 | 844 | /// Gets the current purged_infos_limit (purged documents limit) setting, 845 | /// the maximum number of historical purges (purged document Ids with their revisions) 846 | /// that can be stored in the database 847 | /// 848 | /// Returns JSON like: 849 | /// ```json 850 | /// 1000 851 | /// ``` 852 | Future purgedInfosLimit(String dbName); 853 | 854 | /// Sets the maximum number of purges (requested purged Ids with their revisions) 855 | /// that will be tracked in the database, even after compaction has occurred 856 | /// 857 | /// Returns JSON like: 858 | /// ```json 859 | /// { 860 | /// "ok": true 861 | /// } 862 | /// ``` 863 | Future setPurgedInfosLimit(String dbName, int limit); 864 | 865 | /// Returns the document revisions that do not exist in the database 866 | /// 867 | /// Returns JSON like: 868 | /// ```json 869 | /// { 870 | /// "missed_revs":{ 871 | /// "c6114c65e295552ab1019e2b046b10e": [ 872 | /// "3-b06fcd1c1c9e0ec7c480ee8aa467bf3b" 873 | /// ] 874 | /// } 875 | /// } 876 | /// ``` 877 | Future missingRevs( 878 | String dbName, Map> revs); 879 | 880 | /// Returns the subset of those that do not correspond to revisions stored in the database 881 | /// 882 | /// Returns JSON like: 883 | /// ```json 884 | /// { 885 | /// "190f721ca3411be7aa9477db5f948bbb": { 886 | /// "missing": [ 887 | /// "3-bb72a7682290f94a985f7afac8b27137", 888 | /// "5-067a00dff5e02add41819138abb3284d" 889 | /// ], 890 | /// "possible_ancestors": [ 891 | /// "4-10265e5a26d807a3cfa459cf1a82ef2e" 892 | /// ] 893 | /// } 894 | /// } 895 | /// ``` 896 | Future revsDiff( 897 | String dbName, Map> revs); 898 | 899 | /// Gets the current **revs_limit** (revision limit) setting 900 | /// 901 | /// Returns JSON like: 902 | /// ```json 903 | /// 1000 904 | /// ``` 905 | Future revsLimitOf(String dbName); 906 | 907 | /// Sets the maximum number of document revisions that will be tracked by CouchDB, even after compaction has occurred 908 | /// 909 | /// Returns JSON like: 910 | /// ```json 911 | /// { 912 | /// "ok": true 913 | /// } 914 | /// ``` 915 | Future setRevsLimit(String dbName, int limit); 916 | } 917 | -------------------------------------------------------------------------------- /lib/src/interfaces/design_documents_interface.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../responses/design_documents_response.dart'; 4 | 5 | /// Class that contains methods that allow operate with design documents 6 | abstract class DesignDocumentsInterface { 7 | 8 | /// Returns the HTTP Headers containing a minimal amount of information about the specified design document 9 | Future designDocHeaders( 10 | String dbName, String ddocId, 11 | {Map headers, 12 | bool attachments = false, 13 | bool attEncodingInfo = false, 14 | List attsSince, 15 | bool conflicts = false, 16 | bool deletedConflicts = false, 17 | bool latest = false, 18 | bool localSeq = false, 19 | bool meta = false, 20 | Object openRevs, 21 | String rev, 22 | bool revs = false, 23 | bool revsInfo = false}); 24 | 25 | /// Returns the contents of the design document specified with the name of the design document and 26 | /// from the specified database 27 | Future designDoc(String dbName, String ddocId, 28 | {Map headers, 29 | bool attachments = false, 30 | bool attEncodingInfo = false, 31 | List attsSince, 32 | bool conflicts = false, 33 | bool deletedConflicts = false, 34 | bool latest = false, 35 | bool localSeq = false, 36 | bool meta = false, 37 | Object openRevs, 38 | String rev, 39 | bool revs = false, 40 | bool revsInfo = false}); 41 | 42 | /// Creates a new named design document, or creates a new revision of the existing design document 43 | /// 44 | /// The design documents have some agreement upon their fields and structure. Currently it is the following: 45 | /// 46 | /// 1. language (string): Defines Query Server key to process design document functions 47 | /// 2. options (object): View’s default options 48 | /// 3. filters (object): Filter functions definition 49 | /// 4. lists (object): List functions definition 50 | /// 5. rewrites (array or string): Rewrite rules definition 51 | /// 6. shows (object): Show functions definition 52 | /// 7. updates (object): Update functions definition 53 | /// 8. validate_doc_update (string): Validate document update function source 54 | /// 9. views (object): View functions definition. 55 | /// 56 | /// Note, that for `filters`, `lists`, `shows` and `updates` fields objects are mapping of function name to string function 57 | /// source code. For `views` mapping is the same except that values are objects with `map` and `reduce` (optional) keys 58 | /// which also contains functions source code. 59 | Future insertDesignDoc( 60 | String dbName, String ddocId, Map body, 61 | {Map headers, 62 | String rev, 63 | String batch, 64 | bool newEdits = true}); 65 | 66 | /// Deletes the specified document from the database 67 | Future deleteDesignDoc( 68 | String dbName, String ddocId, String rev, 69 | {Map headers, String batch}); 70 | 71 | /// Copies an existing design document to a new or existing one 72 | Future copyDesignDoc( 73 | String dbName, String ddocId, 74 | {Map headers, String rev, String batch}); 75 | 76 | /// Returns the HTTP headers containing a minimal amount of information about the specified attachment 77 | Future attachmentInfo( 78 | String dbName, String ddocId, String attName, 79 | {Map headers, String rev}); 80 | 81 | /// Returns the file attachment associated with the design document 82 | Future attachment( 83 | String dbName, String ddocId, String attName, 84 | {Map headers, String rev}); 85 | 86 | /// Uploads the supplied content as an attachment to the specified design document 87 | /// 88 | /// You must supply the `Content-Type` header, and for an existing document 89 | /// you must also supply either the [rev] query argument or the `If-Match` HTTP header 90 | Future uploadAttachment( 91 | String dbName, String ddocId, String attName, Object body, 92 | {Map headers, String rev}); 93 | 94 | /// Deletes the attachment of the specified design document 95 | Future deleteAttachment( 96 | String dbName, String ddocId, String attName, 97 | {@required String rev, Map headers, String batch}); 98 | 99 | /// Obtains information about the specified design document, including the index, index size 100 | /// and current status of the design document and associated index information 101 | /// 102 | /// Returns JSON like: 103 | /// ```json 104 | /// { 105 | /// "name": "recipe", 106 | /// "view_index": { 107 | /// "compact_running": false, 108 | /// "data_size": 926691, 109 | /// "disk_size": 1982704, 110 | /// "language": "python", 111 | /// "purge_seq": 0, 112 | /// "signature": "a59a1bb13fdf8a8a584bc477919c97ac", 113 | /// "update_seq": 12397, 114 | /// "updater_running": false, 115 | /// "waiting_clients": 0, 116 | /// "waiting_commit": false 117 | /// } 118 | /// } 119 | /// ``` 120 | Future designDocInfo( 121 | String dbName, String ddocId, 122 | {Map headers}); 123 | 124 | /// Executes the specified view function from the specified design document 125 | /// 126 | /// Supported values for [update]: 127 | /// 128 | /// - true 129 | /// - false 130 | /// - lazy 131 | /// 132 | /// Returns JSON like: 133 | /// ```json 134 | /// { 135 | /// "offset": 0, 136 | /// "rows": [ 137 | /// { 138 | /// "id": "SpaghettiWithMeatballs", 139 | /// "key": "meatballs", 140 | /// "value": 1 141 | /// }, 142 | /// { 143 | /// "id": "SpaghettiWithMeatballs", 144 | /// "key": "spaghetti", 145 | /// "value": 1 146 | /// }, 147 | /// { 148 | /// "id": "SpaghettiWithMeatballs", 149 | /// "key": "tomato sauce", 150 | /// "value": 1 151 | /// } 152 | /// ], 153 | /// "total_rows": 3 154 | /// } 155 | /// ``` 156 | Future executeViewFunction( 157 | String dbName, String ddocId, String viewName, 158 | {bool conflicts = false, 159 | bool descending = false, 160 | Object endKey, 161 | String endKeyDocId, 162 | bool group = false, 163 | int groupLevel, 164 | bool includeDocs = false, 165 | bool attachments = false, 166 | bool attEncodingInfo = false, 167 | bool inclusiveEnd = true, 168 | Object key, 169 | List keys, 170 | int limit, 171 | bool reduce = false, // in implementation class 172 | int skip = 0, 173 | bool sorted = true, 174 | bool stable = false, 175 | String stale, 176 | Object startKey, 177 | String startKeyDocId, 178 | String update = 'true', 179 | bool updateSeq = false, 180 | Map headers}); 181 | 182 | /// Executes the specified view function from the specified design document. 183 | /// 184 | /// Unlike GET [executeViewFunction] for accessing views, the POST method 185 | /// supports the specification of explicit [keys] to be retrieved from the view results. 186 | /// 187 | /// Returns JSON like: 188 | /// ```json 189 | /// { 190 | /// "offset": 0, 191 | /// "rows": [ 192 | /// { 193 | /// "id": "SpaghettiWithMeatballs", 194 | /// "key": "meatballs", 195 | /// "value": 1 196 | /// }, 197 | /// { 198 | /// "id": "SpaghettiWithMeatballs", 199 | /// "key": "spaghetti", 200 | /// "value": 1 201 | /// } 202 | /// ], 203 | /// "total_rows": 3 204 | /// } 205 | /// ``` 206 | Future executeViewFunctionWithKeys( 207 | String dbName, String ddocId, String viewName, 208 | {@required List keys, 209 | bool conflicts = false, 210 | bool descending = false, 211 | Object endKey, 212 | String endKeyDocId, 213 | bool group = false, 214 | int groupLevel, 215 | bool includeDocs = false, 216 | bool attachments = false, 217 | bool attEncodingInfo = false, 218 | bool inclusiveEnd = true, 219 | Object key, 220 | int limit, 221 | bool reduce = false, // in implementing class 222 | int skip = 0, 223 | bool sorted = true, 224 | bool stable = false, 225 | String stale, 226 | Object startKey, 227 | String startKeyDocId, 228 | String update = 'true', 229 | bool updateSeq = false, 230 | Map headers}); 231 | 232 | /// Executes multiple specified view queries against the view function from the specified design document 233 | /// 234 | /// [queries] might have the same parameters as [executeViewFunctionWithKeys] or [executeViewFunction]. 235 | /// Multi-key fetchs for `reduce` views must use `group=true`. 236 | /// 237 | /// Returns JSON like: 238 | /// ```json 239 | /// { 240 | /// "results" : [ 241 | /// { 242 | /// "offset": 0, 243 | /// "rows": [ 244 | /// { 245 | /// "id": "SpaghettiWithMeatballs", 246 | /// "key": "meatballs", 247 | /// "value": 1 248 | /// }, 249 | /// { 250 | /// "id": "SpaghettiWithMeatballs", 251 | /// "key": "spaghetti", 252 | /// "value": 1 253 | /// }, 254 | /// { 255 | /// "id": "SpaghettiWithMeatballs", 256 | /// "key": "tomato sauce", 257 | /// "value": 1 258 | /// } 259 | /// ], 260 | /// "total_rows": 3 261 | /// }, 262 | /// { 263 | /// "offset" : 2, 264 | /// "rows" : [ 265 | /// { 266 | /// "id" : "Adukiandorangecasserole-microwave", 267 | /// "key" : "Aduki and orange casserole - microwave", 268 | /// "value" : [ 269 | /// null, 270 | /// "Aduki and orange casserole - microwave" 271 | /// ] 272 | /// }, 273 | /// { 274 | /// "id" : "Aioli-garlicmayonnaise", 275 | /// "key" : "Aioli - garlic mayonnaise", 276 | /// "value" : [ 277 | /// null, 278 | /// "Aioli - garlic mayonnaise" 279 | /// ] 280 | /// }, 281 | /// { 282 | /// "id" : "Alabamapeanutchicken", 283 | /// "key" : "Alabama peanut chicken", 284 | /// "value" : [ 285 | /// null, 286 | /// "Alabama peanut chicken" 287 | /// ] 288 | /// } 289 | /// ], 290 | /// "total_rows" : 2667 291 | /// } 292 | /// ] 293 | /// } 294 | /// ``` 295 | Future executeViewQueries( 296 | String dbName, String ddocId, String viewName, List queries); 297 | 298 | /// Applies show function for null document 299 | /// 300 | /// Returns responce like: 301 | /// 302 | /// `some data that returned by show function` 303 | /// 304 | Future executeShowFunctionForNull( 305 | String dbName, String ddocId, String funcName, 306 | {String format}); 307 | 308 | /// Applies show function for the specified document 309 | /// 310 | /// Returns responce like: 311 | /// 312 | /// `some data that returned by show function` 313 | /// 314 | Future executeShowFunctionForDocument( 315 | String dbName, String ddocId, String funcName, String docId, 316 | {String format}); 317 | 318 | /// Applies list function for the [view] function from the same design document 319 | /// 320 | /// Returns responce like: 321 | /// 322 | /// `some data that returned by show function` 323 | /// 324 | Future executeListFunctionForView( 325 | String dbName, String ddocId, String funcName, String view, 326 | {String format}); 327 | 328 | /// Applies list function for the [view] function from the other design document 329 | /// 330 | /// Returns responce like: 331 | /// 332 | /// `some data that returned by show function` 333 | /// 334 | Future executeListFunctionForViewFromDoc( 335 | String dbName, 336 | String ddocId, 337 | String funcName, 338 | String otherDoc, 339 | String view, 340 | {String format}); 341 | 342 | /// Executes update function on server side for null document 343 | /// 344 | /// Update function must return JSON like: 345 | /// ```json 346 | /// {"status": "ok"} 347 | /// ``` 348 | /// for success exucution or error for fail. 349 | Future executeUpdateFunctionForNull( 350 | String dbName, String ddocId, String funcName, Object body); 351 | 352 | /// Executes update function on server side for the specified document 353 | Future executeUpdateFunctionForDocument( 354 | String dbName, String ddocId, String funcName, String docId, Object body); 355 | 356 | /// Rewrites the specified path by rules defined in the specified design document 357 | /// 358 | /// The rewrite rules are defined by the `rewrites` field of the design document. 359 | /// The `rewrites` field can either be a string containing the a rewrite function or an array of rule definitions. 360 | Future rewritePath( 361 | String dbName, String ddocId, String path); 362 | } 363 | -------------------------------------------------------------------------------- /lib/src/interfaces/documents_interface.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../responses/api_response.dart'; 4 | import '../responses/documents_response.dart'; 5 | 6 | /// Class that define methods for create, read, update and delete documents within a database 7 | abstract class DocumentsInterface { 8 | 9 | /// Returns the HTTP Headers containing a minimal amount of information about the specified document 10 | Future docInfo(String dbName, String docId, 11 | {Map headers, 12 | bool attachments = false, 13 | bool attEncodingInfo = false, 14 | List attsSince, 15 | bool conflicts = false, 16 | bool deletedConflicts = false, 17 | bool latest = false, 18 | bool localSeq = false, 19 | bool meta = false, 20 | Object openRevs, 21 | String rev, 22 | bool revs = false, 23 | bool revsInfo = false}); 24 | 25 | /// Returns document by the specified [docId] from the specified [dbName] 26 | /// 27 | /// Returns JSON like: 28 | /// ```json 29 | /// { 30 | /// "_id": "SpaghettiWithMeatballs", 31 | /// "_rev": "1-917fa2381192822767f010b95b45325b", 32 | /// "description": "An Italian-American dish that usually consists of spaghetti, tomato sauce and meatballs.", 33 | /// "ingredients": [ 34 | /// "spaghetti", 35 | /// "tomato sauce", 36 | /// "meatballs" 37 | /// ], 38 | /// "name": "Spaghetti with meatballs" 39 | /// } 40 | /// ``` 41 | Future doc(String dbName, String docId, 42 | {Map headers, 43 | bool attachments = false, 44 | bool attEncodingInfo = false, 45 | List attsSince, 46 | bool conflicts = false, 47 | bool deletedConflicts = false, 48 | bool latest = false, 49 | bool localSeq = false, 50 | bool meta = false, 51 | Object openRevs, 52 | String rev, 53 | bool revs = false, 54 | bool revsInfo = false}); 55 | 56 | /// Creates a new named document, or creates a new revision of the existing document 57 | /// 58 | /// Returns JSON like: 59 | /// ```json 60 | /// { 61 | /// "id": "SpaghettiWithMeatballs", 62 | /// "ok": true, 63 | /// "rev": "1-917fa2381192822767f010b95b45325b" 64 | /// } 65 | /// ``` 66 | Future insertDoc( 67 | String dbName, String docId, Map body, 68 | {Map headers, 69 | String rev, 70 | String batch, 71 | bool newEdits = true}); 72 | 73 | /// Marks the specified document as deleted by adding a field `_deleted` with the value `true` 74 | /// 75 | /// Returns JSON like: 76 | /// ```json 77 | /// { 78 | /// "id": "SpaghettiWithMeatballs", 79 | /// "ok": true, 80 | /// "rev": "1-917fa2381192822767f010b95b45325b" 81 | /// } 82 | /// ``` 83 | Future deleteDoc( 84 | String dbName, String docId, String rev, 85 | {Map headers, String batch}); 86 | 87 | /// Copies an existing document to a new or existing document. 88 | /// 89 | /// If you are copying to an existing document, you must specify 90 | /// `destinationRev` as the current rev of the destination doc 91 | /// 92 | /// Returns JSON like: 93 | /// ```json 94 | /// { 95 | /// "id": "SpaghettiWithMeatballs", 96 | /// "ok": true, 97 | /// "rev": "1-917fa2381192822767f010b95b45325b" 98 | /// } 99 | /// ``` 100 | Future copyDoc( 101 | String dbName, String docId, String destinationId, 102 | {Map headers, 103 | String rev, 104 | String destinationRev, 105 | String batch}); 106 | 107 | /// Returns the HTTP headers containing a minimal amount of information about the specified attachment 108 | Future attachmentInfo( 109 | String dbName, String docId, String attName, 110 | {Map headers, String rev}); 111 | 112 | /// Returns the file attachment associated with the document 113 | /// 114 | /// Result is available in [DocumentsResponse.attachment] or [ApiResponse.raw] field as bytes of data. 115 | Future attachment( 116 | String dbName, String docId, String attName, 117 | {Map headers, String rev}); 118 | 119 | /// Uploads the supplied content as an attachment to the specified document 120 | /// 121 | /// You must supply the `Content-Type` header, and for an existing document 122 | /// you must also supply either the [rev] query argument or the `If-Match` HTTP header. 123 | /// 124 | /// Returns JSON like: 125 | /// ```json 126 | /// { 127 | /// "id": "SpaghettiWithMeatballs", 128 | /// "ok": true, 129 | /// "rev": "1-917fa2381192822767f010b95b45325b" 130 | /// } 131 | /// ``` 132 | Future uploadAttachment( 133 | String dbName, String docId, String attName, Object body, 134 | {Map headers, String rev}); 135 | 136 | /// Deletes the attachment with filename [attName] of the specified [docId] 137 | /// 138 | /// Returns JSON like: 139 | /// ```json 140 | /// { 141 | /// "id": "SpaghettiWithMeatballs", 142 | /// "ok": true, 143 | /// "rev": "1-917fa2381192822767f010b95b45325b" 144 | /// } 145 | /// ``` 146 | Future deleteAttachment( 147 | String dbName, String docId, String attName, 148 | {@required String rev, Map headers, String batch}); 149 | } 150 | -------------------------------------------------------------------------------- /lib/src/interfaces/local_documents_interface.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../responses/local_documents_response.dart'; 4 | 5 | /// The Local (non-replicating) document interface allows to create local documents 6 | /// that are not replicated to other databases 7 | /// 8 | /// Local documents don't store attachments. 9 | abstract class LocalDocumentsInterface { 10 | 11 | /// Returns a JSON structure of all of the local documents in a given database 12 | /// 13 | /// Returns JSON like: 14 | /// ```json 15 | /// { 16 | /// "offset": null, 17 | /// "rows": [ 18 | /// { 19 | /// "id": "_local/localdoc01", 20 | /// "key": "_local/localdoc01", 21 | /// "value": { 22 | /// "rev": "0-1" 23 | /// } 24 | /// }, 25 | /// { 26 | /// "id": "_local/localdoc02", 27 | /// "key": "_local/localdoc02", 28 | /// "value": { 29 | /// "rev": "0-1" 30 | /// } 31 | /// }, 32 | /// { 33 | /// "id": "_local/localdoc03", 34 | /// "key": "_local/localdoc03", 35 | /// "value": { 36 | /// "rev": "0-1" 37 | /// } 38 | /// }, 39 | /// { 40 | /// "id": "_local/localdoc04", 41 | /// "key": "_local/localdoc04", 42 | /// "value": { 43 | /// "rev": "0-1" 44 | /// } 45 | /// }, 46 | /// { 47 | /// "id": "_local/localdoc05", 48 | /// "key": "_local/localdoc05", 49 | /// "value": { 50 | /// "rev": "0-1" 51 | /// } 52 | /// } 53 | /// ], 54 | /// "total_rows": null 55 | /// } 56 | /// ``` 57 | Future localDocs(String dbName, 58 | {bool conflicts = false, 59 | bool descending = false, 60 | String endKey, 61 | String endKeyDocId, 62 | bool includeDocs = false, 63 | bool inclusiveEnd = true, 64 | String key, 65 | List keys, 66 | int limit, 67 | int skip = 0, 68 | String startKey, 69 | String startKeyDocId, 70 | bool updateSeq = false, 71 | Map headers}); 72 | 73 | /// Requests multiple local documents in a single request specifying multiple [keys] 74 | /// 75 | /// Returns JSON like: 76 | /// ```json 77 | /// { 78 | /// "total_rows" : null, 79 | /// "rows" : [ 80 | /// { 81 | /// "value" : { 82 | /// "rev" : "0-1" 83 | /// }, 84 | /// "id" : "_local/localdoc02", 85 | /// "key" : "_local/localdoc02" 86 | /// }, 87 | /// { 88 | /// "value" : { 89 | /// "rev" : "0-1" 90 | /// }, 91 | /// "id" : "_local/localdoc05", 92 | /// "key" : "_local/localdoc05" 93 | /// } 94 | /// ], 95 | /// "offset" : null 96 | /// } 97 | /// ``` 98 | Future localDocsWithKeys(String dbName, 99 | {@required List keys, 100 | bool conflicts = false, 101 | bool descending = false, 102 | String endKey, 103 | String endKeyDocId, 104 | bool includeDocs = false, 105 | bool inclusiveEnd = true, 106 | String key, 107 | int limit, 108 | int skip = 0, 109 | String startKey, 110 | String startKeyDocId, 111 | bool updateSeq = false}); 112 | 113 | /// Gets the specified local document 114 | /// 115 | /// [docId] must match pattern - `_local/{id}` 116 | Future localDoc(String dbName, String docId, 117 | {Map headers, 118 | bool conflicts = false, 119 | bool deletedConflicts = false, 120 | bool latest = false, 121 | bool localSeq = false, 122 | bool meta = false, 123 | Object openRevs, 124 | String rev, 125 | bool revs = false, 126 | bool revsInfo = false}); 127 | 128 | /// Stores the specified local document 129 | /// 130 | /// [docId] must match pattern - `_local/{id}` 131 | Future putLocalDoc( 132 | String dbName, String docId, Map body, 133 | {Map headers, 134 | String rev, 135 | String batch, 136 | bool newEdits = true}); 137 | 138 | /// Deletes the specified local document 139 | /// 140 | /// [docId] must match pattern - `_local/{id}` 141 | Future deleteLocalDoc( 142 | String dbName, String docId, String rev, 143 | {Map headers, String batch}); 144 | 145 | /// Copies the specified local document 146 | /// 147 | /// [docId] must match pattern - `_local/{id}` 148 | Future copyLocalDoc(String dbName, String docId, 149 | {Map headers, String rev, String batch}); 150 | } 151 | -------------------------------------------------------------------------------- /lib/src/interfaces/server_interface.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../responses/server_response.dart'; 4 | 5 | /// Server interface provides the basic interface to a CouchDB server 6 | /// for obtaining CouchDB information and getting and setting configuration information 7 | abstract class ServerInterface { 8 | 9 | /// Accessing the root of a CouchDB instance returns meta information about the instance 10 | /// 11 | /// Returns JSON like: 12 | /// ```json 13 | /// { 14 | /// "couchdb": "Welcome", 15 | /// "uuid": "85fb71bf700c17267fef77535820e371", 16 | /// "vendor": { 17 | /// "name": "The Apache Software Foundation", 18 | /// "version": "1.3.1" 19 | /// }, 20 | /// "version": "1.3.1" 21 | /// } 22 | /// ``` 23 | Future couchDbInfo({Map headers}); 24 | 25 | /// List of running tasks, including the task type, name, status and process ID 26 | /// 27 | /// Returns JSON like: 28 | /// ```json 29 | /// [ 30 | /// { 31 | /// "changes_done": 64438, 32 | /// "database": "mailbox", 33 | /// "pid": "<0.12986.1>", 34 | /// "progress": 84, 35 | /// "started_on": 1376116576, 36 | /// "total_changes": 76215, 37 | /// "type": "database_compaction", 38 | /// "updated_on": 1376116619 39 | /// }, 40 | /// { 41 | /// "checkpointed_source_seq": 68585, 42 | /// "continuous": false, 43 | /// "doc_id": null, 44 | /// "doc_write_failures": 0, 45 | /// "docs_read": 4524, 46 | /// "docs_written": 4524, 47 | /// "missing_revisions_found": 4524, 48 | /// "pid": "<0.1538.5>", 49 | /// "progress": 44, 50 | /// "replication_id": "9bc1727d74d49d9e157e260bb8bbd1d5", 51 | /// "revisions_checked": 4524, 52 | /// "source": "mailbox", 53 | /// "source_seq": 154419, 54 | /// "started_on": 1376116644, 55 | /// "target": "http://mailsrv:5984/mailbox", 56 | /// "type": "replication", 57 | /// "updated_on": 1376116651 58 | /// } 59 | /// ] 60 | /// ``` 61 | Future activeTasks({Map headers}); 62 | 63 | /// Returns a list of all the databases in the CouchDB instance 64 | /// 65 | /// Returns JSON like: 66 | /// ```json 67 | /// [ 68 | /// "_users", 69 | /// "contacts", 70 | /// "docs", 71 | /// "invoices", 72 | /// "locations" 73 | /// ] 74 | /// ``` 75 | Future allDbs({Map headers}); 76 | 77 | /// Returns information of a list of the specified databases in the CouchDB instance 78 | /// 79 | /// Returns JSON like: 80 | /// ```json 81 | /// [ 82 | /// { 83 | /// "key": "animals", 84 | /// "info": { 85 | /// "db_name": "animals", 86 | /// "update_seq": "52232", 87 | /// "sizes": { 88 | /// "file": 1178613587, 89 | /// "external": 1713103872, 90 | /// "active": 1162451555 91 | /// }, 92 | /// "purge_seq": 0, 93 | /// "other": { 94 | /// "data_size": 1713103872 95 | /// }, 96 | /// "doc_del_count": 0, 97 | /// "doc_count": 52224, 98 | /// "disk_size": 1178613587, 99 | /// "disk_format_version": 6, 100 | /// "data_size": 1162451555, 101 | /// "compact_running": false, 102 | /// "cluster": { 103 | /// "q": 8, 104 | /// "n": 3, 105 | /// "w": 2, 106 | /// "r": 2 107 | /// }, 108 | /// "instance_start_time": "0" 109 | /// } 110 | /// }, 111 | /// { 112 | /// "key": "plants", 113 | /// "info": { 114 | /// "db_name": "plants", 115 | /// "update_seq": "303", 116 | /// "sizes": { 117 | /// "file": 3872387, 118 | /// "external": 2339, 119 | /// "active": 67475 120 | /// }, 121 | /// "purge_seq": 0, 122 | /// "other": { 123 | /// "data_size": 2339 124 | /// }, 125 | /// "doc_del_count": 0, 126 | /// "doc_count": 11, 127 | /// "disk_size": 3872387, 128 | /// "disk_format_version": 6, 129 | /// "data_size": 67475, 130 | /// "compact_running": false, 131 | /// "cluster": { 132 | /// "q": 8, 133 | /// "n": 3, 134 | /// "w": 2, 135 | /// "r": 2 136 | /// }, 137 | /// "instance_start_time": "0" 138 | /// } 139 | /// } 140 | /// ] 141 | /// ``` 142 | Future dbsInfo(List keys); 143 | 144 | /// Returns the status of the node or cluster, per the cluster setup wizard 145 | /// 146 | /// If [ensureDbsExist] isn't specified, it is defaults to `["_users","_replicator","_global_changes"]`. 147 | /// Return JSON like: 148 | /// ```json 149 | /// {"state": "cluster_enabled"} 150 | /// ``` 151 | /// May be one of the following: 152 | /// - `cluster_disabled`, 153 | /// - `single_node_disabled`, 154 | /// - `single_node_enabled`, 155 | /// - `cluster_enabled`, 156 | /// - `cluster_finished` 157 | Future clusterSetupStatus( 158 | {List ensureDbsExist, Map headers}); 159 | 160 | /// Configure a node as a single (standalone) node, as part of a cluster, or finalise a cluster 161 | /// 162 | /// Correspond to `POST /_cluster_setup` method. 163 | /// If [ensureDbsExist] isn't specified, it is defaults to `["_users","_replicator","_global_changes"]`. 164 | /// [bindAdress] should be provided not [host], if CouchDB is configuring as `single_node`. 165 | Future configureCouchDb( 166 | {@required String action, 167 | @required String bindAdress, 168 | @required String username, 169 | @required String password, 170 | @required int port, 171 | int nodeCount, 172 | String remoteNode, 173 | String remoteCurrentUser, 174 | String remoteCurrentPassword, 175 | String host, 176 | List ensureDbsExist, 177 | Map headers}); 178 | 179 | /// Returns a list of all database events in the CouchDB instance 180 | /// 181 | /// Returns JSON like: 182 | /// ```json 183 | /// { 184 | /// "results": [ 185 | /// { 186 | /// "db_name": "mailbox", 187 | /// "type": "created", 188 | /// "seq": "1-g1AAAAFReJzLYWBg4MhgTmHgzcvPy09JdcjLz8gvLskBCjMlMiTJ____PyuDOZExFyjAnmJhkWaeaIquGIf2JAUgmWQPMiGRAZcaB5CaePxqEkBq6vGqyWMBkgwNQAqobD4h" 189 | /// }, 190 | /// { 191 | /// "db_name": "mailbox", 192 | /// "type": "deleted", 193 | /// "seq": "2-g1AAAAFReJzLYWBg4MhgTmHgzcvPy09JdcjLz8gvLskBCjMlMiTJ____PyuDOZEpFyjAnmJhkWaeaIquGIf2JAUgmWQPMiGRAZcaB5CaePxqEkBq6vGqyWMBkgwNQAqobD4hdQsg6vYTUncAou4-IXUPIOpA7ssCAIFHa60" 194 | /// }, 195 | /// ], 196 | /// "last_seq": "2-g1AAAAFReJzLYWBg4MhgTmHgzcvPy09JdcjLz8gvLskBCjMlMiTJ____PyuDOZEpFyjAnmJhkWaeaIquGIf2JAUgmWQPMiGRAZcaB5CaePxqEkBq6vGqyWMBkgwNQAqobD4hdQsg6vYTUncAou4-IXUPIOpA7ssCAIFHa60" 197 | /// } 198 | /// ``` 199 | Future dbUpdates( 200 | {String feed = 'normal', 201 | int timeout = 60, 202 | int heartbeat = 60000, 203 | String since, 204 | Map headers}); 205 | 206 | /// Displays the nodes that are part of the cluster as `cluster_nodes`\ 207 | /// 208 | /// Returns JSON like: 209 | /// ```json 210 | /// { 211 | /// "all_nodes": [ 212 | /// "node1@127.0.0.1", 213 | /// "node2@127.0.0.1", 214 | /// "node3@127.0.0.1" 215 | /// ], 216 | /// "cluster_nodes": [ 217 | /// "node1@127.0.0.1", 218 | /// "node2@127.0.0.1", 219 | /// "node3@127.0.0.1" 220 | /// ] 221 | /// } 222 | /// ``` 223 | Future membership({Map headers}); 224 | 225 | /// Request, configure, or stop, a replication operation 226 | /// 227 | /// Returns JSON like: 228 | /// ```json 229 | /// { 230 | /// "history": [ 231 | /// { 232 | /// "doc_write_failures": 0, 233 | /// "docs_read": 10, 234 | /// "docs_written": 10, 235 | /// "end_last_seq": 28, 236 | /// "end_time": "Sun, 11 Aug 2013 20:38:50 GMT", 237 | /// "missing_checked": 10, 238 | /// "missing_found": 10, 239 | /// "recorded_seq": 28, 240 | /// "session_id": "142a35854a08e205c47174d91b1f9628", 241 | /// "start_last_seq": 1, 242 | /// "start_time": "Sun, 11 Aug 2013 20:38:50 GMT" 243 | /// }, 244 | /// { 245 | /// "doc_write_failures": 0, 246 | /// "docs_read": 1, 247 | /// "docs_written": 1, 248 | /// "end_last_seq": 1, 249 | /// "end_time": "Sat, 10 Aug 2013 15:41:54 GMT", 250 | /// "missing_checked": 1, 251 | /// "missing_found": 1, 252 | /// "recorded_seq": 1, 253 | /// "session_id": "6314f35c51de3ac408af79d6ee0c1a09", 254 | /// "start_last_seq": 0, 255 | /// "start_time": "Sat, 10 Aug 2013 15:41:54 GMT" 256 | /// } 257 | /// ], 258 | /// "ok": true, 259 | /// "replication_id_version": 3, 260 | /// "session_id": "142a35854a08e205c47174d91b1f9628", 261 | /// "source_last_seq": 28 262 | /// } 263 | /// ``` 264 | Future replicate( 265 | {bool cancel, 266 | bool continuous, 267 | bool createTarget, 268 | List docIds, 269 | String filterFunJS, 270 | String proxy, 271 | Object source, 272 | Object target, 273 | Map headers}); 274 | 275 | /// List of replication jobs 276 | /// 277 | /// Returns JSON like: 278 | /// ```json 279 | /// { 280 | /// "jobs": [ 281 | /// { 282 | /// "database": "_replicator", 283 | /// "doc_id": "cdyno-0000001-0000003", 284 | /// "history": [ 285 | /// { 286 | /// "timestamp": "2017-04-29T05:01:37Z", 287 | /// "type": "started" 288 | /// }, 289 | /// { 290 | /// "timestamp": "2017-04-29T05:01:37Z", 291 | /// "type": "added" 292 | /// } 293 | /// ], 294 | /// "id": "8f5b1bd0be6f9166ccfd36fc8be8fc22+continuous", 295 | /// "node": "node1@127.0.0.1", 296 | /// "pid": "<0.1850.0>", 297 | /// "source": "http://myserver.com/foo", 298 | /// "start_time": "2017-04-29T05:01:37Z", 299 | /// "target": "http://adm:*****@localhost:15984/cdyno-0000003/", 300 | /// "user": null 301 | /// } 302 | /// ], 303 | /// "offset": 0, 304 | /// "total_rows": 2 305 | /// } 306 | /// ``` 307 | Future schedulerJobs({int limit, int skip}); 308 | 309 | /// List of replication document states 310 | /// 311 | /// Returns JSON like: 312 | /// ```json 313 | /// { 314 | /// "docs": [ 315 | /// { 316 | /// "database": "_replicator", 317 | /// "doc_id": "cdyno-0000001-0000002", 318 | /// "error_count": 0, 319 | /// "id": "e327d79214831ca4c11550b4a453c9ba+continuous", 320 | /// "info": null, 321 | /// "last_updated": "2017-04-29T05:01:37Z", 322 | /// "node": "node2@127.0.0.1", 323 | /// "proxy": null, 324 | /// "source": "http://myserver.com/foo", 325 | /// "start_time": "2017-04-29T05:01:37Z", 326 | /// "state": "running", 327 | /// "target": "http://adm:*****@localhost:15984/cdyno-0000002/" 328 | /// } 329 | /// ], 330 | /// "offset": 0, 331 | /// "total_rows": 2 332 | /// } 333 | /// ``` 334 | Future schedulerDocs({int limit, int skip}); 335 | 336 | /// Gets information about replication documents from a [replicator] database 337 | /// 338 | /// The default [replicator] database is `_replicator` but other [replicator] databases can exist 339 | /// if their name ends with the suffix `/_replicator`. 340 | /// 341 | /// Returns JSON like: 342 | /// ```json 343 | /// { 344 | /// "docs": [ 345 | /// { 346 | /// "database": "other/_replicator", 347 | /// "doc_id": "cdyno-0000001-0000002", 348 | /// "error_count": 0, 349 | /// "id": "e327d79214831ca4c11550b4a453c9ba+continuous", 350 | /// "info": null, 351 | /// "last_updated": "2017-04-29T05:01:37Z", 352 | /// "node": "node2@127.0.0.1", 353 | /// "proxy": null, 354 | /// "source": "http://myserver.com/foo", 355 | /// "start_time": "2017-04-29T05:01:37Z", 356 | /// "state": "running", 357 | /// "target": "http://adm:*****@localhost:15984/cdyno-0000002/" 358 | /// } 359 | /// ], 360 | /// "offset": 0, 361 | /// "total_rows": 1 362 | /// } 363 | /// ``` 364 | Future schedulerDocsWithReplicatorDbName( 365 | {String replicator = '_replicator', int limit, int skip}); 366 | 367 | /// Gets information about replication document from a [replicator] database 368 | /// 369 | /// The default [replicator] database is `_replicator` but other [replicator] databases can exist 370 | /// if their name ends with the suffix `/_replicator`. 371 | /// 372 | /// Returns JSON like: 373 | /// ```json 374 | /// { 375 | /// "database": "other/_replicator", 376 | /// "doc_id": "cdyno-0000001-0000002", 377 | /// "error_count": 0, 378 | /// "id": "e327d79214831ca4c11550b4a453c9ba+continuous", 379 | /// "info": null, 380 | /// "last_updated": "2017-04-29T05:01:37Z", 381 | /// "node": "node2@127.0.0.1", 382 | /// "proxy": null, 383 | /// "source": "http://myserver.com/foo", 384 | /// "start_time": "2017-04-29T05:01:37Z", 385 | /// "state": "running", 386 | /// "target": "http://adm:*****@localhost:15984/cdyno-0000002/" 387 | /// } 388 | /// ``` 389 | Future schedulerDocsWithDocId(String docId, 390 | {String replicator = '_replicator'}); 391 | 392 | /// Returns a JSON object containing the statistics for the running server 393 | /// 394 | /// [statisticSection] may be one of: 395 | /// 396 | /// 1. couch_log: Logging subsystem 397 | /// 2. couch_replicator: Replication scheduler and subsystem 398 | /// 3. couchdb: Primary CouchDB database operations 399 | /// 4. fabric: Cluster-related operations 400 | /// 5. global_changes: Global changes feed 401 | /// 6. mem3: Node membership-related statistics 402 | /// 7. pread: CouchDB file-related exceptions 403 | /// 8. rexi: Cluster internal RPC-related statistics 404 | /// 405 | /// and [statisticId] is individual part of [statisticSection]. 406 | /// 407 | /// JSON may be like if [statisticSection] and [statisticId] are provided: 408 | /// ```json 409 | /// { 410 | /// "value": { 411 | /// "min": 0, 412 | /// "max": 0, 413 | /// "arithmetic_mean": 0, 414 | /// "geometric_mean": 0, 415 | /// "harmonic_mean": 0, 416 | /// "median": 0, 417 | /// "variance": 0, 418 | /// "standard_deviation": 0, 419 | /// "skewness": 0, 420 | /// "kurtosis": 0, 421 | /// "percentile": [ 422 | /// [ 423 | /// 50, 424 | /// 0 425 | /// ], 426 | /// [ 427 | /// 75, 428 | /// 0 429 | /// ], 430 | /// [ 431 | /// 90, 432 | /// 0 433 | /// ], 434 | /// [ 435 | /// 95, 436 | /// 0 437 | /// ], 438 | /// [ 439 | /// 99, 440 | /// 0 441 | /// ], 442 | /// [ 443 | /// 999, 444 | /// 0 445 | /// ] 446 | /// ], 447 | /// "histogram": [ 448 | /// [ 449 | /// 0, 450 | /// 0 451 | /// ] 452 | /// ], 453 | /// "n": 0 454 | /// }, 455 | /// "type": "histogram", 456 | /// "desc": "length of a request inside CouchDB without MochiWeb" 457 | /// } 458 | /// ``` 459 | Future nodeStats( 460 | {String nodeName = '_local', 461 | String statisticSection, 462 | String statisticId, 463 | Map headers}); 464 | 465 | /// Returns a JSON object containing various system-level statistics for the running server 466 | /// 467 | /// **These statistics are generally intended for CouchDB developers only.** 468 | Future systemStatsForNode( 469 | {String nodeName = '_local', Map headers}); 470 | 471 | // /// Accesses the built-in Fauxton administration interface for CouchDB. 472 | // /// 473 | // /// Don't work in browser environment! 474 | // Future utils(); 475 | 476 | /// Confirms that the server is up, running, and ready to respond to requests 477 | /// 478 | /// Returns JSON like: 479 | /// ```json 480 | /// {"status": "ok"} 481 | /// ``` 482 | Future up(); 483 | 484 | /// Requests one or more Universally Unique Identifiers (UUIDs) from the CouchDB instance 485 | /// 486 | /// Returns JSON like: 487 | /// ```json 488 | /// { 489 | /// "uuids": [ 490 | /// "75480ca477454894678e22eec6002413", 491 | /// "75480ca477454894678e22eec600250b", 492 | /// "75480ca477454894678e22eec6002c41", 493 | /// "75480ca477454894678e22eec6003b90", 494 | /// "75480ca477454894678e22eec6003fca", 495 | /// "75480ca477454894678e22eec6004bef", 496 | /// "75480ca477454894678e22eec600528f", 497 | /// "75480ca477454894678e22eec6005e0b", 498 | /// "75480ca477454894678e22eec6006158", 499 | /// "75480ca477454894678e22eec6006161" 500 | /// ] 501 | /// } 502 | /// ``` 503 | Future uuids( 504 | {int count = 1, Map headers}); 505 | 506 | // /// Binary content for the favicon.ico site icon 507 | // /// Returns 'Not found' if favicon isn't exist. 508 | // /// Throws `FormatException` all time. **Don't use it!** 509 | // Future favicon(); 510 | } 511 | -------------------------------------------------------------------------------- /lib/src/local_documents.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import 'interfaces/client_interface.dart'; 4 | import 'interfaces/local_documents_interface.dart'; 5 | import 'responses/api_response.dart'; 6 | import 'responses/local_documents_response.dart'; 7 | import 'utils/includer_path.dart'; 8 | 9 | /// The Local (non-replicating) document interface allows to create local documents 10 | /// that are not replicated to other databases 11 | class LocalDocuments implements LocalDocumentsInterface { 12 | /// Instance of connected client 13 | final ClientInterface _client; 14 | 15 | /// Create LocalDocuments by accepting web-based or server-based client 16 | LocalDocuments(this._client); 17 | 18 | @override 19 | Future localDocs(String dbName, 20 | {bool conflicts = false, 21 | bool descending = false, 22 | String endKey, 23 | String endKeyDocId, 24 | bool includeDocs = false, 25 | bool inclusiveEnd = true, 26 | String key, 27 | List keys, 28 | int limit, 29 | int skip = 0, 30 | String startKey, 31 | String startKeyDocId, 32 | bool updateSeq = false, 33 | Map headers}) async { 34 | final path = 35 | '$dbName/_local_docs?conflicts=$conflicts&descending=$descending&' 36 | '${includeNonNullParam('endkey', endKey)}&${includeNonNullParam('endkey_docid', endKeyDocId)}&' 37 | 'include_docs=$includeDocs&inclusive_end=$inclusiveEnd&${includeNonNullParam('key', key)}&' 38 | '${includeNonNullParam('keys', keys)}&${includeNonNullParam('limit', limit)}&' 39 | 'skip=$skip&${includeNonNullParam('startkey', startKey)}&' 40 | '${includeNonNullParam('startkey_docid', startKeyDocId)}&update_seq=$updateSeq'; 41 | 42 | ApiResponse result = await _client.get(path, reqHeaders: headers); 43 | return LocalDocumentsResponse.from(result); 44 | } 45 | 46 | @override 47 | Future localDocsWithKeys(String dbName, 48 | {@required List keys, 49 | bool conflicts = false, 50 | bool descending = false, 51 | String endKey, 52 | String endKeyDocId, 53 | bool includeDocs = false, 54 | bool inclusiveEnd = true, 55 | String key, 56 | int limit, 57 | int skip = 0, 58 | String startKey, 59 | String startKeyDocId, 60 | bool updateSeq = false}) async { 61 | final path = 62 | '$dbName/_local_docs?conflicts=$conflicts&descending=$descending&' 63 | '${includeNonNullParam('endkey', endKey)}&${includeNonNullParam('endkey_docid', endKeyDocId)}&' 64 | 'include_docs=$includeDocs&inclusive_end=$inclusiveEnd&${includeNonNullParam('key', key)}&' 65 | '${includeNonNullParam('limit', limit)}&' 66 | 'skip=$skip&${includeNonNullParam('startkey', startKey)}&' 67 | '${includeNonNullParam('startkey_docid', startKeyDocId)}&update_seq=$updateSeq'; 68 | final body = >{'keys': keys}; 69 | 70 | ApiResponse result = await _client.post(path, body: body); 71 | return LocalDocumentsResponse.from(result); 72 | } 73 | 74 | @override 75 | Future localDoc(String dbName, String docId, 76 | {Map headers, 77 | bool conflicts = false, 78 | bool deletedConflicts = false, 79 | bool latest = false, 80 | bool localSeq = false, 81 | bool meta = false, 82 | Object openRevs, 83 | String rev, 84 | bool revs = false, 85 | bool revsInfo = false}) async { 86 | final path = 87 | '$dbName/$docId?conflicts=$conflicts&deleted_conflicts=$deletedConflicts&' 88 | 'latest=$latest&local_seq=$localSeq&meta=$meta&${includeNonNullParam('open_revs', openRevs)}&' 89 | '${includeNonNullParam('rev', rev)}&revs=$revs&revs_info=$revsInfo'; 90 | 91 | ApiResponse result = await _client.get(path, reqHeaders: headers); 92 | return LocalDocumentsResponse.from(result); 93 | } 94 | 95 | @override 96 | Future copyLocalDoc(String dbName, String docId, 97 | {Map headers, String rev, String batch}) async { 98 | final path = '$dbName/$docId?${includeNonNullParam('rev', rev)}&' 99 | '${includeNonNullParam('batch', batch)}'; 100 | 101 | ApiResponse result = await _client.copy(path, reqHeaders: headers); 102 | return LocalDocumentsResponse.from(result); 103 | } 104 | 105 | @override 106 | Future deleteLocalDoc( 107 | String dbName, String docId, String rev, 108 | {Map headers, String batch}) async { 109 | final path = 110 | '$dbName/$docId?rev=$rev&${includeNonNullParam('batch', batch)}'; 111 | 112 | ApiResponse result = await _client.delete(path, reqHeaders: headers); 113 | return LocalDocumentsResponse.from(result); 114 | } 115 | 116 | @override 117 | Future putLocalDoc( 118 | String dbName, String docId, Map body, 119 | {Map headers, 120 | String rev, 121 | String batch, 122 | bool newEdits = true}) async { 123 | final path = 124 | '$dbName/$docId?new_edits=$newEdits&${includeNonNullParam('rev', rev)}&' 125 | '${includeNonNullParam('batch', batch)}'; 126 | 127 | ApiResponse result = 128 | await _client.put(path, reqHeaders: headers, body: body); 129 | return LocalDocumentsResponse.from(result); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /lib/src/models/database_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:couchdb/couchdb.dart'; 2 | 3 | /// Only provided for backward compatibility 4 | @Deprecated('Use Databases instead') 5 | class DatabaseModel extends Databases { 6 | DatabaseModel(ClientInterface client) : super(client); 7 | } -------------------------------------------------------------------------------- /lib/src/models/design_document_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:couchdb/couchdb.dart'; 2 | 3 | /// Only provided for backward compatibility 4 | @Deprecated('Use DesignDocuments instead') 5 | class DesignDocumentModel extends DesignDocuments { 6 | DesignDocumentModel(ClientInterface client) : super(client); 7 | } -------------------------------------------------------------------------------- /lib/src/models/document_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:couchdb/couchdb.dart'; 2 | 3 | /// Only provided for backward compatibility 4 | @Deprecated('Use Documents instead') 5 | class DocumentModel extends Documents { 6 | DocumentModel(ClientInterface client) : super(client); 7 | } -------------------------------------------------------------------------------- /lib/src/models/local_document_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:couchdb/couchdb.dart'; 2 | 3 | /// Only provided for backward compatibility 4 | @Deprecated('Use LocalDocuments instead') 5 | class LocalDocumentModel extends LocalDocuments { 6 | LocalDocumentModel(ClientInterface client) : super(client); 7 | } -------------------------------------------------------------------------------- /lib/src/models/server_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:couchdb/couchdb.dart'; 2 | 3 | /// Only provided for backward compatibility 4 | @Deprecated('Use Server instead') 5 | class ServerModel extends Server { 6 | ServerModel(ClientInterface client) : super(client); 7 | } -------------------------------------------------------------------------------- /lib/src/responses/api_response.dart: -------------------------------------------------------------------------------- 1 | import 'error_response.dart'; 2 | 3 | /// Base class that unit all responses of CouchDB 4 | class ApiResponse { 5 | /// Creates instance of [ApiResponse] with [raw] and [json] 6 | ApiResponse(this.json, {this.headers, this.raw}); 7 | 8 | /// Field that contain raw body of response 9 | final String raw; 10 | 11 | /// Field that contain json itself in order to grab custom fields 12 | final Map json; 13 | 14 | /// Headers of response 15 | final Map headers; 16 | 17 | /// Returns error response if exists, otherwise return `null` 18 | ErrorResponse errorResponse() { 19 | ErrorResponse e; 20 | if (isError()) { 21 | e = ErrorResponse(json['error'] as String, json['reason'] as String); 22 | } 23 | return e; 24 | } 25 | 26 | /// Check if this response is error 27 | bool isError() => json['error'] != null; 28 | 29 | @override 30 | String toString() => ''' 31 | json - $json 32 | raw - $raw 33 | '''; 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/responses/databases_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:couchdb/couchdb.dart'; 2 | 3 | import '../databases.dart'; 4 | 5 | /// Class that contains responses from `Databases` class 6 | class DatabasesResponse { 7 | /// Creates instance of [DatabasesResponse] 8 | DatabasesResponse({this.cluster, 9 | this.compactRunning, 10 | this.dbName, 11 | this.diskFormatVersion, 12 | this.docCount, 13 | this.docDelCount, 14 | this.purgeSeq, 15 | this.sizes, 16 | this.updateSeq, 17 | this.ok, 18 | this.id, 19 | this.rev, 20 | this.offset, 21 | this.rows, 22 | this.totalRows, 23 | this.results, 24 | this.docs, 25 | this.warning, 26 | this.executionStats, 27 | this.bookmark, 28 | this.result, 29 | this.name, 30 | this.indexes, 31 | this.index, 32 | this.selector, 33 | this.opts, 34 | this.limit, 35 | this.skip, 36 | this.fields, 37 | this.range, 38 | this.lastSeq, 39 | this.pending, 40 | this.admins, 41 | this.members, 42 | this.purged, 43 | this.missedRevs, 44 | this.revsDiff, 45 | this.list, 46 | this.shards, 47 | this.shardRange, 48 | this.nodes}); 49 | 50 | /// Returns response with fields that may be returned by `Databases` 51 | /// request methods 52 | DatabasesResponse.from(ApiResponse response) : this( 53 | cluster: (response.json['cluster'] as Map) 54 | ?.map((k, v) => MapEntry(k, v as int)), 55 | compactRunning: response.json['compact_running'] as bool, 56 | dbName: (response.json['db_name'] ?? response.json['dbname']) as String, 57 | diskFormatVersion: response.json['disk_format_version'] as int, 58 | docCount: response.json['doc_count'] as int, 59 | docDelCount: response.json['doc_del_count'] as int, 60 | purgeSeq: response.json['purge_seq'] as String, 61 | sizes: (response.json['sizes'] as Map) 62 | ?.map((k, v) => MapEntry(k, v as int)), 63 | updateSeq: response.json['update_seq'] as String, 64 | ok: response.json['ok'] as bool, 65 | id: response.json['id'] as String, 66 | rev: response.json['rev'] as String, 67 | offset: response.json['offset'] as int, 68 | rows: (response.json['rows'] as List) 69 | ?.map((e) => e as Map) 70 | ?.toList(), 71 | totalRows: response.json['total_rows'] as int, 72 | results: (response.json['results'] as List) 73 | ?.map((e) => e as Map) 74 | ?.toList(), 75 | docs: (response.json['docs'] as List) 76 | ?.map((e) => e as Map) 77 | ?.toList(), 78 | warning: response.json['warning'] as String, 79 | executionStats: (response.json['execution_stats'] as Map) 80 | ?.map((k, v) => MapEntry(k, v as num)), 81 | bookmark: response.json['bookmark'] as String, 82 | result: response.json['result'] as String, 83 | name: response.json['name'] as String, 84 | indexes: (response.json['indexes'] as List) 85 | ?.map((e) => e as Map) 86 | ?.toList(), 87 | index: response.json['index'] as Map, 88 | selector: (response.json['selector'] as Map)?.map((k, 89 | v) => 90 | MapEntry>(k, v as Map)), 91 | opts: response.json['opts'] as Map, 92 | limit: response.json['limit'] as int, 93 | skip: response.json['skip'] as int, 94 | fields: response.json['fields'] is String ? [ 95 | response.json['fields'] as String 96 | ] : (response.json['fields'] as List) 97 | ?.map((e) => e as String) 98 | ?.toList(), 99 | range: response.json['range'] is Map ? (response 100 | .json['range'] as Map)?.map((k, v) => 101 | MapEntry(k, v as List)) : null, 102 | lastSeq: response.json['last_seq'] as String, 103 | pending: response.json['pending'] as int, 104 | admins: (response.json['admins'] as Map)?.map((k, v) => 105 | MapEntry>( 106 | k, (v as List)?.map((e) => e as String)?.toList())), 107 | members: (response.json['members'] as Map)?.map((k, v) => 108 | MapEntry>( 109 | k, (v as List)?.map((e) => e as String)?.toList())), 110 | purged: (response.json['purged'] as Map)?.map((k, v) => 111 | MapEntry>>(k, 112 | (v as Map)?.map((k, v) => 113 | MapEntry>(k, (v as List) 114 | ?.map((e) => e as String) 115 | ?.toList())))), 116 | missedRevs: (response.json['missed_revs'] as Map)?.map((k, 117 | v) => MapEntry>( 118 | k, (v as List)?.map((e) => e as String)?.toList())), 119 | revsDiff: response.json.keys.every(RegExp('[a-z0-9-]{32,36}').hasMatch) 120 | ? response.json?.map((k, v) => 121 | MapEntry>>(k, 122 | (v as Map)?.map((k, v) => 123 | MapEntry>(k, 124 | (v as List)?.map((e) => e as String)?.toList())))) 125 | : null, 126 | list: (response.json['list'] as List)?.map((e) => 127 | e as Map)?.toList(), 128 | shards: (response.json['shards'] as Map)?.map((k, v) => 129 | MapEntry>( 130 | k, (v as List)?.map((v) => v as String)?.toList())), 131 | shardRange: response.json['range'] is String ? response 132 | .json['range'] as String : null, 133 | nodes: (response.json['nodes'] as List) 134 | ?.map((v) => v as String) 135 | ?.toList()); 136 | 137 | /// Holds cluster's info 138 | final Map cluster; 139 | 140 | /// Is true if the database compaction routine is operating on this database 141 | /// 142 | /// Returns by [Databases.dbInfo] 143 | final bool compactRunning; 144 | 145 | /// Holds the name of the database 146 | final String dbName; 147 | 148 | /// The version of the physical format used for the data when it 149 | /// is stored on disk 150 | final int diskFormatVersion; 151 | 152 | /// A count of the documents in the specified database 153 | final int docCount; 154 | 155 | /// Number of deleted documents 156 | final int docDelCount; 157 | 158 | /// An opaque string that describes the purge state of the database 159 | /// 160 | /// Do not rely on this string for counting the number of purge operations. 161 | final String purgeSeq; 162 | 163 | /// Sizes info returned by [Databases.dbInfo] 164 | final Map sizes; 165 | 166 | /// An opaque string that describes the state of the database 167 | /// 168 | /// Do not rely on this string for counting the number of updates. 169 | final String updateSeq; 170 | 171 | /// Holds operation status. Available in case of success 172 | final bool ok; 173 | 174 | /// Holds document ID 175 | final String id; 176 | 177 | /// Holds revision info 178 | final String rev; 179 | 180 | /// Holds counts of documents skipped by search? 181 | final int offset; 182 | 183 | /// List of documents returned by search 184 | final List> rows; 185 | 186 | /// Holds total number of documents returned by search 187 | final int totalRows; 188 | 189 | /// Holds result objects - one for each query 190 | /// 191 | /// Returned by [Databases.queriesDocsFrom], [Databases.bulkDocs], 192 | /// [Databases.changesIn] 193 | final List> results; 194 | 195 | /// Holds documents matching the search 196 | final List> docs; 197 | 198 | /// Holds execution warnings 199 | final String warning; 200 | 201 | /// Execution statistics info 202 | final Map executionStats; 203 | 204 | /// An opaque string used for paging 205 | final String bookmark; 206 | 207 | /// Flag to show whether the index was created or one already exists. 208 | /// Can be “created” or “exists” 209 | final String result; 210 | 211 | /// Holds name of the index created 212 | final String name; 213 | 214 | /// Holds array of index definitions 215 | final List> indexes; 216 | 217 | /// Index used to fulfill the query 218 | final Map index; 219 | 220 | /// Holds query selector used 221 | final Map> selector; 222 | 223 | /// Holds query options used 224 | final Map opts; 225 | 226 | /// Holds limit parameter used 227 | /// 228 | /// Returns by [Databases.purgedInfosLimit], [Databases.explain] 229 | final int limit; 230 | 231 | /// Holds skip parameter used 232 | final int skip; 233 | 234 | /// Fields to be returned by the query 235 | final List fields; 236 | 237 | /// Range parameters passed to the underlying view 238 | final Map> range; 239 | 240 | /// Last change update sequence info 241 | final String lastSeq; 242 | 243 | /// Count of remaining items in the feed 244 | final int pending; 245 | 246 | /// Holds list of users and/or roles that have admin rights 247 | final Map> admins; 248 | 249 | /// Holds list of users and/or roles that have member rights 250 | final Map> members; 251 | 252 | /// Mapping of document ID to list of purged revisions 253 | final Map>> purged; 254 | 255 | /// Holds mapping of document ID to list of missed revisions 256 | final Map> missedRevs; 257 | 258 | /// Holds revs diffs for specified document 259 | /// 260 | /// Returns by [Databases.revsDiff] 261 | final Map>> revsDiff; 262 | 263 | /// List of some objects (if JSON itself is list) 264 | /// 265 | /// Returned by [Databases.insertBulkDocs] 266 | final List> list; 267 | 268 | /// Mapping of shard ranges to individual shard replicas on each 269 | /// node in the cluster 270 | final Map> shards; 271 | 272 | /// The shard range in which the document is stored 273 | final String shardRange; 274 | 275 | /// List of nodes serving a replica of the shard 276 | final List nodes; 277 | } 278 | -------------------------------------------------------------------------------- /lib/src/responses/design_documents_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:couchdb/couchdb.dart'; 2 | 3 | import '../design_documents.dart'; 4 | 5 | /// Class that contains responses from `DesignDocumentModel` class 6 | class DesignDocumentsResponse { 7 | /// Creates instance of [DesignDocumentsResponse] 8 | DesignDocumentsResponse( 9 | {this.ddoc, 10 | this.ok, 11 | this.id, 12 | this.rev, 13 | this.attachment, 14 | this.conflicts, 15 | this.deleted, 16 | this.deletedConflicts, 17 | this.localSeq, 18 | this.revsInfo, 19 | this.revisions, 20 | this.name, 21 | this.viewIndex, 22 | this.offset, 23 | this.rows, 24 | this.totalRows, 25 | this.updateSeq, 26 | this.results, 27 | this.status, 28 | this.raw}); 29 | 30 | DesignDocumentsResponse.from(ApiResponse response) : this( 31 | ddoc: response.json, 32 | ok: response.json['ok'] as bool, 33 | id: (response.json['_id'] ?? response.json['id']) as String, 34 | rev: (response.json['_rev'] ?? response.json['rev']) as String, 35 | attachment: response.json['_attachments'] ?? response.raw, 36 | conflicts: (response.json['_conflicts'] as List) 37 | ?.map((e) => e as String) 38 | ?.toList(), 39 | deleted: response.json['_deleted'] as bool, 40 | deletedConflicts: (response.json['_deleted_conflicts'] as List) 41 | ?.map((e) => e as String) 42 | ?.toList(), 43 | localSeq: response.json['_local_seq'] as String, 44 | revsInfo: (response.json['_revs_info'] as List) 45 | ?.map((e) => e as Map) 46 | ?.toList(), 47 | revisions: response.json['_revisions'] as Map, 48 | name: response.json['name'] as String, 49 | viewIndex: response.json['view_index'] as Map, 50 | offset: response.json['offset'] as int, 51 | rows: (response.json['rows'] as List) 52 | ?.map((e) => e as Map) 53 | ?.toList(), 54 | totalRows: response.json['total_rows'] as int, 55 | updateSeq: response.json['update_seq'] as String, 56 | results: (response.json['results'] as List) 57 | ?.map((e) => e as Map) 58 | ?.toList(), 59 | status: response.json['status'] as String, 60 | raw: response.raw); 61 | 62 | /// Holds document object 63 | /// 64 | /// May contain: 65 | /// - `_id` (string) – Document ID 66 | /// - `_rev` (string) – Revision MVCC token 67 | /// - `_deleted` (boolean) – Deletion flag. Available if document was removed 68 | /// - `_attachments` (object) – Attachment’s stubs. Available if document has any attachments 69 | /// - `_conflicts` (array) – List of conflicted revisions. Available if requested with `conflicts=true` query parameter 70 | /// - `_deleted_conflicts` (array) – List of deleted conflicted revisions. Available if requested with `deleted_conflicts=true` query parameter 71 | /// - `_local_seq (string)` – Document’s update sequence in current database. Available if requested with `local_seq=true` query parameter 72 | /// - `_revs_info (array)` – List of objects with information about local revisions and their status. Available if requested with `open_revs` query parameter 73 | /// - `_revisions (object)` – List of local revision tokens without. Available if requested with `revs=true` query parameter 74 | /// 75 | /// This properties are listed separately in [DesignDocumentsResponse] and you can get them directly. 76 | /// 77 | /// Returns by [DesignDocuments.designDoc] 78 | final Map ddoc; 79 | 80 | /// Holds operation status. Available in case of success 81 | final bool ok; 82 | 83 | /// Holds document ID 84 | final String id; 85 | 86 | /// Holds revision info of document 87 | final String rev; 88 | 89 | /// Attachment's raw data 90 | final Object attachment; 91 | 92 | /// List of conflicted revisions 93 | final List conflicts; 94 | 95 | /// Deletion flag. Available if document was removed 96 | final bool deleted; 97 | 98 | /// List of deleted conflicted revisions 99 | final List deletedConflicts; 100 | 101 | /// Document’s update sequence in current database 102 | final String localSeq; 103 | 104 | /// List of objects with information about local revisions and their status 105 | final List> revsInfo; 106 | 107 | /// List of local revision tokens without 108 | final Map revisions; 109 | 110 | /// Holds design document name 111 | final String name; 112 | 113 | /// View index information 114 | final Map viewIndex; 115 | 116 | /// Holds offset where the document list started 117 | final int offset; 118 | 119 | /// List array of view row objects 120 | final List> rows; 121 | 122 | /// Holds number of documents in the database/view 123 | final int totalRows; 124 | 125 | /// Current update sequence for the database 126 | final String updateSeq; 127 | 128 | /// Holds an array of result objects - one for each query 129 | final List> results; 130 | 131 | /// Holds execution status 132 | /// 133 | /// Can be returned by [DesignDocuments.executeUpdateFunctionForNull] 134 | /// and [DesignDocuments.executeUpdateFunctionForDocument] 135 | final String status; 136 | 137 | /// Contains non-JSON body 138 | /// 139 | /// Can be returned by [DesignDocuments.executeShowFunctionForNull] 140 | final String raw; 141 | } 142 | -------------------------------------------------------------------------------- /lib/src/responses/documents_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:couchdb/couchdb.dart'; 2 | 3 | import '../documents.dart'; 4 | 5 | /// Class that contains responses from `Documents` class 6 | class DocumentsResponse { 7 | /// Creates instance of [DocumentsResponse] 8 | DocumentsResponse( 9 | {this.doc, 10 | this.ok, 11 | this.id, 12 | this.rev, 13 | this.attachment, 14 | this.conflicts, 15 | this.deleted, 16 | this.deletedConflicts, 17 | this.localSeq, 18 | this.revsInfo, 19 | this.revisions}); 20 | 21 | DocumentsResponse.from(ApiResponse response) : this( 22 | doc: response.json, 23 | ok: response.json['ok'] as bool, 24 | id: (response.json['_id'] ?? response.json['id']) as String, 25 | rev: (response.json['_rev'] ?? response.json['rev']) as String, 26 | attachment: response.json['_attachments'] ?? response.raw, 27 | conflicts: (response.json['_conflicts'] as List) 28 | ?.map((e) => e as String) 29 | ?.toList(), 30 | deleted: response.json['_deleted'] as bool, 31 | deletedConflicts: (response.json['_deleted_conflicts'] as List) 32 | ?.map((e) => e as String) 33 | ?.toList(), 34 | localSeq: response.json['_local_seq'] as String, 35 | revsInfo: (response.json['_revs_info'] as List) 36 | ?.map((e) => e as Map) 37 | ?.toList(), 38 | revisions: response.json['_revisions'] as Map); 39 | 40 | /// Holds document object 41 | /// 42 | /// May contain: 43 | /// - `_id` (string) – Document ID 44 | /// - `_rev` (string) – Revision MVCC token 45 | /// - `_deleted` (boolean) – Deletion flag. Available if document was removed 46 | /// - `_attachments` (object) – Attachment’s stubs. Available if document has any attachments 47 | /// - `_conflicts` (array) – List of conflicted revisions. Available if requested with `conflicts=true` query parameter 48 | /// - `_deleted_conflicts` (array) – List of deleted conflicted revisions. Available if requested with `deleted_conflicts=true` query parameter 49 | /// - `_local_seq (string)` – Document’s update sequence in current database. Available if requested with `local_seq=true` query parameter 50 | /// - `_revs_info (array)` – List of objects with information about local revisions and their status. Available if requested with `open_revs` query parameter 51 | /// - `_revisions (object)` – List of local revision tokens without. Available if requested with `revs=true` query parameter 52 | /// 53 | /// This properties are listed separately in [DocumentsResponse] and you can get them directly. 54 | /// 55 | /// Returns by [Documents.doc] 56 | final Map doc; 57 | 58 | /// Holds operation status. Available in case of success 59 | final bool ok; 60 | 61 | /// Holds document ID 62 | final String id; 63 | 64 | /// Holds revision info of document 65 | final String rev; 66 | 67 | /// Attachment's raw data 68 | final Object attachment; 69 | 70 | /// List of conflicted revisions 71 | final List conflicts; 72 | 73 | /// Deletion flag. Available if document was removed 74 | final bool deleted; 75 | 76 | /// List of deleted conflicted revisions 77 | final List deletedConflicts; 78 | 79 | /// Document’s update sequence in current database 80 | final String localSeq; 81 | 82 | /// List of objects with information about local revisions and their status 83 | final List> revsInfo; 84 | 85 | /// List of local revision tokens without 86 | final Map revisions; 87 | } 88 | -------------------------------------------------------------------------------- /lib/src/responses/error_response.dart: -------------------------------------------------------------------------------- 1 | /// Class that contains error info from CouchDB 2 | class ErrorResponse { 3 | /// Creates [ErrorResponse] instance 4 | ErrorResponse(this.error, this.reason); 5 | 6 | /// Holds error type 7 | final String error; 8 | 9 | /// Holds error reason 10 | final String reason; 11 | 12 | @override 13 | String toString() => 'Error - $error, reason: $reason'; 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/responses/local_documents_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:couchdb/couchdb.dart'; 2 | 3 | import '../local_documents.dart'; 4 | 5 | /// Class that contains responses from `LocalDocuments` class 6 | class LocalDocumentsResponse { 7 | /// Creates instance of [LocalDocumentsResponse] 8 | LocalDocumentsResponse( 9 | {this.offset, 10 | this.rows, 11 | this.totalRows, 12 | this.updateSeq, 13 | this.doc, 14 | this.ok, 15 | this.id, 16 | this.rev, 17 | this.attachment, 18 | this.conflicts, 19 | this.deleted, 20 | this.deletedConflicts, 21 | this.localSeq, 22 | this.revsInfo, 23 | this.revisions}); 24 | 25 | LocalDocumentsResponse.from(ApiResponse response) : this( 26 | offset: response.json['offset'] as int, 27 | rows: (response.json['rows'] 28 | as List) 29 | ?.map((e) => e as Map) 30 | ?.toList(), 31 | totalRows: response.json['total_rows'] as int, 32 | updateSeq: response.json['update_seq'] as String, 33 | doc: response.json, 34 | ok: response.json['ok'] as bool, 35 | id: (response.json['_id'] ?? response.json['id']) as String, 36 | rev: (response.json['_rev'] ?? response.json['rev']) as String, 37 | attachment: response.json['_attachments'] ?? response.raw, 38 | conflicts: 39 | (response.json['_conflicts'] as List) 40 | ?.map((e) => e as String) 41 | ?.toList(), 42 | deleted: response.json['_deleted'] as bool, 43 | deletedConflicts: (response.json['_deleted_conflicts'] as List) 44 | ?.map((e) => e as String) 45 | ?.toList(), 46 | localSeq: response.json['_local_seq'] as String, 47 | revsInfo: (response.json['_revs_info'] as List) 48 | ?.map((e) => e as Map) 49 | ?.toList(), 50 | revisions: response.json['_revisions'] as Map); 51 | 52 | /// Holds offset where the document list started 53 | final int offset; 54 | 55 | /// List array of view row objects 56 | final List> rows; 57 | 58 | /// Holds number of documents in the database 59 | final int totalRows; 60 | 61 | /// Current update sequence for the database 62 | final String updateSeq; 63 | 64 | /// Holds local document object 65 | /// 66 | /// May contain: 67 | /// - `_id` (string) – Document ID 68 | /// - `_rev` (string) – Revision MVCC token 69 | /// - `_deleted` (boolean) – Deletion flag. Available if document was removed 70 | /// - `_attachments` (object) – Attachment’s stubs. Available if document has any attachments 71 | /// - `_conflicts` (array) – List of conflicted revisions. Available if requested with `conflicts=true` query parameter 72 | /// - `_deleted_conflicts` (array) – List of deleted conflicted revisions. Available if requested with `deleted_conflicts=true` query parameter 73 | /// - `_local_seq (string)` – Document’s update sequence in current database. Available if requested with `local_seq=true` query parameter 74 | /// - `_revs_info (array)` – List of objects with information about local revisions and their status. Available if requested with `open_revs` query parameter 75 | /// - `_revisions (object)` – List of local revision tokens without. Available if requested with `revs=true` query parameter 76 | /// 77 | /// This properties are listed separately in [LocalDocumentsResponse] and you can get them directly. 78 | /// 79 | /// Returns by [LocalDocuments.localDoc] 80 | final Map doc; 81 | 82 | /// Holds operation status. Available in case of success 83 | final bool ok; 84 | 85 | /// Holds document ID 86 | final String id; 87 | 88 | /// Holds revision info of document 89 | final String rev; 90 | 91 | /// Attachment's raw data 92 | final Object attachment; 93 | 94 | /// List of conflicted revisions 95 | final List conflicts; 96 | 97 | /// Deletion flag. Available if document was removed 98 | final bool deleted; 99 | 100 | /// List of deleted conflicted revisions 101 | final List deletedConflicts; 102 | 103 | /// Document’s update sequence in current database 104 | final String localSeq; 105 | 106 | /// List of objects with information about local revisions and their status 107 | final List> revsInfo; 108 | 109 | /// List of local revision tokens without 110 | final Map revisions; 111 | } 112 | -------------------------------------------------------------------------------- /lib/src/responses/server_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:couchdb/couchdb.dart'; 2 | 3 | import '../server.dart'; 4 | 5 | /// Class that contains responses from `Server` class 6 | class ServerResponse { 7 | /// Creates instance of [ServerResponse] 8 | ServerResponse({this.couchDbMessage, 9 | this.uuid, 10 | this.vendor, 11 | this.version, 12 | this.state, 13 | this.results, 14 | this.lastSeq, 15 | this.allNodes, 16 | this.clusterNodes, 17 | this.history, 18 | this.ok, 19 | this.replicationIdVersion, 20 | this.sessionId, 21 | this.sourceLastSeq, 22 | this.offset, 23 | this.totalRows, 24 | this.id, 25 | this.database, 26 | this.docId, 27 | this.pid, 28 | this.node, 29 | this.source, 30 | this.target, 31 | this.startTime, 32 | this.lastUpdate, 33 | this.info, 34 | this.errorCount, 35 | this.fabric, 36 | this.ddocCache, 37 | this.couchDb, 38 | this.pread, 39 | this.couchReplicator, 40 | this.mem3, 41 | this.couchLog, 42 | this.rexi, 43 | this.globalChanges, 44 | this.uptime, 45 | this.memory, 46 | this.runQueue, 47 | this.etsTableCount, 48 | this.contextSwitches, 49 | this.reductions, 50 | this.garbageCollectionCount, 51 | this.wordsReclaimed, 52 | this.ioInput, 53 | this.ioOutput, 54 | this.osProcCount, 55 | this.staleProcCount, 56 | this.processCount, 57 | this.processLimit, 58 | this.messageQueues, 59 | this.internalReplicationJobs, 60 | this.distribution, 61 | this.status, 62 | this.uuids, 63 | this.list, 64 | this.name, 65 | this.roles, 66 | this.userCtx}); 67 | 68 | ServerResponse.from(ApiResponse response) : this( 69 | // [Server.nodeStats] returns JSON with `couchdb` field 70 | // which type is Map 71 | couchDbMessage: 72 | response.json['couchdb'] is String 73 | ? response.json['couchdb'] as String 74 | : null, 75 | uuid: response.json['uuid'] as String, 76 | vendor: (response.json['vendor'] as Map) 77 | ?.map((k, e) => MapEntry(k, e as String)), 78 | version: response.json['version'] as String, 79 | state: response.json['state'] as String, 80 | results: (response.json['results'] as List) 81 | ?.map((o) => 82 | (o as Map) 83 | ?.map((k, e) => MapEntry(k, e as String))) 84 | ?.toList(), 85 | lastSeq: response.json['last_seq'] as String, 86 | allNodes: (response.json['all_nodes'] as List) 87 | ?.map((v) => v as String) 88 | ?.toList(), 89 | clusterNodes: (response.json['cluster_nodes'] as List) 90 | ?.map((v) => v as String) 91 | ?.toList(), 92 | history: (response.json['results'] as List) 93 | ?.map((o) => o as Map) 94 | ?.toList(), 95 | ok: response.json['ok'] as bool, 96 | replicationIdVersion: response.json['replication_id_version'] as int, 97 | sessionId: response.json['session_id'] as String, 98 | sourceLastSeq: response.json['source_last_seq'] as int, 99 | offset: response.json['offset'] as int, 100 | totalRows: response.json['total_rows'] as int, 101 | id: response.json['id'] as String, 102 | database: response.json['database'] as String, 103 | docId: response.json['doc_id'] as String, 104 | pid: response.json['pid'] as String, 105 | node: response.json['node'] as String, 106 | source: response.json['source'] as String, 107 | target: response.json['target'] as String, 108 | startTime: response.json['start_time'] as String, 109 | lastUpdate: response.json['last_update'] as String, 110 | info: response.json['info'], 111 | errorCount: response.json['error_count'] as int, 112 | fabric: (response.json['fabric'] as Map)?.map((k, v) => 113 | MapEntry>>( 114 | k, 115 | (v as Map)?.map( 116 | (k, v) => MapEntry>( 117 | k, v as Map)))), 118 | ddocCache: (response.json['ddoc_cache'] as Map)?.map((k, 119 | v) => 120 | MapEntry>(k, v as Map)), 121 | couchDb: response.json['couchdb'] is Map 122 | ? (response.json['couchdb'] as Map)?.map((k, v) => 123 | MapEntry>(k, v as Map)) 124 | : null, 125 | pread: (response.json['pread'] as Map)?.map((k, v) => 126 | MapEntry>(k, v as Map)), 127 | couchReplicator: (response.json['couch_replicator'] as Map)?.map((k, v) => 129 | MapEntry>(k, v as Map)), 130 | mem3: (response.json['mem3'] as Map)?.map((k, v) => 131 | MapEntry>>(k, 132 | (v as Map)?.map((k, v) => 133 | MapEntry>( 134 | k, v as Map)))), 135 | couchLog: (response.json['couch_log'] as Map)?.map((k, 136 | v) => MapEntry>>(k, 137 | (v as Map)?.map((k, v) => 138 | MapEntry>( 139 | k, v as Map)))), 140 | rexi: (response.json['rexi'] as Map)?.map((k, v) => 141 | MapEntry>(k, v as Map)), 142 | globalChanges: (response.json['global_changes'] as Map) 143 | ?.map((k, v) => 144 | MapEntry>(k, v as Map)), 145 | uptime: response.json['uptime'] as int, 146 | memory: (response.json['memory'] as Map)?.map((k, v) => 147 | MapEntry(k, v as int)), 148 | runQueue: response.json['run_queue'] as int, 149 | etsTableCount: response.json['ets_table_count'] as int, 150 | contextSwitches: response.json['context_switches'] as int, 151 | reductions: response.json['reductions'] as int, 152 | garbageCollectionCount: response.json['garbage_collection_count'] as int, 153 | wordsReclaimed: response.json['words_reclaimed'] as int, 154 | ioInput: response.json['io_input'] as int, 155 | ioOutput: response.json['io_output'] as int, 156 | osProcCount: response.json['os_proc_count'] as int, 157 | staleProcCount: response.json['stale_proc_count'] as int, 158 | processCount: response.json['process_count'] as int, 159 | processLimit: response.json['process_limit'] as int, 160 | messageQueues: response.json['message_queues'] as Map, 161 | internalReplicationJobs: response 162 | .json['internal_replication_jobs'] as int, 163 | distribution: response.json['distribution'] as Map, 164 | status: response.json['status'] as String, 165 | uuids: (response.json['uuids'] as List) 166 | ?.map((e) => e as String) 167 | ?.toList(), 168 | list: response.json['list'] as List, 169 | name: response.json['name'] as String, 170 | roles: (response.json['roles'] as List) 171 | ?.map((v) => v as String) 172 | ?.toList(), 173 | userCtx: response.json['userCtx'] as Map); 174 | 175 | /// Welcome message from CouchDB 176 | /// 177 | /// Provided by [Server.couchDbInfo] 178 | final String couchDbMessage; 179 | 180 | /// UUID of database 181 | final String uuid; 182 | 183 | /// Field that contain manufacturer of CouchDB 184 | final Map vendor; 185 | 186 | /// Version of CouchDB 187 | final String version; 188 | 189 | /// Current state of the node and/or cluster 190 | final String state; 191 | 192 | /// Holds database updates 193 | final List> results; 194 | 195 | /// Last sequense of the event 196 | final String lastSeq; 197 | 198 | /// Holds all nodes of database instance 199 | final List allNodes; 200 | 201 | /// Holds all nodes of database instance 202 | final List clusterNodes; 203 | 204 | /// Holds replication history 205 | final List> history; 206 | 207 | /// Holds replication status 208 | final bool ok; 209 | 210 | /// Holds replication protocol version 211 | final int replicationIdVersion; 212 | 213 | /// Holds unique session ID 214 | final String sessionId; 215 | 216 | /// Holds last sequence number read from source database 217 | final int sourceLastSeq; 218 | 219 | /// Holds how many results were skipped in [Server.schedulerJobs] 220 | final int offset; 221 | 222 | /// Holds total number of replication jobs 223 | final int totalRows; 224 | 225 | /// Holds replication ID 226 | final String id; 227 | 228 | /// Holds replication document database 229 | final String database; 230 | 231 | /// Holds replication document ID 232 | final String docId; 233 | 234 | /// Holds replication process ID 235 | final String pid; 236 | 237 | /// Holds cluster node where the job is running 238 | final String node; 239 | 240 | /// Holds replication source 241 | final String source; 242 | 243 | /// Holds replication target 244 | final String target; 245 | 246 | /// Timestamp of when the replication was started 247 | final String startTime; 248 | 249 | /// Holds timestamp of last state update 250 | final String lastUpdate; 251 | 252 | /// May contain additional information about the state. 253 | /// 254 | /// For error states, this will be a string. For success states this will contain a JSON object. 255 | /// Also may contain server authentication configuration as `Map` type. 256 | final Object info; 257 | 258 | /// Holds consecutive errors count 259 | final int errorCount; 260 | 261 | /// Holds cluster-related operations 262 | final Map>> fabric; 263 | 264 | /// Cache info about design document? 265 | final Map> ddocCache; 266 | 267 | /// Primary CouchDB database operations info 268 | /// 269 | /// Produced by [Server.nodeStats] 270 | final Map> couchDb; 271 | 272 | /// CouchDB file-related exceptions info 273 | final Map> pread; 274 | 275 | /// Replication scheduler and subsystem info 276 | final Map> couchReplicator; 277 | 278 | /// Node membership-related statistics 279 | final Map>> mem3; 280 | 281 | /// Logging subsystem info 282 | final Map>> couchLog; 283 | 284 | /// Cluster internal RPC-related statistics 285 | final Map> rexi; 286 | 287 | /// Global changes feed info 288 | final Map> globalChanges; 289 | 290 | /// These statistic are generally intended for CouchDB developers only. 291 | final int uptime; 292 | 293 | /// These statistic are generally intended for CouchDB developers only. 294 | final Map memory; 295 | 296 | /// These statistic are generally intended for CouchDB developers only. 297 | final int runQueue; 298 | 299 | /// These statistic are generally intended for CouchDB developers only. 300 | final int etsTableCount; 301 | 302 | /// These statistic are generally intended for CouchDB developers only. 303 | final int contextSwitches; 304 | 305 | /// These statistic are generally intended for CouchDB developers only. 306 | final int reductions; 307 | 308 | /// These statistic are generally intended for CouchDB developers only. 309 | final int garbageCollectionCount; 310 | 311 | /// These statistic are generally intended for CouchDB developers only. 312 | final int wordsReclaimed; 313 | 314 | /// These statistic are generally intended for CouchDB developers only. 315 | final int ioInput; 316 | 317 | /// These statistic are generally intended for CouchDB developers only. 318 | final int ioOutput; 319 | 320 | /// These statistic are generally intended for CouchDB developers only. 321 | final int osProcCount; 322 | 323 | /// These statistic are generally intended for CouchDB developers only. 324 | final int staleProcCount; 325 | 326 | /// These statistic are generally intended for CouchDB developers only. 327 | final int processCount; 328 | 329 | /// These statistic are generally intended for CouchDB developers only. 330 | final int processLimit; 331 | 332 | /// These statistic are generally intended for CouchDB developers only. 333 | final Map messageQueues; 334 | 335 | /// These statistic are generally intended for CouchDB developers only. 336 | final int internalReplicationJobs; 337 | 338 | /// These statistic are generally intended for CouchDB developers only. 339 | final Map distribution; 340 | 341 | /// Status of current running node 342 | final String status; 343 | 344 | /// List of uuids returned by CouchDB 345 | /// 346 | /// Returned by [Server.uuids] 347 | final List uuids; 348 | 349 | /// List of some objects (if JSON itself is list) 350 | /// 351 | /// Returned by [Server.activeTasks], [Server.allDbs], [Server.dbsInfo] 352 | final List list; 353 | 354 | /// Holds username 355 | final String name; 356 | 357 | /// Holds roles of user in database 358 | final List roles; 359 | 360 | /// Holds user context for the current user 361 | final Map userCtx; 362 | } 363 | -------------------------------------------------------------------------------- /lib/src/server.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import 'interfaces/client_interface.dart'; 4 | import 'interfaces/server_interface.dart'; 5 | import 'responses/api_response.dart'; 6 | import 'responses/server_response.dart'; 7 | import 'utils/includer_path.dart'; 8 | 9 | /// Server interface provides the basic interface to a CouchDB server 10 | /// for obtaining CouchDB information and getting and setting configuration information 11 | class Server implements ServerInterface { 12 | /// Instance of connected client 13 | final ClientInterface _client; 14 | 15 | /// Create Server by accepting web-based or server-based client 16 | Server(this._client); 17 | 18 | @override 19 | Future activeTasks({Map headers}) async { 20 | ApiResponse result = 21 | await _client.get('_active_tasks', reqHeaders: headers); 22 | return ServerResponse.from(result); 23 | } 24 | 25 | @override 26 | Future allDbs({Map headers}) async { 27 | ApiResponse result = await _client.get('_all_dbs', reqHeaders: headers); 28 | return ServerResponse.from(result); 29 | } 30 | 31 | @override 32 | Future dbsInfo(List keys) async { 33 | final body = >{'keys': keys}; 34 | 35 | ApiResponse result = await _client.post('_dbs_info', body: body); 36 | return ServerResponse.from(result); 37 | } 38 | 39 | @override 40 | Future clusterSetupStatus( 41 | {List ensureDbsExist, Map headers}) async { 42 | ApiResponse result = await _client.get( 43 | '_cluster_setup?${includeNonNullParam('ensure_dbs_exist', ensureDbsExist)}', 44 | reqHeaders: headers); 45 | return ServerResponse.from(result); 46 | } 47 | 48 | @override 49 | Future configureCouchDb( 50 | {@required String action, 51 | String bindAdress, 52 | String username, 53 | String password, 54 | int port, 55 | int nodeCount, 56 | String remoteNode, 57 | String remoteCurrentUser, 58 | String remoteCurrentPassword, 59 | String host, 60 | List ensureDbsExist, 61 | Map headers}) async { 62 | final body = {'action': action}; 63 | 64 | switch (action) { 65 | case 'enable_single_node': 66 | body['bind_address'] = bindAdress; 67 | body['username'] = username; 68 | body['password'] = password; 69 | body['port'] = port; 70 | break; 71 | 72 | case 'enable_cluster': 73 | body['bind_address'] = bindAdress; 74 | body['username'] = username; 75 | body['password'] = password; 76 | body['port'] = port; 77 | body['node_count'] = nodeCount; 78 | body['remote_node'] = remoteNode; 79 | body['remote_current_user'] = remoteCurrentUser; 80 | body['remote_current_password'] = remoteCurrentPassword; 81 | break; 82 | 83 | case 'add_node': 84 | body['username'] = username; 85 | body['password'] = password; 86 | body['port'] = port; 87 | body['host'] = host; 88 | break; 89 | } 90 | 91 | if (ensureDbsExist != null) { 92 | body['ensure_dbs_exist'] = ensureDbsExist; 93 | } 94 | 95 | ApiResponse result = 96 | await _client.post('_cluster_setup', reqHeaders: headers, body: body); 97 | return ServerResponse.from(result); 98 | } 99 | 100 | @override 101 | Future couchDbInfo({Map headers}) async { 102 | ApiResponse result = await _client.get('', reqHeaders: headers); 103 | return ServerResponse.from(result); 104 | } 105 | 106 | @override 107 | Future dbUpdates( 108 | {String feed = 'normal', 109 | int timeout = 60, 110 | int heartbeat = 60000, 111 | String since, 112 | Map headers}) async { 113 | String path; 114 | 115 | feed == 'longpoll' || feed == 'continuous' || feed == 'eventsource' 116 | ? path = 117 | '_db_updates?feed=$feed&timeout=$timeout&heartbeat=$heartbeat&${includeNonNullParam('since', since)}' 118 | : path = 119 | '_db_updates?feed=$feed&timeout=$timeout&${includeNonNullParam('since', since)}'; 120 | 121 | ApiResponse result = await _client.get(path, reqHeaders: headers); 122 | return ServerResponse.from(result); 123 | } 124 | 125 | @override 126 | Future membership({Map headers}) async { 127 | ApiResponse result = await _client.get('_membership', reqHeaders: headers); 128 | return ServerResponse.from(result); 129 | } 130 | 131 | @override 132 | Future nodeStats( 133 | {String nodeName = '_local', 134 | String statisticSection, 135 | String statisticId, 136 | Map headers}) async { 137 | final path = statisticSection != null && statisticId != null 138 | ? '_node/$nodeName/_stats/$statisticSection/$statisticId' 139 | : '_node/$nodeName/_stats'; 140 | 141 | ApiResponse result = await _client.get(path, reqHeaders: headers); 142 | return ServerResponse.from(result); 143 | } 144 | 145 | @override 146 | Future replicate( 147 | {bool cancel, 148 | bool continuous, 149 | bool createTarget, 150 | List docIds, 151 | String filterFunJS, 152 | String proxy, 153 | Object source, 154 | Object target, 155 | Map headers}) async { 156 | final body = {}; 157 | 158 | if (cancel != null) { 159 | body['cancel'] = cancel; 160 | } 161 | if (continuous != null) { 162 | body['continuous'] = continuous; 163 | } 164 | if (createTarget != null) { 165 | body['create_target'] = createTarget; 166 | } 167 | if (docIds != null) { 168 | body['doc_ids'] = docIds; 169 | } 170 | if (filterFunJS != null) { 171 | body['filter'] = filterFunJS; 172 | } 173 | if (proxy != null) { 174 | body['proxy'] = proxy; 175 | } 176 | if (source != null) { 177 | body['source'] = source; 178 | } 179 | if (target != null) { 180 | body['target'] = target; 181 | } 182 | 183 | ApiResponse result = 184 | await _client.post('_replicate', reqHeaders: headers, body: body); 185 | return ServerResponse.from(result); 186 | } 187 | 188 | @override 189 | Future schedulerJobs({int limit, int skip}) async { 190 | ApiResponse result = await _client.get( 191 | '_scheduler/jobs?${includeNonNullParam('limit', limit)}&${includeNonNullParam('skip', skip)}'); 192 | return ServerResponse.from(result); 193 | } 194 | 195 | @override 196 | Future schedulerDocs({int limit, int skip}) async { 197 | ApiResponse result = await _client.get( 198 | '_scheduler/docs?${includeNonNullParam('limit', limit)}&${includeNonNullParam('skip', skip)}'); 199 | return ServerResponse.from(result); 200 | } 201 | 202 | @override 203 | Future schedulerDocsWithReplicatorDbName( 204 | {String replicator = '_replicator', int limit, int skip}) async { 205 | ApiResponse result = await _client.get( 206 | '_scheduler/docs/$replicator?${includeNonNullParam('limit', limit)}&${includeNonNullParam('skip', skip)}'); 207 | return ServerResponse.from(result); 208 | } 209 | 210 | @override 211 | Future schedulerDocsWithDocId(String docId, 212 | {String replicator = '_replicator'}) async { 213 | ApiResponse result = 214 | await _client.get('_scheduler/docs/$replicator/$docId'); 215 | return ServerResponse.from(result); 216 | } 217 | 218 | 219 | @override 220 | Future systemStatsForNode( 221 | {String nodeName = '_local', Map headers}) async { 222 | ApiResponse result = 223 | await _client.get('_node/$nodeName/_system', reqHeaders: headers); 224 | return ServerResponse.from(result); 225 | } 226 | 227 | @override 228 | Future up() async { 229 | ApiResponse result = await _client.get('_up'); 230 | return ServerResponse.from(result); 231 | } 232 | 233 | @override 234 | Future uuids( 235 | {int count = 1, Map headers}) async { 236 | ApiResponse result = 237 | await _client.get('_uuids?count=$count', reqHeaders: headers); 238 | return ServerResponse.from(result); 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /lib/src/utils/includer_path.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | /// Method for including only non-null parameter to path 4 | String includeNonNullParam(String name, Object value) => 5 | value != null ? '$name=$value' : ''; 6 | 7 | /// If value != null, returns value as a JSON-encoded value, for use in a url. 8 | /// Otherwise returns an empty string 9 | /// 10 | /// Some URL parameters (e.g. startkey and endkey) expect JSON-encoded 11 | /// values rather than bare strings. Mostly this amounts to adding 12 | /// quotation marks on either side of the string and escaping 13 | /// special characters. 14 | String includeNonNullJsonParam(String name, Object value) => 15 | value != null ? '$name=${jsonEncode(value)}' : ''; 16 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: couchdb 2 | description: CouchDB client written in Dart 3 | version: 0.7.0 4 | homepage: https://github.com/YevhenKap/couchdb_dart 5 | license: MIT 6 | 7 | environment: 8 | sdk: '>=2.2.0 <3.0.0' 9 | 10 | dependencies: 11 | meta: ^1.1.6 12 | http: ^0.12.0 13 | crypto: ^2.1.3 14 | 15 | dev_dependencies: 16 | test: ^1.9.1 17 | pedantic: ^1.8.0 -------------------------------------------------------------------------------- /test/couchdb_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:couchdb/couchdb.dart'; 2 | 3 | Future main() async { 4 | final c = CouchDbClient.fromString('http://admin:password@127.0.0.1:5984'); 5 | final da = Databases(c); 6 | final ddm = DesignDocuments(c); 7 | final dm = Documents(c); 8 | final sm = Server(c); 9 | 10 | final queries = >[{'keys': ['_design/yyy', 'FishStew']}]; 11 | 12 | try { 13 | final headers = {'Accept': 'text/plain'}; 14 | final o = await da.dbInfo('denta'); 15 | print(o.dbName); 16 | } on CouchDbException catch (e) { 17 | print(e); 18 | } 19 | } 20 | 21 | --------------------------------------------------------------------------------