Line data Source code
1 : /*
2 : * Package : Wilt
3 : * Author : S. Hamblett <steve.hamblett@linux.com>
4 : * Date : 04/06/2013
5 : * Copyright : S.Hamblett@OSCF
6 : *
7 : * The Wilt class provides core functionality for interacting with CouchDB databases from
8 : * both the browser and the server. Wilt should not be instantiated standalone but rather through
9 : * inclusion of one of the wilt_browser_client or wilt_server_client files.
10 : *
11 : * Further documentation can be found in the docs folder.
12 : *
13 : */
14 :
15 : part of wilt;
16 :
17 : class Wilt {
18 : /// URL constant for CouchDB SESSION function
19 : static const String session = "/_session";
20 :
21 : /// URL constant for CouchDB STATS function
22 : static const String stats = "/_stats";
23 :
24 : /// URL constant for CouchDB ALLDBS function
25 : static const String alldbs = "/_all_dbs";
26 :
27 : /// URL constant for CouchDB ALLDOCS function
28 : static const String alldocs = "/_all_docs";
29 :
30 : /// URL constant for CouchDB BULKDOCS function
31 : static const String bulkdocs = "/_bulk_docs";
32 :
33 : /// URL constant for CouchDB UUID function
34 : static const String uuids = "/_uuids";
35 :
36 : /// Etag header
37 : static const String etag = 'etag';
38 :
39 : /// AUTH_BASIC denotes Basic HTTP authentication.
40 : /// If login is called AUTH_BASIC is set, otherwise it defaults to AUTH_NONE
41 : static const String authBasic = 'basic';
42 :
43 : /// No authentication
44 : static const String authNone = 'none';
45 :
46 : /// Operation types and method definitions
47 : static const String gett = 'GET_GET';
48 : static const String headd = 'HEAD_HEAD';
49 : static const String postt = 'POST_POST';
50 : static const String putt = 'PUT_PUT';
51 : static const String deletee = 'DELETE_DELETE';
52 : static const String copy = 'COPY_COPY';
53 : static const String getDocumentt = 'GET_DOCUMENT';
54 : static const String deleteDocumentt = 'DELETE_DOCUMENT';
55 : static const String putDocumentt = 'PUT_DOCUMENT';
56 : static const String postDocumentt = 'POST_DOCUMENT';
57 : static const String postDocumentStringg = 'POST_DOCUMENTSTRING';
58 : static const String copyDocumentt = 'COPY_DOCUMENT';
59 : static const String getAllDocss = 'GET_ALLDOCS';
60 : static const String bulkk = 'POST_BULK';
61 : static const String bulkStringg = 'POST_BULKSTRING';
62 : static const String createDatabasee = 'PUT_DATABASE';
63 : static const String deleteDatabasee = 'DELETE_DATABASE';
64 : static const String databaseInfo = 'GET_DATABASEINFO';
65 : static const String getSessionn = 'GET_SESSION';
66 : static const String getStatss = 'GET_STATS';
67 : static const String getAllDbss = 'GET_ALLDBS';
68 : static const String createAttachmentt = 'PUT_CREATEATTACH';
69 : static const String updateAttachmentt = 'PUT_UPDATEATTACH';
70 : static const String deleteAttachmentt = 'DELETE_ATTACH';
71 : static const String getAttachmentt = 'GET_ATTACH';
72 : static const String generateIdss = 'GET_IDS';
73 :
74 : /// Please use the wilt_browser_client or wilt_server_client import files to
75 : /// instantiate a Wilt object for use in either the browser or server environment.
76 : /// You can do this here but you must supply either a browser or server HTTP adapter
77 : /// to use.
78 : Wilt(this._host, this._port, this._scheme, this._httpAdapter,
79 1 : [this._clientCompletion = null]) {
80 3 : if ((host == null) || (port == null) || (scheme == null)) {
81 1 : throw new WiltException(WiltException.badConstParams);
82 : }
83 :
84 1 : if (_httpAdapter == null) {
85 0 : throw new WiltException(WiltException.badConstNoAdapter);
86 : }
87 : }
88 :
89 : /// Database name
90 : String _db = null;
91 1 : String get db => _db;
92 1 : set db(String name) => _db = name;
93 :
94 : /// Change notification database name
95 : String changeNotificationDbName = null;
96 :
97 : /// Host name
98 : String _host = null;
99 1 : String get host => _host;
100 :
101 : /// Port number
102 : String _port = null;
103 1 : String get port => _port;
104 :
105 : /// HTTP scheme
106 : String _scheme = null;
107 1 : String get scheme => _scheme;
108 :
109 : /// HTTP Adapter
110 : WiltHTTPAdapter _httpAdapter = null;
111 0 : set httpAdapter(WiltHTTPAdapter adapter) => _httpAdapter = adapter;
112 0 : WiltHTTPAdapter get httpAdapter => _httpAdapter;
113 :
114 : /// Change notification
115 : _WiltChangeNotification _changeNotifier = null;
116 :
117 : /// Change notification event stream
118 : /// This is a broadcast stream so can support more than one listener.
119 : Stream<WiltChangeNotificationEvent> get changeNotification =>
120 3 : _changeNotifier.changeNotification.stream;
121 :
122 : /// Change notification paused state
123 2 : bool get changeNotificationsPaused => _changeNotifier.pause;
124 :
125 : /// Completion function
126 : var _clientCompletion = null;
127 :
128 : /// Completion callback
129 : set resultCompletion(final Object completion) {
130 0 : _clientCompletion = completion;
131 : }
132 :
133 : /// Response getter for completion callbacks
134 : jsonobject.JsonObject _completionResponse;
135 0 : jsonobject.JsonObject get completionResponse => _completionResponse;
136 :
137 : /// Authentication, user name
138 : String _user = null;
139 :
140 : /// Authentication, user password
141 : String _password = null;
142 :
143 : /// Authentication, type
144 : String authenticationType = authNone;
145 :
146 : /// The internal HTTP request method. This wraps the
147 : /// HTTP adapter class.
148 : Future<jsonobject.JsonObject> _httpRequest(String method, String url,
149 : {String data: null, Map headers: null}) {
150 : /* Build the request for the HttpAdapter*/
151 1 : final Map wiltHeaders = new Map<String, String>();
152 1 : wiltHeaders["Accept"] = "application/json";
153 1 : if (headers != null) wiltHeaders.addAll(headers);
154 :
155 : /* Build the URL */
156 4 : final String wiltUrl = "$scheme$host:$port$url";
157 :
158 : /* Check for authentication */
159 1 : if (_user != null) {
160 1 : switch (authenticationType) {
161 1 : case authBasic:
162 3 : final String authStringToEncode = "$_user:$_password";
163 : final String encodedAuthString =
164 2 : CryptoUtils.bytesToBase64(authStringToEncode.codeUnits);
165 1 : final String authString = "Basic $encodedAuthString";
166 1 : wiltHeaders['Authorization'] = authString;
167 : break;
168 :
169 0 : case authNone:
170 : break;
171 : }
172 : }
173 :
174 : /* Execute the request*/
175 : final Future<jsonobject.JsonObject> completion =
176 2 : _httpAdapter.httpRequest(method, wiltUrl, data, wiltHeaders)
177 1 : ..then((jsonResponse) {
178 1 : if (_clientCompletion != null) {
179 0 : _completionResponse = jsonResponse;
180 0 : _clientCompletion();
181 : }
182 : });
183 :
184 : return completion;
185 : }
186 :
187 : /// Takes a URL and key/value pair for a URL parameter and adds this
188 : /// to the query parameters of the URL.
189 : String _setURLParameter(String url, String key, String value) {
190 1 : final originalUrl = Uri.parse(url);
191 1 : final Map queryParams = originalUrl.queryParameters;
192 1 : final Map newQueryParams = new Map<String, String>.from(queryParams);
193 1 : newQueryParams[key] = value;
194 :
195 1 : final newUrl = new Uri(
196 1 : scheme: originalUrl.scheme,
197 1 : userInfo: originalUrl.userInfo,
198 1 : host: originalUrl.host,
199 1 : port: originalUrl.port,
200 1 : path: originalUrl.path,
201 : queryParameters: newQueryParams);
202 :
203 1 : final String returnUrl = newUrl.toString();
204 : return returnUrl /* Private */;
205 : }
206 :
207 : /// Conditions the URL for use by Wilt and checks for
208 : /// a valid database by default.
209 : String _conditionUrl(String url) {
210 1 : if (db == null) {
211 : return WiltException.noDatabaseSpecified;
212 : }
213 : if (url == null) {
214 : return '/';
215 : }
216 : String urlRet = url;
217 : /* The first char of the URL should be a slash. */
218 1 : if (!url.startsWith('/')) {
219 1 : urlRet = "/$urlRet";
220 : }
221 1 : if (db != null) {
222 2 : urlRet = "/$db$urlRet";
223 : }
224 : return urlRet;
225 : }
226 :
227 : /// Raise an exception from a future API call.
228 : /// If we are using completion throw an exception as normal.
229 : Future<WiltException> _raiseException(String name) {
230 1 : if (_clientCompletion == null) {
231 2 : return new Future.error(new WiltException(name));
232 : } else {
233 0 : throw new WiltException(name);
234 : }
235 : }
236 :
237 : /// Basic method where only a URL and a method is passed.
238 : /// Wilt applies no checks to this URL nor does it add the
239 : /// database, the format of this is entirely up to the user.
240 : ///
241 : /// This can be used for CouchDb functions that are not directly supported by Wilt,
242 : /// e.g views, attachments and design documents.
243 : Future<jsonobject.JsonObject> httpRequest(String url,
244 : {String method: "GET"}) {
245 1 : return _httpRequest(method, url);
246 : }
247 :
248 : /// Performs an HTTP GET operation, the URL is conditioned and
249 : /// the current database added.
250 : Future get(String url) {
251 1 : final String url1 = _conditionUrl(url);
252 1 : if (url1 == WiltException.noDatabaseSpecified) {
253 1 : return _raiseException(WiltException.noDatabaseSpecified);
254 : }
255 :
256 : /* Perform the get */
257 0 : return _httpRequest('GET', url1);
258 : }
259 :
260 : /// Performs a HTTP HEAD operation, the URL is conditioned and
261 : /// the current database added.
262 : Future head(String url) {
263 1 : final String url1 = _conditionUrl(url);
264 1 : if (url1 == WiltException.noDatabaseSpecified) {
265 1 : return _raiseException(WiltException.noDatabaseSpecified);
266 : }
267 :
268 : /* Perform the head */
269 1 : return _httpRequest(headd, url1);
270 : }
271 :
272 : /// Performs a HTTP POST operation,, the URL is conditioned and
273 : /// the current database added.
274 : Future post(String url, String data, [Map headers]) {
275 1 : final String url1 = _conditionUrl(url);
276 1 : if (url1 == WiltException.noDatabaseSpecified) {
277 1 : return _raiseException(WiltException.noDatabaseSpecified);
278 : }
279 :
280 : /* Perform the post */
281 0 : return _httpRequest('POST', url1, data: data, headers: headers);
282 : }
283 :
284 : /// Performs a HTTP PUT operation,, the URL is conditioned and
285 : /// the current database added.
286 : Future put(String url, String data, [Map headers]) {
287 1 : final String url1 = _conditionUrl(url);
288 1 : if (url1 == WiltException.noDatabaseSpecified) {
289 1 : return _raiseException(WiltException.noDatabaseSpecified);
290 : }
291 :
292 : /* Perform the put */
293 0 : return _httpRequest('PUT', url1, data: data, headers: headers);
294 : }
295 :
296 : /// Performs a HTTP DELETE operation,, the URL is conditioned and
297 : /// the current database added.
298 : Future delete(String url) {
299 1 : final String url1 = _conditionUrl(url);
300 1 : if (url1 == WiltException.noDatabaseSpecified) {
301 1 : return _raiseException(WiltException.noDatabaseSpecified);
302 : }
303 :
304 : /* Perform the delete */
305 0 : return _httpRequest('DELETE', url1);
306 : }
307 :
308 : /// Performs an HTTP GET operation for the supplied document id and
309 : /// optional revision. If withAttachments is set the the body of
310 : /// any attachments are also supplied, note this could make this
311 : /// a large transfer.
312 : Future getDocument(String id,
313 : [String rev = null, bool withAttachments = false]) {
314 : if (id == null) {
315 1 : return _raiseException(WiltException.getDocNoId);
316 : }
317 :
318 : String url = id;
319 : if (rev != null) {
320 0 : url = _setURLParameter(url, 'rev', rev);
321 : }
322 :
323 : if (withAttachments) {
324 1 : url = _setURLParameter(url, 'attachments', 'true');
325 : }
326 :
327 1 : url = _conditionUrl(url);
328 1 : return _httpRequest('GET_DOCUMENT', url);
329 : }
330 :
331 : /// Gets a documents current revision, returns null if
332 : /// the document does not exist.
333 : Future getDocumentRevision(String id) {
334 : if (id == null) {
335 1 : return _raiseException(WiltException.getDocRevNoId);
336 : }
337 :
338 1 : final Completer completer = new Completer();
339 2 : head(id).then((res) {
340 : final jsonobject.JsonObject headers =
341 2 : new jsonobject.JsonObject.fromMap(res.allResponseHeaders);
342 : if (headers != null) {
343 1 : if (headers.containsKey(etag)) {
344 1 : String ver = headers.etag;
345 3 : ver = ver.substring(1, ver.length - 1);
346 1 : completer.complete(ver);
347 : } else {
348 0 : completer.complete(null);
349 : }
350 : }
351 : });
352 :
353 1 : return completer.future;
354 : }
355 :
356 : /// DELETE's the specified document. Must have a revision.
357 : /// If preserve is set to true the whole document is preserved
358 : /// and marked as deleted otherwise only a stub document is
359 : /// kept. Default is to not preserve.
360 : Future deleteDocument(String id, String rev, [bool preserve = false]) {
361 : if ((id == null) || (rev == null)) {
362 1 : return _raiseException(WiltException.deleteDocNoIdRev);
363 : }
364 1 : final Completer completer = new Completer();
365 :
366 : /* Check the preserve flag */
367 : if (preserve) {
368 2 : getDocument(id).then((jsonobject.JsonObject res) {
369 : if (res != null) {
370 1 : jsonobject.JsonObject resp = res.jsonCouchResponse;
371 1 : resp = WiltUserUtils.addDocumentDeleteJo(resp);
372 2 : putDocument(id, resp).then((res1) {
373 1 : completer.complete(res1);
374 : });
375 : } else {
376 0 : completer.complete(null);
377 : }
378 : });
379 1 : return completer.future;
380 : } else {
381 : String url = id;
382 1 : url = _setURLParameter(url, 'rev', rev);
383 1 : url = _conditionUrl(url);
384 1 : return _httpRequest('DELETE_DOCUMENT', url);
385 : }
386 : }
387 :
388 : /// PUT's to the specified document.
389 : ///
390 : /// For an update the revision must be specified, this can be in the
391 : /// document body as a _rev parameter or specified in the call in which
392 : /// case this will be added to the document body.
393 : Future putDocument(String id, jsonobject.JsonObject document,
394 : [String rev = null]) {
395 : if ((id == null) || (document == null)) {
396 1 : return _raiseException(WiltException.putDocNoIdBody);
397 : }
398 :
399 : /* Check for a revision */
400 : String jsonData = null;
401 :
402 : try {
403 : if (rev != null) {
404 1 : jsonData = WiltUserUtils.addDocumentRev(document, rev);
405 : } else {
406 1 : jsonData = JSON.encode(document);
407 : }
408 : } catch (e) {
409 0 : return _raiseException(WiltException.putDocCantStringify);
410 : }
411 :
412 1 : final String url = _conditionUrl(id);
413 1 : return _httpRequest(putDocumentt, url, data: jsonData);
414 : }
415 :
416 : /// PUT's to the specified document where the document is supplied as
417 : /// a JSON string. Must be used if '_id' and or '_rev' are needed.
418 : Future putDocumentString(String id, String document, [String rev = null]) {
419 : if ((id == null) || (document == null)) {
420 0 : return _raiseException(WiltException.putDocStringNoIdBody);
421 : }
422 :
423 : /* Check for a revision */
424 : String id1 = id;
425 : if (rev != null) {
426 0 : id1 = "$id1?rev=$rev";
427 : }
428 1 : final String url = _conditionUrl(id1);
429 1 : return _httpRequest(putDocumentt, url, data: document);
430 : }
431 :
432 : /// POST's the specified document.
433 : /// An optional path to the document can be specified.
434 : Future postDocument(jsonobject.JsonObject document, {String path: null}) {
435 : if (document == null) {
436 1 : return _raiseException(WiltException.postDocNoBody);
437 : }
438 :
439 : String url = "";
440 0 : if (path != null) url = "$url/$path";
441 :
442 : /* Set the content type for a post */
443 1 : final Map headers = new Map<String, String>();
444 1 : headers["Content-Type"] = "application/json";
445 :
446 : String jsonData = null;
447 : try {
448 1 : jsonData = JSON.encode(document);
449 : } catch (e) {
450 0 : return _raiseException(WiltException.postDocCantStringify);
451 : }
452 :
453 1 : url = _conditionUrl(url);
454 1 : return _httpRequest(postDocumentt, url, data: jsonData, headers: headers);
455 : }
456 :
457 : /// POST's to the specified document where the document is supplied as
458 : /// a JSON string. Must be used if '_id' and or '_rev' are needed.
459 : Future postDocumentString(String document, {String path: null}) {
460 : if (document == null) {
461 1 : return _raiseException(WiltException.postDocStringNoBody);
462 : }
463 :
464 : String url = "";
465 0 : if (path != null) url = "$url/$path";
466 :
467 : /* Set the content type for a post */
468 0 : final Map headers = new Map<String, String>();
469 0 : headers["Content-Type"] = "application/json";
470 :
471 0 : url = _conditionUrl(url);
472 0 : return _httpRequest('POST_DOCUMENT_STRING', url,
473 : data: document, headers: headers);
474 : }
475 :
476 : /// Copies the source document to the destination document with an optional revision
477 : /// NOTE this method uses the CouchDB COPY method which is not standard HTTP.
478 : Future copyDocument(String sourceId, String destinationId,
479 : [String rev = null]) {
480 : if (sourceId == null) {
481 1 : return _raiseException(WiltException.copyDocNoSrcId);
482 : }
483 :
484 : if (destinationId == null) {
485 1 : return _raiseException(WiltException.copyDocNoDestId);
486 : }
487 :
488 : String url = sourceId;
489 :
490 : /* Create the special COPY header */
491 1 : final Map headers = new Map<String, String>();
492 : String destination = destinationId;
493 0 : if (rev != null) destination = "$destinationId?rev=$rev";
494 1 : headers['Destination'] = destination;
495 :
496 1 : url = _conditionUrl(url);
497 1 : return _httpRequest('COPY_DOCUMENT', url, headers: headers);
498 : }
499 :
500 : /// Get all documents.
501 : /// The parameters should be self explanatory and are addative.
502 : /// Refer to the CouchDb documentation for further explanation.
503 : Future getAllDocs(
504 : {bool includeDocs: false,
505 : int limit: null,
506 : String startKey: null,
507 : String endKey: null,
508 : List<String> keys: null,
509 : bool descending: false}) {
510 : /* Validate the parameters */
511 1 : if ((limit != null) && (limit < 0)) {
512 1 : return _raiseException(WiltException.getAllDocsLimit);
513 : }
514 :
515 : String url = alldocs;
516 :
517 : /* Check the parameters and build the URL as needed */
518 : if (includeDocs) {
519 1 : url = _setURLParameter(url, 'include_docs', "true");
520 : }
521 :
522 : if (limit != null) {
523 2 : url = _setURLParameter(url, 'limit', limit.toString());
524 : }
525 :
526 : if (startKey != null) {
527 1 : final String jsonStartkey = '"$startKey"';
528 1 : url = _setURLParameter(url, 'startkey', jsonStartkey);
529 : }
530 :
531 : if (endKey != null) {
532 1 : final String jsonEndkey = '"$endKey"';
533 1 : url = _setURLParameter(url, 'endkey', jsonEndkey);
534 : }
535 :
536 : if (descending) {
537 2 : url = _setURLParameter(url, 'descending', descending.toString());
538 : }
539 :
540 : if (keys != null) {
541 1 : final String keyString = JSON.encode(keys);
542 1 : url = _setURLParameter(url, 'keys', keyString);
543 : }
544 :
545 1 : url = _conditionUrl(url);
546 1 : return _httpRequest('GET_ALLDOCS', url);
547 : }
548 :
549 : /// Bulk insert
550 : /// Bulk inserts a list of documents
551 : Future bulk(List<jsonobject.JsonObject> docs, [bool allOrNothing = false]) {
552 : /* Validate the parameters */
553 : if (docs == null) {
554 0 : return _raiseException(WiltException.bulkNoDocList);
555 : }
556 :
557 : String url = bulkdocs;
558 :
559 : if (allOrNothing) {
560 0 : url = _setURLParameter(url, 'all_or_nothing', allOrNothing.toString());
561 : }
562 :
563 : /* Create the bulk insertion data structure */
564 1 : final Map documentMap = new Map<String, List>();
565 1 : documentMap["docs"] = docs;
566 : String docString = null;
567 : try {
568 1 : docString = JSON.encode(documentMap);
569 : } catch (e) {
570 0 : return _raiseException(WiltException.bulkCantStringify);
571 : }
572 :
573 : /* Must set the content type for a post */
574 1 : final Map headers = new Map<String, String>();
575 1 : headers["Content-Type"] = "application/json";
576 :
577 1 : url = _conditionUrl(url);
578 1 : return _httpRequest(bulkk, url, data: docString, headers: headers);
579 : }
580 :
581 : /// Bulk insert JSON string version.
582 : /// Must be used if '_id' and or '_rev' are needed in ANY of the documents
583 : Future bulkString(String docs, [bool allOrNothing = false]) {
584 : /* Validate the parameters */
585 : if (docs == null) {
586 0 : return _raiseException(WiltException.bulkStringNoDoc);
587 : }
588 :
589 : String url = bulkdocs;
590 :
591 : if (allOrNothing) {
592 0 : url = _setURLParameter(url, 'all_or_nothing', allOrNothing.toString());
593 : }
594 :
595 : /* Must set the content type for a post */
596 1 : final Map headers = new Map<String, String>();
597 1 : headers["Content-Type"] = "application/json";
598 :
599 1 : url = _conditionUrl(url);
600 1 : return _httpRequest(bulkStringg, url, data: docs, headers: headers);
601 : }
602 :
603 : /// Creates a database with the specified name.
604 : Future createDatabase(String name) {
605 : if ((name == null)) {
606 1 : return _raiseException(WiltException.createDbNoName);
607 : }
608 :
609 : /* The first char of the URL should be a slash. */
610 : String url = name;
611 1 : if (!url.startsWith('/')) {
612 1 : url = "/$url";
613 : }
614 :
615 1 : return _httpRequest(createDatabasee, url);
616 : }
617 :
618 : /// Deletes the specified database
619 : Future deleteDatabase(String name) {
620 : if (name == null) {
621 1 : return _raiseException(WiltException.deleteDbNoName);
622 : }
623 :
624 : /* The first char of the URL should be a slash. */
625 : String url = name;
626 1 : if (!url.startsWith('/')) {
627 1 : url = "/$url";
628 : }
629 :
630 : /* Null the current database if we have deleted it */
631 2 : if (name == db) _db = null;
632 :
633 1 : return _httpRequest(deleteDatabasee, url);
634 : }
635 :
636 : /// Get information about a database
637 : Future getDatabaseInfo([String dbName = null]) {
638 : String name;
639 : if (dbName != null) {
640 : name = dbName;
641 : } else {
642 1 : name = db;
643 : }
644 :
645 1 : final String url = "/$name";
646 :
647 1 : return _httpRequest(databaseInfo, url);
648 : }
649 :
650 : /// Get current session information from CouchDB
651 : Future getSession() {
652 : final String url = session;
653 :
654 1 : return _httpRequest(getSessionn, url);
655 : }
656 :
657 : /// Get current stats from CouchDB
658 : Future getStats() {
659 : final String url = stats;
660 :
661 1 : return _httpRequest(getStatss, url);
662 : }
663 :
664 : /// Get all the databases from CouchDB
665 : Future getAllDbs() {
666 : final String url = alldbs;
667 :
668 1 : return _httpRequest(getAllDbss, url);
669 : }
670 :
671 : /// Create an attachment on an existing document.
672 : /// contentType is in the form of a mime type e.g. 'image/png'
673 : /// If the document needs to be created as well as the attachment set the rev to ''
674 : Future createAttachment(String docId, String attachmentName, String rev,
675 : String contentType, String payload) {
676 : /**
677 : * Check all parameters are supplied
678 : */
679 : if (docId == null) {
680 1 : return _raiseException(WiltException.createAttNoDocId);
681 : }
682 :
683 : if (attachmentName == null) {
684 1 : return _raiseException(WiltException.createAttNoName);
685 : }
686 :
687 : if (rev == null) {
688 1 : return _raiseException(WiltException.createAttNoRev);
689 : }
690 :
691 : if (contentType == null) {
692 1 : return _raiseException(WiltException.createAttNoContentType);
693 : }
694 :
695 : if (payload == null) {
696 1 : return _raiseException(WiltException.createAttNoPayload);
697 : }
698 :
699 : /**
700 : * Set the headers
701 : */
702 1 : final Map headers = new Map<String, String>();
703 1 : headers["Content-Type"] = contentType;
704 :
705 : /**
706 : * Make the PUT request
707 : */
708 : String url;
709 1 : if (rev != '') {
710 1 : url = "$docId/$attachmentName?rev=$rev";
711 : } else {
712 1 : url = "$docId/$attachmentName";
713 : }
714 :
715 1 : url = _conditionUrl(url);
716 1 : return _httpRequest(createAttachmentt, url,
717 : data: payload, headers: headers);
718 : }
719 :
720 : /// Update an attachment on an existing document.
721 : /// contentType is in the form of a mime type e.g. 'image/png'
722 : Future updateAttachment(String docId, String attachmentName, String rev,
723 : String contentType, String payload) {
724 : /**
725 : * Check all parameters are supplied
726 : */
727 : if (docId == null) {
728 1 : return _raiseException(WiltException.updateAttNoDocId);
729 : }
730 :
731 : if (attachmentName == null) {
732 1 : return _raiseException(WiltException.updateAttNoName);
733 : }
734 :
735 : if (rev == null) {
736 1 : return _raiseException(WiltException.updateAttNoRev);
737 : }
738 :
739 : if (contentType == null) {
740 1 : return _raiseException(WiltException.updateAttNoContentType);
741 : }
742 :
743 : if (payload == null) {
744 1 : return _raiseException(WiltException.updateAttNoPayload);
745 : }
746 :
747 : /**
748 : * Set the headers
749 : */
750 1 : final Map headers = new Map<String, String>();
751 1 : headers["Content-Type"] = contentType;
752 :
753 1 : String url = "$docId/$attachmentName?rev=$rev";
754 :
755 1 : url = _conditionUrl(url);
756 1 : return _httpRequest(updateAttachmentt, url,
757 : data: payload, headers: headers);
758 : }
759 :
760 : /// Delete an attachment
761 : Future deleteAttachment(String docId, String attachmentName, String rev) {
762 : if (docId == null) {
763 1 : return _raiseException(WiltException.deleteAttNoDocId);
764 : }
765 :
766 : if (attachmentName == null) {
767 1 : return _raiseException(WiltException.deleteAttNoName);
768 : }
769 :
770 : if (rev == null) {
771 1 : return _raiseException(WiltException.deleteAttNoRev);
772 : }
773 :
774 1 : String url = "$docId/$attachmentName?rev=$rev";
775 :
776 1 : url = _conditionUrl(url);
777 1 : return _httpRequest(deleteAttachmentt, url);
778 : }
779 :
780 : /// Get an attachment
781 : Future getAttachment(String docId, String attachmentName) {
782 : if (docId == null) {
783 1 : return _raiseException(WiltException.getAttNoDocId);
784 : }
785 :
786 : if (attachmentName == null) {
787 1 : return _raiseException(WiltException.getAttNoName);
788 : }
789 :
790 1 : String url = "$docId/$attachmentName";
791 :
792 1 : url = _conditionUrl(url);
793 1 : return _httpRequest(getAttachmentt, url);
794 : }
795 :
796 : /// Change notification start, see the WiltChangeNotification class for more details
797 : ///
798 : /// If a database name is not supplied the currently selected database is used.
799 : ///
800 : /// If auth credentials are not set raise an exception.
801 : void startChangeNotification(
802 : [WiltChangeNotificationParameters parameters = null,
803 : String databaseName = null]) {
804 1 : if (_user == null) {
805 1 : throw new WiltException(WiltException.cnNoAuth);
806 : }
807 : String name;
808 : if (databaseName == null) {
809 1 : name = db;
810 : } else {
811 : name = databaseName;
812 : }
813 :
814 1 : changeNotificationDbName = name;
815 2 : _changeNotifier = new _WiltChangeNotification(
816 4 : _host, _port, _scheme, _httpAdapter, name, parameters);
817 : }
818 :
819 : /// Change notification stop, see the WiltChangeNotification class for more details
820 : ///
821 : /// Note that this destroys the internal changeNotifier object which can only be
822 : /// reinstated by a call to startChangeNotification.
823 : void stopChangeNotification() {
824 2 : _changeNotifier.stopNotifications();
825 1 : _changeNotifier = null;
826 1 : changeNotificationDbName = null;
827 : }
828 :
829 : /// Change the parameter set for change notifications.
830 : ///
831 : /// Note that database name, host, port and scheme are not changeable.
832 : void updateChangeNotificationParameters(
833 : WiltChangeNotificationParameters parameters) {
834 : if (parameters == null) {
835 0 : throw new WiltException(WiltException.updateCnpNoParams);
836 : }
837 :
838 0 : if (_changeNotifier == null) {
839 0 : throw new WiltException(WiltException.updateCnpNoNotifier);
840 : }
841 :
842 0 : _changeNotifier.parameters = parameters;
843 : }
844 :
845 : /// Pause change notifications
846 : void pauseChangeNotifications() {
847 2 : _changeNotifier.pause = true;
848 2 : _changeNotifier.stopNotifications();
849 : }
850 :
851 : /// Restart change notifications after a pause
852 : void restartChangeNotifications() {
853 2 : _changeNotifier.pause = false;
854 2 : _changeNotifier.restartChangeNotifications();
855 : }
856 :
857 : /// Authentication.
858 : /// Updates the login credentials in Wilt that will be used for all further
859 : /// requests to CouchDB. Both user name and password must be set, even if one
860 : /// or the other is '' i.e empty. After logging in all communication with CouchDB
861 : /// is made using the selected authentication method.
862 : void login(String user, String password) {
863 : if ((user == null) || (password == null)) {
864 1 : throw new WiltException(WiltException.loginWrongParams);
865 : }
866 :
867 1 : _user = user;
868 1 : _password = password;
869 1 : authenticationType = authBasic;
870 :
871 : /* Set the auth details for change notification */
872 5 : _httpAdapter.notificationAuthParams(_user, _password, authenticationType);
873 : }
874 :
875 : /// Ask CouchDB to generate document Id's.
876 : Future generateIds([int amount = 10]) {
877 1 : if (amount < 1) {
878 1 : return _raiseException(WiltException.genIdsAmount);
879 : }
880 :
881 : String url = uuids;
882 :
883 2 : url = url + "?count=${amount}";
884 :
885 1 : return _httpRequest(generateIdss, url);
886 : }
887 : }
|