Comparing version 4.0.7 to 4.0.8
@@ -6,7 +6,7 @@ const querystring = require('querystring') | ||
module.exports = (jsonRequest, dbName) => { | ||
validateString(dbName, 'dbName') | ||
const API = { | ||
docUrl: docId => { | ||
if (typeof docId !== 'string' || docId.length === 0) { | ||
throw new TypeError('doc id must be a non-empty string') | ||
} | ||
validateString(docId, 'doc id') | ||
if (docId.indexOf('_design/') === 0) { | ||
@@ -18,121 +18,103 @@ return '/' + dbName + '/_design/' + encodeURIComponent(docId.substr(8)) | ||
}, | ||
info: () => { | ||
return jsonRequest('GET', `/${dbName}`) | ||
.then(res => res.data) | ||
info: async () => { | ||
const res = await jsonRequest('GET', `/${dbName}`) | ||
return res.data | ||
}, | ||
get: (docId, revId) => { | ||
get: async (docId, revId) => { | ||
var url = API.docUrl(docId) | ||
if (typeof revId === 'string') url += `?rev=${revId}` | ||
return jsonRequest('GET', url) | ||
.then(res => { | ||
if (res.statusCode === 200) return res.data | ||
else throw errors_.buildFromRes(res, `error getting doc ${docId}`) | ||
}) | ||
const res = await jsonRequest('GET', url) | ||
if (res.statusCode === 200) return res.data | ||
else throw errors_.buildFromRes(res, `error getting doc ${docId}`) | ||
}, | ||
exists: docId => { | ||
return jsonRequest('GET', API.docUrl(docId)) | ||
.then(res => { | ||
exists: async docId => { | ||
try { | ||
const res = await jsonRequest('GET', API.docUrl(docId)) | ||
if (res.statusCode === 200) return true | ||
else throw errors_.buildFromRes(res, `error getting doc ${docId}`) | ||
}) | ||
.catch(err => { | ||
} catch (err) { | ||
if (err.statusCode === 404) return false | ||
else throw err | ||
}) | ||
} | ||
}, | ||
put: (doc) => { | ||
return jsonRequest('PUT', API.docUrl(doc._id), doc) | ||
.then(res => { | ||
if ([ 200, 201 ].includes(res.statusCode)) { | ||
return res.data | ||
} else { | ||
throw errors_.buildFromRes(res, `error putting doc ${doc._id}`) | ||
} | ||
}) | ||
put: async doc => { | ||
const res = await jsonRequest('PUT', API.docUrl(doc._id), doc) | ||
if (res.statusCode === 200 || res.statusCode === 201) return res.data | ||
else throw errors_.buildFromRes(res, `error putting doc ${doc._id}`) | ||
}, | ||
post: (doc) => { | ||
return jsonRequest('POST', `/${dbName}`, doc) | ||
.then(res => { | ||
if (res.statusCode === 201) { | ||
return res.data | ||
} else if (doc._id) { | ||
throw errors_.buildFromRes(res, `error posting doc ${doc._id}`) | ||
} else { | ||
throw errors_.buildFromRes(res, `error posting new doc`) | ||
} | ||
}) | ||
post: async doc => { | ||
const res = await jsonRequest('POST', `/${dbName}`, doc) | ||
if (res.statusCode === 201) { | ||
return res.data | ||
} else if (doc._id) { | ||
throw errors_.buildFromRes(res, `error posting doc ${doc._id}`) | ||
} else { | ||
throw errors_.buildFromRes(res, `error posting new doc`) | ||
} | ||
}, | ||
batch: (doc) => { | ||
batch: async doc => { | ||
const path = `/${dbName}?batch=ok` | ||
return jsonRequest('POST', path, doc) | ||
.then(res => { | ||
if (res.statusCode === 202) { | ||
return res.data | ||
} else if (doc._id) { | ||
throw errors_.buildFromRes(res, `error batch posting doc ${doc._id}`) | ||
} else { | ||
throw errors_.buildFromRes(res, `error batch posting new doc`) | ||
} | ||
}) | ||
const res = await jsonRequest('POST', path, doc) | ||
if (res.statusCode === 202) { | ||
return res.data | ||
} else if (doc._id) { | ||
throw errors_.buildFromRes(res, `error batch posting doc ${doc._id}`) | ||
} else { | ||
throw errors_.buildFromRes(res, `error batch posting new doc`) | ||
} | ||
}, | ||
update: (docId, fn) => { | ||
update: async (docId, fn) => { | ||
const db = API | ||
const tryIt = () => { | ||
return db.get(docId) | ||
.catch(err => { | ||
if (err.statusCode === 404) { | ||
return { _id: docId } | ||
} else { | ||
throw err | ||
} | ||
}) | ||
.then(doc => db.put(fn(doc))) | ||
.then(res => { | ||
if (res.ok) { | ||
return res | ||
} else { | ||
return tryIt() | ||
} | ||
}) | ||
const tryIt = async () => { | ||
let doc | ||
try { | ||
doc = await db.get(docId) | ||
} catch (err) { | ||
if (err.statusCode === 404) doc = { _id: docId } | ||
else throw err | ||
} | ||
const res = await db.put(fn(doc)) | ||
if (res.ok) return res | ||
else return tryIt() | ||
} | ||
return tryIt() | ||
}, | ||
delete: (docId, rev) => { | ||
delete: async (docId, rev) => { | ||
const url = API.docUrl(docId) + '?rev=' + encodeURIComponent(rev) | ||
return jsonRequest('DELETE', url) | ||
.then(res => { | ||
if (res.statusCode === 200) { | ||
return res.data | ||
} else { | ||
throw errors_.buildFromRes(res, `error deleting doc ${docId}`) | ||
} | ||
}) | ||
const res = await jsonRequest('DELETE', url) | ||
if (res.statusCode === 200) return res.data | ||
else throw errors_.buildFromRes(res, `error deleting doc ${docId}`) | ||
}, | ||
// Based on http://stackoverflow.com/a/16827094/3324977 | ||
undelete: docId => { | ||
// Verify that it's indeed a deleted document | ||
return API.get(docId) | ||
.then(res => { | ||
undelete: async docId => { | ||
try { | ||
// Verify that it's indeed a deleted document: if get doesn't throw, there is nothing to undelete | ||
await API.get(docId) | ||
throw errors_.new("can't undelete an non-deleted document", 400, docId) | ||
}) | ||
.catch(err => { | ||
} catch (err) { | ||
if (err.statusCode !== 404 || err.body.reason !== 'deleted') throw err | ||
var url = API.docUrl(docId) + '?revs=true&open_revs=all' | ||
return jsonRequest('GET', url) | ||
.then(res => { | ||
const data = res.data[0].ok | ||
const currentRev = data._rev | ||
const preDeleteRevNum = data._revisions.start - 1 | ||
const preDeleteRevId = data._revisions.ids[1] | ||
const preDeleteRev = preDeleteRevNum + '-' + preDeleteRevId | ||
return API.get(docId, preDeleteRev) | ||
.then(preDeleteDoc => { | ||
preDeleteDoc._rev = currentRev | ||
return API.put(preDeleteDoc) | ||
}) | ||
}) | ||
}) | ||
const res = await jsonRequest('GET', url) | ||
const data = res.data[0].ok | ||
const currentRev = data._rev | ||
const preDeleteRevNum = data._revisions.start - 1 | ||
const preDeleteRevId = data._revisions.ids[1] | ||
const preDeleteRev = preDeleteRevNum + '-' + preDeleteRevId | ||
const preDeleteDoc = await API.get(docId, preDeleteRev) | ||
preDeleteDoc._rev = currentRev | ||
return API.put(preDeleteDoc) | ||
} | ||
}, | ||
bulk: docs => { | ||
bulk: async docs => { | ||
const url = `/${dbName}/_bulk_docs` | ||
@@ -145,25 +127,19 @@ | ||
if (!isPlainObject(doc)) { | ||
const err = errors_.new('invalid bulk doc', 400, { doc, index: i }) | ||
return Promise.reject(err) | ||
throw errors_.new('invalid bulk doc', 400, { doc, index: i }) | ||
} | ||
} | ||
return jsonRequest('POST', url, { docs }) | ||
.then(res => { | ||
if (res.statusCode === 201) { | ||
for (let i = 0; i < res.data.length; i++) { | ||
if (res.data[i].error != null) { | ||
throw errors_.new('bulk response contains errors', 400, res.data) | ||
} | ||
} | ||
return res.data | ||
} else { | ||
throw errors_.buildFromRes(res, `error posting to _bulk_docs`) | ||
const res = await jsonRequest('POST', url, { docs }) | ||
if (res.statusCode !== 201) throw errors_.buildFromRes(res, `error posting to _bulk_docs`) | ||
for (let i = 0; i < res.data.length; i++) { | ||
if (res.data[i].error != null) { | ||
throw errors_.new('bulk response contains errors', 400, res.data) | ||
} | ||
}) | ||
} | ||
return res.data | ||
}, | ||
buildQueryString: (query = {}) => { | ||
if (!isPlainObject(query)) { | ||
throw new Error(`query should be an object: ${JSON.stringify(query)}`) | ||
} | ||
validatePlainObject(query, 'query') | ||
@@ -183,73 +159,74 @@ const q = {} | ||
}, | ||
viewQuery: (path, query) => { | ||
viewQuery: async (path, query) => { | ||
const qs = API.buildQueryString(query) | ||
const url = `/${dbName}/${path}?${qs}` | ||
return jsonRequest('GET', url) | ||
.then(res => { | ||
if (res.statusCode === 200) return res.data | ||
else throw errors_.buildFromRes(res, `error reading view ${path}`) | ||
}) | ||
const res = await jsonRequest('GET', url) | ||
if (res.statusCode === 200) return res.data | ||
else throw errors_.buildFromRes(res, `error reading view ${path}`) | ||
}, | ||
view: (designName, viewName, query) => { | ||
view: async (designName, viewName, query) => { | ||
return API.viewQuery(`_design/${designName}/_view/${viewName}`, query) | ||
}, | ||
allDocs: query => { | ||
allDocs: async query => { | ||
return API.viewQuery('_all_docs', query) | ||
}, | ||
viewKeysQuery: (path, keys, query) => { | ||
viewKeysQuery: async (path, keys, query) => { | ||
const qs = API.buildQueryString(query) | ||
const url = `/${dbName}/${path}?${qs}` | ||
return jsonRequest('POST', url, { keys }) | ||
.then(res => { | ||
if (res.statusCode === 200) return res.data | ||
else throw errors_.buildFromRes(res, `error reading view ${path}`) | ||
}) | ||
const res = await jsonRequest('POST', url, { keys }) | ||
if (res.statusCode === 200) return res.data | ||
else throw errors_.buildFromRes(res, `error reading view ${path}`) | ||
}, | ||
viewKeys: (designName, viewName, keys, query) => { | ||
viewKeys: async (designName, viewName, keys, query) => { | ||
const path = `_design/${designName}/_view/${viewName}` | ||
return API.viewKeysQuery(path, keys, query) | ||
}, | ||
// http://docs.couchdb.org/en/latest/api/database/bulk-api.html#post--db-_all_docs | ||
allDocsKeys: (keys, query) => { | ||
allDocsKeys: async (keys, query) => { | ||
return API.viewKeysQuery('_all_docs', keys, query) | ||
}, | ||
fetch: keys => { | ||
return API.viewKeysQuery('_all_docs', keys, { include_docs: true }) | ||
.then(({ rows }) => { | ||
const docs = [] | ||
const errors = [] | ||
for (const row of rows) { | ||
if (row.error) errors.push(row) | ||
else if (row.value.deleted) errors.push({ key: row.key, error: 'deleted' }) | ||
else docs.push(row.doc) | ||
} | ||
if (errors.length > 0) { | ||
throw errors_.new('docs fetch errors', 400, { keys, errors }) | ||
} | ||
return docs | ||
}) | ||
fetch: async keys => { | ||
const { rows } = await API.viewKeysQuery('_all_docs', keys, { include_docs: true }) | ||
const docs = [] | ||
const errors = [] | ||
for (const row of rows) { | ||
if (row.error) errors.push(row) | ||
else if (row.value.deleted) errors.push({ key: row.key, error: 'deleted' }) | ||
else docs.push(row.doc) | ||
} | ||
if (errors.length > 0) { | ||
throw errors_.new('docs fetch errors', 400, { keys, errors }) | ||
} | ||
return docs | ||
}, | ||
listRevs: docId => { | ||
listRevs: async docId => { | ||
const url = API.docUrl(docId) + '?revs_info=true' | ||
return jsonRequest('GET', url) | ||
.then(res => res.data._revs_info) | ||
const res = await jsonRequest('GET', url) | ||
return res.data._revs_info | ||
}, | ||
revertLastChange: docId => { | ||
return API.listRevs(docId) | ||
.then((revsInfo) => { | ||
const currentRevInfo = revsInfo[0] | ||
// Select only the previous one | ||
const candidatesRevsInfo = revsInfo.slice(1, 2) | ||
return recover(API, docId, candidatesRevsInfo, currentRevInfo) | ||
}) | ||
revertLastChange: async docId => { | ||
const revsInfo = await API.listRevs(docId) | ||
const currentRevInfo = revsInfo[0] | ||
// Select only the previous one | ||
const candidatesRevsInfo = revsInfo.slice(1, 2) | ||
return recover(API, docId, candidatesRevsInfo, currentRevInfo) | ||
}, | ||
revertToLastVersionWhere: (docId, testFn) => { | ||
return API.listRevs(docId) | ||
.then((revsInfo) => { | ||
const currentRevInfo = revsInfo[0] | ||
const candidatesRevsInfo = revsInfo.slice(1) | ||
return recover(API, docId, candidatesRevsInfo, currentRevInfo, testFn) | ||
}) | ||
revertToLastVersionWhere: async (docId, testFn) => { | ||
const revsInfo = await API.listRevs(docId) | ||
const currentRevInfo = revsInfo[0] | ||
const candidatesRevsInfo = revsInfo.slice(1) | ||
return recover(API, docId, candidatesRevsInfo, currentRevInfo, testFn) | ||
}, | ||
changes: (query = {}) => { | ||
changes: async (query = {}) => { | ||
const q = {} | ||
@@ -263,7 +240,5 @@ changesQueryKeys.forEach(key => { | ||
return jsonRequest('GET', path) | ||
.then(res => { | ||
if (res.statusCode === 200) return res.data | ||
else throw errors_.buildFromRes(res, `error reading _changes`) | ||
}) | ||
const res = await jsonRequest('GET', path) | ||
if (res.statusCode === 200) return res.data | ||
else throw errors_.buildFromRes(res, `error reading _changes`) | ||
} | ||
@@ -310,1 +285,15 @@ } | ||
} | ||
const validateString = (str, label) => { | ||
if (typeof str !== 'string' || str.length === 0) { | ||
let errMessage = `invalid ${label}` | ||
if (str != null) errMessage += ` ${str} (${typeof str})` | ||
throw new TypeError(errMessage) | ||
} | ||
} | ||
const validatePlainObject = (obj, label) => { | ||
if (!isPlainObject(obj)) { | ||
throw new TypeError(`invalid ${label} object: ${JSON.stringify(obj)} (${typeof obj})`) | ||
} | ||
} |
@@ -23,3 +23,3 @@ { | ||
], | ||
"version": "4.0.7", | ||
"version": "4.0.8", | ||
"main": "lib/cot.js", | ||
@@ -26,0 +26,0 @@ "dependencies": { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
31649
507