| // Licensed under the Apache License, Version 2.0 (the 'License'); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| 'use strict' |
| |
| const u = require('url') |
| const assert = require('assert') |
| const querystring = require('querystring') |
| const request = require('request') |
| const errs = require('errs') |
| const follow = require('cloudant-follow') |
| const logger = require('./logger') |
| |
| function isEmpty (val) { |
| return val == null || !(Object.keys(val) || val).length |
| } |
| |
| function getCallback (opts, callback) { |
| if (typeof opts === 'function') { |
| callback = opts |
| opts = {} |
| } |
| opts = opts || {} |
| return { |
| opts, |
| callback |
| } |
| } |
| |
| module.exports = exports = function dbScope (cfg) { |
| let serverScope = {} |
| |
| if (typeof cfg === 'string') { |
| cfg = { url: cfg } |
| } |
| |
| assert.strictEqual(typeof cfg, 'object', |
| 'You must specify the endpoint url when invoking this module') |
| assert.ok(/^https?:/.test(cfg.url), 'url is not valid') |
| |
| cfg = Object.assign({}, cfg) |
| |
| serverScope.config = cfg |
| cfg.requestDefaults = cfg.requestDefaults || {} |
| |
| const httpAgent = (typeof cfg.request === 'function') ? cfg.request |
| : request.defaults(cfg.requestDefaults) |
| const followAgent = (typeof cfg.follow === 'function') ? cfg.follow : follow |
| const log = typeof cfg.log === 'function' ? cfg.log : logger(cfg) |
| const parseUrl = 'parseUrl' in cfg ? cfg.parseUrl : true |
| |
| function maybeExtractDatabaseComponent () { |
| if (!parseUrl) { |
| return |
| } |
| |
| const path = u.parse(cfg.url) |
| let pathArray = path.pathname.split('/').filter(function (e) { return e }) |
| const db = pathArray.pop() |
| const rootPath = path.pathname.replace(/\/?$/, '/..') |
| |
| if (db) { |
| cfg.url = urlResolveFix(cfg.url, rootPath).replace(/\/?$/, '') |
| return db |
| } |
| } |
| |
| function scrub (str) { |
| if (str) { |
| str = str.replace(/\/\/(.*)@/, '//XXXXXX:XXXXXX@') |
| } |
| return str |
| } |
| const responseHandler = function (req, opts, resolve, reject, callback) { |
| return function (err, response = { statusCode: 500 }, body = '') { |
| let parsed |
| const responseHeaders = Object.assign({ |
| uri: req.uri, |
| statusCode: response.statusCode |
| }, response.headers) |
| if (err) { |
| log({ err: 'socket', body: body, headers: responseHeaders }) |
| const returnError = errs.merge(err, { |
| message: 'error happened in your connection', |
| scope: 'socket', |
| errid: 'request' |
| }) |
| if (reject) { |
| reject(returnError) |
| } |
| if (callback) { |
| callback(returnError) |
| } |
| return |
| } |
| |
| delete responseHeaders.server |
| delete responseHeaders['content-length'] |
| |
| if (opts.dontParse) { |
| parsed = body |
| } else { |
| try { parsed = JSON.parse(body) } catch (err) { parsed = body } |
| } |
| |
| if (responseHeaders.statusCode >= 200 && responseHeaders.statusCode < 400) { |
| log({ err: null, body: parsed, headers: responseHeaders }) |
| if (resolve) { |
| resolve(parsed) |
| } |
| if (callback) { |
| callback(null, parsed, responseHeaders) |
| } |
| return |
| } |
| |
| log({ err: 'couch', body: parsed, headers: responseHeaders }) |
| |
| // cloudant stacktrace |
| if (typeof parsed === 'string') { |
| parsed = { message: parsed } |
| } |
| |
| if (!parsed.message && (parsed.reason || parsed.error)) { |
| parsed.message = (parsed.reason || parsed.error) |
| } |
| |
| // fix cloudant issues where they give an erlang stacktrace as js |
| delete parsed.stack |
| |
| // scrub credentials |
| req.uri = scrub(req.uri) |
| responseHeaders.uri = scrub(responseHeaders.uri) |
| if (req.headers.cookie) { |
| req.headers.cookie = 'XXXXXXX' |
| } |
| |
| let errors = errs.merge({ |
| message: 'couch returned ' + responseHeaders.statusCode, |
| scope: 'couch', |
| statusCode: responseHeaders.statusCode, |
| request: req, |
| headers: responseHeaders, |
| errid: 'non_200' |
| }, errs.create(parsed)) |
| |
| if (reject) { |
| reject(errors) |
| } |
| if (callback) { |
| callback(errors) |
| } |
| } |
| } |
| |
| function relax (opts, callback) { |
| if (typeof opts === 'function') { |
| callback = opts |
| opts = { path: '' } |
| } |
| |
| if (typeof opts === 'string') { |
| opts = { path: opts } |
| } |
| |
| if (!opts) { |
| opts = { path: '' } |
| callback = null |
| } |
| |
| const qs = Object.assign({}, opts.qs) |
| |
| const headers = { |
| 'content-type': 'application/json', |
| accept: 'application/json' |
| } |
| |
| const req = { |
| method: (opts.method || 'GET'), |
| headers: headers, |
| uri: cfg.url |
| } |
| |
| // https://github.com/mikeal/request#requestjar |
| const isJar = opts.jar || cfg.jar |
| |
| if (isJar) { |
| req.jar = isJar |
| } |
| |
| // http://wiki.apache.org/couchdb/HTTP_database_API#Naming_and_Addressing |
| if (opts.db) { |
| req.uri = urlResolveFix(req.uri, encodeURIComponent(opts.db)) |
| } |
| |
| if (opts.multipart) { |
| req.multipart = opts.multipart |
| } |
| |
| req.headers = Object.assign(req.headers, opts.headers, cfg.defaultHeaders) |
| |
| if (opts.path) { |
| req.uri += '/' + opts.path |
| } else if (opts.doc) { |
| if (!/^_design/.test(opts.doc)) { |
| // http://wiki.apache.org/couchdb/HTTP_Document_API#Naming.2FAddressing |
| req.uri += '/' + encodeURIComponent(opts.doc) |
| } else { |
| // http://wiki.apache.org/couchdb/HTTP_Document_API#Document_IDs |
| req.uri += '/' + opts.doc |
| } |
| |
| // http://wiki.apache.org/couchdb/HTTP_Document_API#Attachments |
| if (opts.att) { |
| req.uri += '/' + opts.att |
| } |
| } |
| |
| // prevent bugs where people set encoding when piping |
| if (opts.encoding !== undefined && callback) { |
| req.encoding = opts.encoding |
| delete req.headers['content-type'] |
| delete req.headers.accept |
| } |
| |
| if (opts.contentType) { |
| req.headers['content-type'] = opts.contentType |
| delete req.headers.accept |
| } |
| |
| if (opts.accept) { |
| req.headers['accept'] = opts.accept |
| } |
| |
| // http://guide.couchdb.org/draft/security.html#cookies |
| if (cfg.cookie) { |
| req.headers['X-CouchDB-WWW-Authenticate'] = 'Cookie' |
| req.headers.cookie = cfg.cookie |
| } |
| |
| // http://wiki.apache.org/couchdb/HTTP_view_API#Querying_Options |
| if (typeof opts.qs === 'object' && !isEmpty(opts.qs)) { |
| ['startkey', 'endkey', 'key', 'keys', 'start_key', 'end_key'].forEach(function (key) { |
| if (key in opts.qs) { |
| qs[key] = JSON.stringify(opts.qs[key]) |
| } |
| }) |
| req.qs = qs |
| } |
| |
| if (opts.body) { |
| if (Buffer.isBuffer(opts.body) || opts.dontStringify) { |
| req.body = opts.body |
| } else { |
| req.body = JSON.stringify(opts.body, function (key, value) { |
| // don't encode functions |
| if (typeof (value) === 'function') { |
| return value.toString() |
| } else { |
| return value |
| } |
| }) |
| } |
| } |
| |
| if (opts.form) { |
| req.headers['content-type'] = |
| 'application/x-www-form-urlencoded; charset=utf-8' |
| req.body = querystring.stringify(opts.form).toString('utf8') |
| } |
| |
| // ask request to render query string arrays as repeated values e.g. |
| // ?drilldown=["author","Dickens"]&drilldown=["publisher","Penguin"] |
| req.qsStringifyOptions = { arrayFormat: 'repeat' } |
| |
| log(req) |
| |
| if (opts.stream) { |
| // return the Request object for streaming |
| return httpAgent(req) |
| } else { |
| if (typeof callback === 'function') { |
| // return nothing - feedback via the callback function |
| httpAgent(req, responseHandler(req, opts, null, null, callback)) |
| } else { |
| // return a Promise |
| return new Promise(function (resolve, reject) { |
| httpAgent(req, responseHandler(req, opts, resolve, reject)) |
| }) |
| } |
| } |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/server/authn.html#cookie-authentication |
| function auth (username, password, callback) { |
| return relax({ |
| method: 'POST', |
| db: '_session', |
| form: { |
| name: username, |
| password: password |
| } |
| }, callback) |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/server/authn.html#post--_session |
| function session (callback) { |
| return relax({ db: '_session' }, callback) |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/server/common.html#get--_db_updates |
| function updates (qs0, callback0) { |
| const { opts, callback } = getCallback(qs0, callback0) |
| return relax({ |
| db: '_db_updates', |
| qs: opts |
| }, callback) |
| } |
| |
| function followUpdates (qs, callback) { |
| return followDb('_db_updates', qs, callback) |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/database/common.html#put--db |
| function createDb (dbName, qs0, callback0) { |
| const { opts, callback } = getCallback(qs0, callback0) |
| return relax({ db: dbName, method: 'PUT', qs: opts }, callback) |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/database/common.html#delete--db |
| function destroyDb (dbName, callback) { |
| return relax({ db: dbName, method: 'DELETE' }, callback) |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/database/common.html#get--db |
| function getDb (dbName, callback) { |
| return relax({ db: dbName }, callback) |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/server/common.html#get--_all_dbs |
| function listDbs (callback) { |
| return relax({ db: '_all_dbs' }, callback) |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/server/common.html#get--_all_dbs |
| function listDbsAsStream () { |
| return relax({ db: '_all_dbs', stream: true }) |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/database/compact.html#post--db-_compact |
| function compactDb (dbName, ddoc, callback) { |
| if (typeof ddoc === 'function') { |
| callback = ddoc |
| ddoc = null |
| } |
| return relax({ |
| db: dbName, |
| doc: '_compact', |
| att: ddoc, |
| method: 'POST' |
| }, callback) |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/database/changes.html#get--db-_changes |
| function changesDb (dbName, qs0, callback0) { |
| const { opts, callback } = getCallback(qs0, callback0) |
| return relax({ db: dbName, path: '_changes', qs: opts }, callback) |
| } |
| |
| function changesDbAsStream (dbName, opts) { |
| return relax({ db: dbName, path: '_changes', stream: true, qs: opts }) |
| } |
| |
| function followDb (dbName, qs0, callback0) { |
| const { opts, callback } = getCallback(qs0, callback0) |
| opts.db = urlResolveFix(cfg.url, encodeURIComponent(dbName)) |
| opts.httpAgent = httpAgent |
| if (typeof callback === 'function') { |
| return followAgent(opts, callback) |
| } else { |
| return new followAgent.Feed(opts) |
| } |
| } |
| |
| function _serializeAsUrl (db) { |
| if (typeof db === 'object' && db.config && db.config.url && db.config.db) { |
| return urlResolveFix(db.config.url, encodeURIComponent(db.config.db)) |
| } else { |
| const parsed = u.parse(db) |
| if (parsed.protocol) { |
| return db |
| } else { |
| return urlResolveFix(cfg.url, encodeURIComponent(db)) |
| } |
| } |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/server/common.html#post--_replicate |
| function replicateDb (source, target, opts0, callback0) { |
| const { opts, callback } = getCallback(opts0, callback0) |
| |
| // _replicate |
| opts.source = _serializeAsUrl(source) |
| opts.target = _serializeAsUrl(target) |
| |
| return relax({ db: '_replicate', body: opts, method: 'POST' }, callback) |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/server/common.html#uuids |
| function uuids (count, callback) { |
| if (typeof count === 'function') { |
| callback = count |
| count = 1 |
| } |
| return relax({ method: 'GET', path: '_uuids', qs: { count: count } }, callback) |
| } |
| |
| // http://guide.couchdb.org/draft/replication.html |
| function enableReplication (source, target, opts0, callback0) { |
| const { opts, callback } = getCallback(opts0, callback0) |
| |
| // _replicator |
| opts.source = _serializeAsUrl(source) |
| opts.target = _serializeAsUrl(target) |
| |
| return relax({ db: '_replicator', body: opts, method: 'POST' }, callback) |
| } |
| |
| // http://guide.couchdb.org/draft/replication.html |
| function queryReplication (id, opts0, callback0) { |
| const { opts, callback } = getCallback(opts0, callback0) |
| return relax({ db: '_replicator', method: 'GET', path: id, qs: opts }, callback) |
| } |
| |
| // http://guide.couchdb.org/draft/replication.html |
| function disableReplication (id, rev, opts0, callback0) { |
| const { opts, callback } = getCallback(opts0, callback0) |
| const req = { |
| db: '_replicator', |
| method: 'DELETE', |
| path: id, |
| qs: Object.assign(opts, { rev: rev }) |
| } |
| return relax(req, callback) |
| } |
| |
| function docModule (dbName) { |
| let docScope = {} |
| dbName = decodeURIComponent(dbName) |
| |
| // http://docs.couchdb.org/en/latest/api/document/common.html#put--db-docid |
| // http://docs.couchdb.org/en/latest/api/database/common.html#post--db |
| function insertDoc (doc, qs0, callback0) { |
| const req = { db: dbName, body: doc, method: 'POST' } |
| |
| let { opts, callback } = getCallback(qs0, callback0) |
| |
| if (typeof opts === 'string') { |
| opts = { docName: opts } |
| } |
| |
| if (opts) { |
| if (opts.docName) { |
| req.doc = opts.docName |
| req.method = 'PUT' |
| delete opts.docName |
| } |
| req.qs = opts |
| } |
| |
| return relax(req, callback) |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/document/common.html#delete--db-docid |
| function destroyDoc (docName, rev, callback) { |
| if (!docName) { |
| const msg = 'Invalid doc id' |
| if (callback) { |
| callback(msg, null) |
| } else { |
| return Promise.reject(msg) |
| } |
| } else { |
| return relax({ |
| db: dbName, |
| doc: docName, |
| method: 'DELETE', |
| qs: { rev: rev } |
| }, callback) |
| } |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/document/common.html#get--db-docid |
| function getDoc (docName, qs0, callback0) { |
| const { opts, callback } = getCallback(qs0, callback0) |
| |
| if (!docName) { |
| if (callback) { callback(new Error('Invalid doc id'), null) } |
| } else { |
| return relax({ db: dbName, doc: docName, qs: opts }, callback) |
| } |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/document/common.html#head--db-docid |
| function headDoc (docName, callback) { |
| if (callback) { |
| relax({ |
| db: dbName, |
| doc: docName, |
| method: 'HEAD', |
| qs: {} |
| }, callback) |
| } else { |
| // this function doesn't pass on the Promise from relax because it needs |
| // to return the headers when resolving the Promise |
| return new Promise(function (resolve, reject) { |
| relax({ |
| db: dbName, |
| doc: docName, |
| method: 'HEAD', |
| qs: {} |
| }, function (err, body, headers) { |
| if (err) { |
| reject(err) |
| } else { |
| resolve(headers) |
| } |
| }) |
| }) |
| } |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/document/common.html#copy--db-docid |
| function copyDoc (docSrc, docDest, opts0, callback0) { |
| const { opts, callback } = getCallback(opts0, callback0) |
| |
| const qs = { |
| db: dbName, |
| doc: docSrc, |
| method: 'COPY', |
| headers: { 'Destination': docDest } |
| } |
| |
| if (opts.overwrite) { |
| const p = headDoc(docDest).then( |
| function (h) { |
| if (h.etag) { |
| qs.headers.Destination += '?rev=' + |
| h.etag.substring(1, h.etag.length - 1) |
| } |
| relax(qs, callback) |
| }, |
| function (e) { |
| if (e && e.statusCode !== 404) { |
| if (callback) { |
| callback(e) |
| } else { |
| return Promise.reject(e) |
| } |
| } else { |
| relax(qs, callback) |
| } |
| } |
| ) |
| if (!callback) { |
| return p |
| } |
| } else { |
| return relax(qs, callback) |
| } |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/database/bulk-api.html#get--db-_all_docs |
| function listDoc (qs0, callback0) { |
| const { opts, callback } = getCallback(qs0, callback0) |
| return relax({ db: dbName, path: '_all_docs', qs: opts }, callback) |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/database/bulk-api.html#get--db-_all_docs |
| function listDocAsStream (opts) { |
| return relax({ db: dbName, path: '_all_docs', qs: opts, stream: true }) |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/database/bulk-api.html#post--db-_all_docs |
| function fetchDocs (docNames, qs0, callback0) { |
| const { opts, callback } = getCallback(qs0, callback0) |
| opts['include_docs'] = true |
| |
| return relax({ |
| db: dbName, |
| path: '_all_docs', |
| method: 'POST', |
| qs: opts, |
| body: docNames |
| }, callback) |
| } |
| |
| function fetchRevs (docNames, qs0, callback0) { |
| const { opts, callback } = getCallback(qs0, callback0) |
| return relax({ |
| db: dbName, |
| path: '_all_docs', |
| method: 'POST', |
| qs: opts, |
| body: docNames |
| }, callback) |
| } |
| |
| function view (ddoc, viewName, meta, qs0, callback0) { |
| const { opts, callback } = getCallback(qs0, callback0) |
| |
| if (typeof meta.stream !== 'boolean') { |
| meta.stream = false |
| } |
| |
| // prevent mutation of the client qs object by using a clone |
| const qs1 = Object.assign({}, opts) |
| |
| const viewPath = meta.viewPath || '_design/' + ddoc + '/_' + meta.type + |
| '/' + viewName |
| |
| if (meta.type === 'search') { |
| return relax({ |
| db: dbName, |
| path: viewPath, |
| method: 'POST', |
| body: qs1, |
| stream: meta.stream |
| }, callback) |
| } else if (qs1 && qs1.keys) { |
| const body = { keys: qs1.keys } |
| delete qs1.keys |
| return relax({ |
| db: dbName, |
| path: viewPath, |
| method: 'POST', |
| qs: qs1, |
| body: body, |
| stream: meta.stream |
| }, callback) |
| } else if (qs1 && qs1.queries) { |
| const body = { queries: qs1.queries } |
| delete qs1.queries |
| return relax({ |
| db: dbName, |
| path: viewPath, |
| method: 'POST', |
| qs: qs1, |
| body: body |
| }, callback) |
| } else { |
| const req = { |
| db: dbName, |
| method: meta.method || 'GET', |
| path: viewPath, |
| qs: qs1, |
| stream: meta.stream |
| } |
| |
| if (meta.body) { |
| req.body = meta.body |
| } |
| |
| return relax(req, callback) |
| } |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/ddoc/views.html#post--db-_design-ddoc-_view-view |
| function viewDocs (ddoc, viewName, qs, callback) { |
| return view(ddoc, viewName, { type: 'view' }, qs, callback) |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/ddoc/views.html#post--db-_design-ddoc-_view-view |
| function viewDocsAsStream (ddoc, viewName, qs) { |
| return view(ddoc, viewName, { type: 'view', stream: true }, qs) |
| } |
| |
| // cloudant |
| function viewSearch (ddoc, viewName, qs, callback) { |
| return view(ddoc, viewName, { type: 'search' }, qs, callback) |
| } |
| |
| // cloudant |
| function viewSearchAsStream (ddoc, viewName, qs) { |
| return view(ddoc, viewName, { type: 'search', stream: true }, qs) |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/ddoc/render.html#get--db-_design-ddoc-_show-func |
| function showDoc (ddoc, viewName, docName, qs, callback) { |
| return view(ddoc, viewName + '/' + docName, { type: 'show' }, qs, callback) |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/ddoc/render.html#put--db-_design-ddoc-_update-func-docid |
| function updateWithHandler (ddoc, viewName, docName, body, callback) { |
| if (typeof body === 'function') { |
| callback = body |
| body = undefined |
| } |
| return view(ddoc, viewName + '/' + encodeURIComponent(docName), { |
| type: 'update', |
| method: 'PUT', |
| body: body |
| }, callback) |
| } |
| |
| function viewWithList (ddoc, viewName, listName, qs, callback) { |
| return view(ddoc, listName + '/' + viewName, { |
| type: 'list' |
| }, qs, callback) |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/database/bulk-api.html#post--db-_bulksDoc |
| function bulksDoc (docs, qs0, callback0) { |
| const { opts, callback } = getCallback(qs0, callback0) |
| return relax({ |
| db: dbName, |
| path: '_bulk_docs', |
| body: docs, |
| method: 'POST', |
| qs: opts |
| }, callback) |
| } |
| |
| // http://docs.couchdb.org/en/latest/api/document/common.html#creating-multiple-attachments |
| function insertMultipart (doc, attachments, qs, callback) { |
| if (typeof qs === 'string') { |
| qs = { docName: qs } |
| } |
| qs = qs || {} |
| |
| const docName = qs.docName |
| delete qs.docName |
| |
| doc = Object.assign({ _attachments: {} }, doc) |
| |
| const multipart = [] |
| |
| attachments.forEach(function (att) { |
| doc._attachments[att.name] = { |
| follows: true, |
| length: Buffer.isBuffer(att.data) |
| ? att.data.length : Buffer.byteLength(att.data), |
| /* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */ |
| 'content_type': att.content_type |
| } |
| multipart.push({ body: att.data }) |
| }) |
| |
| multipart.unshift({ |
| 'content-type': 'application/json', |
| body: JSON.stringify(doc) |
| }) |
| |
| return relax({ |
| db: dbName, |
| method: 'PUT', |
| contentType: 'multipart/related', |
| doc: docName, |
| qs: qs, |
| multipart: multipart |
| }, callback) |
| } |
| |
| function getMultipart (docName, qs0, callback0) { |
| const { opts, callback } = getCallback(qs0, callback0) |
| opts.attachments = true |
| |
| return relax({ |
| db: dbName, |
| doc: docName, |
| encoding: null, |
| accept: 'multipart/related', |
| qs: opts |
| }, callback) |
| } |
| |
| function insertAtt (docName, attName, att, contentType, qs0, callback0) { |
| const { opts, callback } = getCallback(qs0, callback0) |
| return relax({ |
| db: dbName, |
| att: attName, |
| method: 'PUT', |
| contentType: contentType, |
| doc: docName, |
| qs: opts, |
| body: att, |
| dontStringify: true |
| }, callback) |
| } |
| |
| function insertAttAsStream (docName, attName, att, contentType, opts) { |
| return relax({ |
| db: dbName, |
| att: attName, |
| method: 'PUT', |
| contentType: contentType, |
| doc: docName, |
| qs: opts, |
| body: att, |
| stream: true, |
| dontStringify: true |
| }) |
| } |
| |
| function getAtt (docName, attName, qs0, callback0) { |
| const { opts, callback } = getCallback(qs0, callback0) |
| return relax({ |
| db: dbName, |
| att: attName, |
| doc: docName, |
| qs: opts, |
| encoding: null, |
| dontParse: true |
| }, callback) |
| } |
| |
| function getAttAsStream (docName, attName, opts) { |
| return relax({ |
| db: dbName, |
| att: attName, |
| doc: docName, |
| qs: opts, |
| stream: true, |
| encoding: null, |
| dontParse: true |
| }) |
| } |
| |
| function destroyAtt (docName, attName, qs, callback) { |
| return relax({ |
| db: dbName, |
| att: attName, |
| method: 'DELETE', |
| doc: docName, |
| qs: qs |
| }, callback) |
| } |
| |
| function find (selector, callback) { |
| return relax({ |
| db: dbName, |
| path: '_find', |
| method: 'POST', |
| body: selector |
| }, callback) |
| } |
| |
| function findAsStream (selector) { |
| return relax({ |
| db: dbName, |
| path: '_find', |
| method: 'POST', |
| body: selector, |
| stream: true |
| }) |
| } |
| |
| function createIndex (indexDef, callback) { |
| return relax({ |
| db: dbName, |
| path: '_index', |
| method: 'POST', |
| body: indexDef |
| }, callback) |
| } |
| |
| // db level exports |
| docScope = { |
| info: function (cb) { |
| return getDb(dbName, cb) |
| }, |
| replicate: function (target, opts, cb) { |
| return replicateDb(dbName, target, opts, cb) |
| }, |
| compact: function (cb) { |
| return compactDb(dbName, cb) |
| }, |
| changes: function (qs, cb) { |
| return changesDb(dbName, qs, cb) |
| }, |
| changesAsStream: function (qs) { |
| return changesDbAsStream(dbName, qs) |
| }, |
| follow: function (qs, cb) { |
| return followDb(dbName, qs, cb) |
| }, |
| auth: auth, |
| session: session, |
| insert: insertDoc, |
| get: getDoc, |
| head: headDoc, |
| copy: copyDoc, |
| destroy: destroyDoc, |
| bulk: bulksDoc, |
| list: listDoc, |
| listAsStream: listDocAsStream, |
| fetch: fetchDocs, |
| fetchRevs: fetchRevs, |
| config: { url: cfg.url, db: dbName }, |
| multipart: { |
| insert: insertMultipart, |
| get: getMultipart |
| }, |
| attachment: { |
| insert: insertAtt, |
| insertAsStream: insertAttAsStream, |
| get: getAtt, |
| getAsStream: getAttAsStream, |
| destroy: destroyAtt |
| }, |
| show: showDoc, |
| atomic: updateWithHandler, |
| updateWithHandler: updateWithHandler, |
| baseView: view, |
| search: viewSearch, |
| searchAsStream: viewSearchAsStream, |
| view: viewDocs, |
| viewAsStream: viewDocsAsStream, |
| find: find, |
| findAsStream: findAsStream, |
| createIndex: createIndex, |
| viewWithList: viewWithList, |
| server: serverScope, |
| replication: { |
| enable: function (target, opts, cb) { |
| return enableReplication(dbName, target, opts, cb) |
| }, |
| disable: function (id, revision, opts, cb) { |
| return disableReplication(id, revision, opts, cb) |
| }, |
| query: function (id, opts, cb) { |
| return queryReplication(id, opts, cb) |
| } |
| } |
| } |
| |
| docScope.view.compact = function (ddoc, cb) { |
| return compactDb(dbName, ddoc, cb) |
| } |
| |
| return docScope |
| } |
| |
| // server level exports |
| serverScope = Object.assign(serverScope, { |
| db: { |
| create: createDb, |
| get: getDb, |
| destroy: destroyDb, |
| list: listDbs, |
| listAsStream: listDbsAsStream, |
| use: docModule, |
| scope: docModule, |
| compact: compactDb, |
| replicate: replicateDb, |
| replication: { |
| enable: enableReplication, |
| disable: disableReplication, |
| query: queryReplication |
| }, |
| changes: changesDb, |
| follow: followDb, |
| followUpdates: followUpdates, |
| updates: updates |
| }, |
| use: docModule, |
| scope: docModule, |
| request: relax, |
| relax: relax, |
| dinosaur: relax, |
| auth: auth, |
| session: session, |
| updates: updates, |
| followUpdates: followUpdates, |
| uuids: uuids |
| }) |
| |
| const db = maybeExtractDatabaseComponent() |
| |
| return db ? docModule(db) : serverScope |
| } |
| |
| /* |
| * and now an ascii dinosaur |
| * _ |
| * / _) ROAR! i'm a vegan! |
| * .-^^^-/ / |
| * __/ / |
| * /__.|_|-|_| |
| * |
| * thanks for visiting! come again! |
| */ |
| |
| function urlResolveFix (couchUrl, dbName) { |
| if (/[^/]$/.test(couchUrl)) { |
| couchUrl += '/' |
| } |
| return u.resolve(couchUrl, dbName) |
| } |