1
- // Copyright © 2015, 2019 IBM Corp. All rights reserved.
1
+ // Copyright © 2015, 2021 IBM Corp. All rights reserved.
2
2
//
3
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
4
// you may not use this file except in compliance with the License.
@@ -21,6 +21,8 @@ var nanodebug = require('debug')('nano');
21
21
22
22
const Client = require ( './lib/client.js' ) ;
23
23
const BasePlugin = require ( './plugins/base.js' ) ;
24
+ const INVALID_DOC_ID_MSG = 'Invalid document ID' ;
25
+ const INVALID_ATT_MSG = 'Invalid attachment name' ;
24
26
25
27
Cloudant . BasePlugin = BasePlugin ; // expose base plugin
26
28
@@ -165,6 +167,136 @@ function Cloudant(options, callback) {
165
167
body : query } , callback ) ;
166
168
} ;
167
169
170
+ // Encode '/' path separator if it exists within the document ID
171
+ // or attachment name e.g. _design//foo will result in _design/%2Ffoo
172
+ function encodePathSeparator ( docName ) {
173
+ if ( docName . includes ( '/' ) ) {
174
+ return docName . replace ( / \/ / g, encodeURIComponent ( '/' ) ) ;
175
+ }
176
+ return docName ;
177
+ }
178
+
179
+ // Validate document ID during document requests.
180
+ // Raises an error if the ID is an `_` prefixed name
181
+ // that isn't either `_design` or `_local`.
182
+ function assertDocumentTypeId ( docName ) {
183
+ if ( docName && docName . startsWith ( '_' ) ) {
184
+ const possibleDocPrefixes = [ '_local/' , '_design/' ] ;
185
+
186
+ for ( let docPrefix of possibleDocPrefixes ) {
187
+ if ( docName . startsWith ( docPrefix ) && docName !== docPrefix ) {
188
+ // encode '/' if it exists after the document prefix
189
+ return docPrefix + encodePathSeparator ( docName . slice ( docPrefix . length ) ) ;
190
+ }
191
+ }
192
+ return new Error ( `${ INVALID_DOC_ID_MSG } : ${ docName } ` ) ;
193
+ }
194
+ return docName ;
195
+ }
196
+
197
+ // Validate attachment name during attachment requests.
198
+ // Raises an error if the name has a `_` prefixed name
199
+ function assertValidAttachmentName ( attName ) {
200
+ if ( attName && attName . startsWith ( '_' ) ) {
201
+ const error = new Error ( `${ INVALID_ATT_MSG } : ${ attName } ` ) ;
202
+ return error ;
203
+ } else if ( attName && attName . includes ( '/' ) ) {
204
+ // URI encode slashes in attachment name
205
+ attName = encodePathSeparator ( attName ) ;
206
+ return attName ;
207
+ }
208
+ return attName ;
209
+ }
210
+
211
+ function callbackError ( result , callback ) {
212
+ if ( callback ) {
213
+ return callback ( result , null ) ;
214
+ }
215
+ return Promise . reject ( result ) ;
216
+ }
217
+
218
+ var getDoc = function getDoc ( docName , qs0 , callback0 ) {
219
+ const { opts, callback} = getCallback ( qs0 , callback0 ) ;
220
+ var docResult = assertDocumentTypeId ( docName ) ;
221
+ if ( docResult instanceof Error ) {
222
+ return callbackError ( docResult , callback ) ;
223
+ } else {
224
+ return nano . _use ( db ) . get ( docResult , opts , callback ) ;
225
+ }
226
+ } ;
227
+
228
+ var headDoc = function headDoc ( docName , callback0 ) {
229
+ const { callback} = getCallback ( callback0 ) ;
230
+ var docResult = assertDocumentTypeId ( docName ) ;
231
+ if ( docResult instanceof Error ) {
232
+ return callbackError ( docResult , callback ) ;
233
+ } else {
234
+ return nano . _use ( db ) . head ( docResult , callback ) ;
235
+ }
236
+ } ;
237
+
238
+ var getAttachment = function getAttachment ( docName , attachmentName , qs0 , callback0 ) {
239
+ const { opts, callback} = getCallback ( qs0 , callback0 ) ;
240
+ var docResult = assertDocumentTypeId ( docName ) ;
241
+ var attResult = assertValidAttachmentName ( attachmentName ) ;
242
+ if ( docResult instanceof Error ) {
243
+ return callbackError ( docResult , callback ) ;
244
+ } else if ( attResult instanceof Error ) {
245
+ return callbackError ( attResult , callback ) ;
246
+ } else {
247
+ return nano . _use ( db ) . attachment . get ( docResult , attResult , opts , callback ) ;
248
+ }
249
+ } ;
250
+
251
+ var deleteDoc = function deleteDoc ( docName , qs0 , callback0 ) {
252
+ const { opts, callback} = getCallback ( qs0 , callback0 ) ;
253
+ var docResult = assertDocumentTypeId ( docName ) ;
254
+ if ( docResult instanceof Error ) {
255
+ return callbackError ( docResult , callback ) ;
256
+ } else {
257
+ return nano . _use ( db ) . destroy ( docResult , opts , callback ) ;
258
+ }
259
+ } ;
260
+
261
+ var deleteAttachment = function deleteAttachment ( docName , attachmentName , qs0 , callback0 ) {
262
+ const { opts, callback} = getCallback ( qs0 , callback0 ) ;
263
+ var docResult = assertDocumentTypeId ( docName ) ;
264
+ var attResult = assertValidAttachmentName ( attachmentName ) ;
265
+ if ( docResult instanceof Error ) {
266
+ return callbackError ( docResult , callback ) ;
267
+ } else if ( attResult instanceof Error ) {
268
+ return callbackError ( attResult , callback ) ;
269
+ } else {
270
+ return nano . _use ( db ) . attachment . destroy ( docResult , attResult , opts , callback ) ;
271
+ }
272
+ } ;
273
+
274
+ var putAttachment = function putAttachment ( docName , attachmentName , att , contentType , qs0 , callback0 ) {
275
+ const { opts, callback} = getCallback ( qs0 , callback0 ) ;
276
+ var docResult = assertDocumentTypeId ( docName ) ;
277
+ var attResult = assertValidAttachmentName ( attachmentName ) ;
278
+ if ( docResult instanceof Error ) {
279
+ return callbackError ( docResult , callback ) ;
280
+ } else if ( attResult instanceof Error ) {
281
+ return callbackError ( attResult , callback ) ;
282
+ } else {
283
+ return nano . _use ( db ) . attachment . insert ( docResult , attResult , att , contentType , opts , callback ) ;
284
+ }
285
+ } ;
286
+
287
+ var putDoc = function putDoc ( docBody , qs0 , callback0 ) {
288
+ const { opts, callback} = getCallback ( qs0 , callback0 ) ;
289
+ if ( typeof opts === 'string' ) {
290
+ var docResult = assertDocumentTypeId ( opts ) ;
291
+ if ( docResult instanceof Error ) {
292
+ return callbackError ( docResult , callback ) ;
293
+ } else {
294
+ return nano . _use ( db ) . insert ( docBody , docResult , callback ) ;
295
+ }
296
+ }
297
+ return nano . _use ( db ) . insert ( docBody , opts , callback ) ;
298
+ } ;
299
+
168
300
// Partitioned Databases
169
301
// ---------------------
170
302
@@ -247,6 +379,13 @@ function Cloudant(options, callback) {
247
379
obj . index = index ;
248
380
obj . index . del = index_del ; // eslint-disable-line camelcase
249
381
obj . find = find ;
382
+ obj . destroy = deleteDoc ;
383
+ obj . get = getDoc ;
384
+ obj . head = headDoc ;
385
+ obj . insert = putDoc ;
386
+ obj . attachment . destroy = deleteAttachment ;
387
+ obj . attachment . get = getAttachment ;
388
+ obj . attachment . insert = putAttachment ;
250
389
251
390
obj . partitionInfo = partitionInfo ;
252
391
obj . partitionedFind = partitionedFind ;
0 commit comments