1- // Copyright © 2015, 2019 IBM Corp. All rights reserved.
1+ // Copyright © 2015, 2021 IBM Corp. All rights reserved.
22//
33// Licensed under the Apache License, Version 2.0 (the "License");
44// you may not use this file except in compliance with the License.
@@ -21,6 +21,8 @@ var nanodebug = require('debug')('nano');
2121
2222const Client = require ( './lib/client.js' ) ;
2323const BasePlugin = require ( './plugins/base.js' ) ;
24+ const INVALID_DOC_ID_MSG = 'Invalid document ID' ;
25+ const INVALID_ATT_MSG = 'Invalid attachment name' ;
2426
2527Cloudant . BasePlugin = BasePlugin ; // expose base plugin
2628
@@ -165,6 +167,136 @@ function Cloudant(options, callback) {
165167 body : query } , callback ) ;
166168 } ;
167169
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+
168300 // Partitioned Databases
169301 // ---------------------
170302
@@ -247,6 +379,13 @@ function Cloudant(options, callback) {
247379 obj . index = index ;
248380 obj . index . del = index_del ; // eslint-disable-line camelcase
249381 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 ;
250389
251390 obj . partitionInfo = partitionInfo ;
252391 obj . partitionedFind = partitionedFind ;
0 commit comments