diff --git a/packages/cozy-pouch-link/src/mango.js b/packages/cozy-pouch-link/src/mango.js index f6fb351e3..360a031b0 100644 --- a/packages/cozy-pouch-link/src/mango.js +++ b/packages/cozy-pouch-link/src/mango.js @@ -53,12 +53,25 @@ export const getIndexNameFromFields = (fields, partialFilter) => { const indexName = `by_${fields.join('_and_')}` if (partialFilter) { - return `${indexName}_filter_(${makeKeyFromPartialFilter(partialFilter)})` + const filterKey = makeKeyFromPartialFilter(partialFilter) + return sanitizeIndexName(`${indexName}_filter_(${filterKey})`) } return indexName } +/** + * Sanitize an index name to remove characters that are not compatible with + * CouchDB design document naming conventions (e.g. "/" breaks CouchDB > 3.3.3) + * https://github.com/linagora/cozy-client/issues/1667 + * + * @param {string} name - The index name to sanitize + * @returns {string} The sanitized index name + */ +const sanitizeIndexName = name => { + return name.replace(/%/g, '%25').replace(/\//g, '%2F') +} + /** * @function * @description Compute fields that should be indexed for a mango diff --git a/packages/cozy-stack-client/src/mangoIndex.js b/packages/cozy-stack-client/src/mangoIndex.js index c75be3e64..661dbd72d 100644 --- a/packages/cozy-stack-client/src/mangoIndex.js +++ b/packages/cozy-stack-client/src/mangoIndex.js @@ -98,12 +98,25 @@ export const getIndexNameFromFields = (fields, partialFilter) => { const indexName = `by_${fields.join('_and_')}` if (partialFilter) { - return `${indexName}_filter_(${makeKeyFromPartialFilter(partialFilter)})` + const filterKey = makeKeyFromPartialFilter(partialFilter) + return sanitizeIndexName(`${indexName}_filter_(${filterKey})`) } return indexName } +/** + * Sanitize an index name to remove characters that are not compatible with + * CouchDB design document naming conventions (e.g. "/" breaks CouchDB > 3.3.3) + * https://github.com/linagora/cozy-client/issues/1667 + * + * @param {string} name - The index name to sanitize + * @returns {string} The sanitized index name + */ +const sanitizeIndexName = name => { + return name.replace(/%/g, '%25').replace(/\//g, '%2F') +} + /** * Transform sort into Array * diff --git a/packages/cozy-stack-client/src/mangoIndex.spec.js b/packages/cozy-stack-client/src/mangoIndex.spec.js index 0ded7cd07..d523a15aa 100644 --- a/packages/cozy-stack-client/src/mangoIndex.spec.js +++ b/packages/cozy-stack-client/src/mangoIndex.spec.js @@ -255,6 +255,34 @@ describe('getIndexNameFromFields', () => { ) }) + it('should sanitize slash characters from index name', () => { + const partialFilter = { + _id: { + $nin: ['io.cozy.files.trash-dir', 'io.cozy.files.shared-drives-dir'] + }, + path: { + $or: [{ $exists: false }, { $nin: ['/Settings'] }] + } + } + + const indexName = getIndexNameFromFields(fields, partialFilter) + expect(indexName).not.toContain('/') + expect(indexName).toContain('%2FSettings') + }) + + it('should produce different index names for values with and without slash', () => { + const withSlash = { + path: { $nin: ['/Settings'] } + } + const withoutSlash = { + path: { $nin: ['Settings'] } + } + + const nameWithSlash = getIndexNameFromFields(fields, withSlash) + const nameWithoutSlash = getIndexNameFromFields(fields, withoutSlash) + expect(nameWithSlash).not.toEqual(nameWithoutSlash) + }) + it('should return index fields with partial filter with $nor inside sub condition', () => { const partialFilter = { $nor: [