Skip to content

Commit

Permalink
feat(konnectors): Avoid creating multiple konnector folders
Browse files Browse the repository at this point in the history
When the account folder already exist and has konnector reference and
not the konnector folder
  • Loading branch information
doubleface committed Dec 17, 2024
1 parent b46c9c9 commit 50e88f1
Show file tree
Hide file tree
Showing 2 changed files with 175 additions and 11 deletions.
68 changes: 58 additions & 10 deletions packages/cozy-client/src/models/konnectorFolder.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ export const ensureKonnectorFolder = async (
{ konnector, account, lang }
) => {
const permissions = client.collection(PERMISSIONS_DOCTYPE)
const fileCollection = client.collection(FILES_DOCTYPE)

const t = getLocalizer(lang)
const [adminFolder, photosFolder] = await Promise.all([
ensureMagicFolder(
Expand Down Expand Up @@ -64,6 +62,19 @@ export const ensureKonnectorFolder = async (
})
}

const mainPath = buildMainFolderPath(konnector, {
administrative: adminFolder.path,
photos: photosFolder.path
})
const mainFolder =
(await statDirectoryByPath(client, mainPath)) ||
(await createDirectoryByPath(client, mainPath))

ensureKonnectorReference({
client,
folder: mainFolder,
konnector
})
// if the previous shortcuts did not work, create the folders like we did before but with proper references
const path = buildFolderPath(konnector, account, {
administrative: adminFolder.path,
Expand All @@ -73,19 +84,13 @@ export const ensureKonnectorFolder = async (
(await statDirectoryByPath(client, path)) ||
(await createDirectoryByPath(client, path))

const { data: konnectorFolder } = await fileCollection.statById(folder.dir_id)
await Promise.all([
permissions.add(konnector, buildFolderPermission(folder)),
ensureKonnectorReference({ client, folder, konnector }),
ensureSourceAccountIdentifierReference({
client,
folder,
sourceAccountIdentifier
}),
ensureKonnectorReference({
client,
folder: konnectorFolder,
konnector
})
])

Expand Down Expand Up @@ -150,7 +155,6 @@ export const statDirectoryByPath = async (client, path) => {
* Administratif).
* @returns {String} The result path
*/

export const buildFolderPath = (konnector, account, magicFolders = {}) => {
const fullPath =
konnector?.folders?.[0]?.defaultDir || '$administrative/$konnector/$account'
Expand Down Expand Up @@ -178,6 +182,50 @@ export const buildFolderPath = (konnector, account, magicFolders = {}) => {
return `/${renderedBaseDir}/${renderedPath}`
}

/**
* Build konnector main folder path for a given konnector.
*
* If konnector.folders[0].defaultDir exists, it is used as default directory.
*
* Occurrences of following strings in base directory are replaced by:
* * `$administrative`: Administrative folder
* * `$photos`: Photos folder
*
* Occurrences of following strings in path are replaced by:
* * `$konnector`: Konnector name
*
* If no konnectors.folders[0].defaultDir is set, the default dir used is
* * `$administrative/$konnector`
*
* @param {import('../types').IOCozyKonnector} konnector Konnector document
* @param {Object<string, string>} magicFolders Object containing a mapping from folder identifiers (ex: $administrative) to their localized values (ex: Administratif).
* @returns {String} The result path
*/
export const buildMainFolderPath = (konnector, magicFolders = {}) => {
const fullPath =
konnector?.folders?.[0]?.defaultDir
?.split('/')
?.slice(0, -1)
?.join('/') || '$administrative/$konnector'
let sanitizedPath = trim(fullPath.replace(/(\/+)/g, '/'), '/')
if (!hasBaseDir(sanitizedPath)) {
sanitizedPath = '$administrative/' + sanitizedPath
}
/**
* Now that we have our sanitizedPath, we can split it in two strings
* * `baseDir` containing the baseDir path
* * `buildedSubDir` containing the rest of the path (ie the path without baseDir)
*/
const baseDir = sanitizedPath.split('/', 1)
const buildedSubDir = buildSubDir(sanitizedPath, baseDir[0])

const renderedBaseDir = renderBaseDir(baseDir[0], magicFolders)
const renderedPath = renderSubDir(buildedSubDir, {
konnector: konnector.name
})
return `/${renderedBaseDir}/${renderedPath}`
}

/**
* Check if the provided Path start withs our allowedBaseDirPath to see
*
Expand Down Expand Up @@ -239,7 +287,7 @@ const renderBaseDir = (baseDir, magicFolders = {}) => {
* @param {String} path Path to render : ex '/Administratif/$konnector/$account'
* @param {Object} variables Object mapping variable to actual values
* @param {import('../types').IOCozyKonnector['name']} variables.konnector - konnector name
* @param {String} variables.account - account name
* @param {String} [variables.account] - account name
* @returns {String} Rendered path
*/
const renderSubDir = (path, variables) => {
Expand Down
118 changes: 117 additions & 1 deletion packages/cozy-client/src/models/konnectorFolder.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('ensureKonnectorFolder', () => {
mockClient.query.mockResolvedValue({
included: []
})
mockClient.statByPath.mockRejectedValueOnce({ status: 404 })
mockClient.statByPath.mockRejectedValue({ status: 404 })
mockClient.statById.mockResolvedValueOnce({
data: { _id: 'parentfolderid' }
})
Expand All @@ -54,6 +54,12 @@ describe('ensureKonnectorFolder', () => {
dir_id: 'parentfolderid'
}
})
mockClient.createDirectoryByPath.mockResolvedValueOnce({
data: {
_id: 'createdfolderid',
dir_id: 'parentfolderid'
}
})
const result = await ensureKonnectorFolder(mockClient, {
konnector,
account,
Expand Down Expand Up @@ -220,6 +226,12 @@ describe('ensureKonnectorFolder', () => {
dir_id: 'parentfolderid'
}
})
mockClient.statByPath.mockResolvedValueOnce({
data: {
_id: 'alreadyexistingfolderid',
dir_id: 'parentfolderid'
}
})
const result = await ensureKonnectorFolder(mockClient, {
konnector: {
name: 'konnectorName',
Expand Down Expand Up @@ -251,6 +263,12 @@ describe('ensureKonnectorFolder', () => {
trashed: true
}
})
mockClient.statByPath.mockResolvedValueOnce({
data: {
_id: 'folderintrash',
trashed: true
}
})
mockClient.statById.mockResolvedValueOnce({
data: {
_id: 'parentfolderid'
Expand All @@ -262,6 +280,12 @@ describe('ensureKonnectorFolder', () => {
dir_id: 'parentfolderid'
}
})
mockClient.createDirectoryByPath.mockResolvedValueOnce({
data: {
_id: 'createdfolderid',
dir_id: 'parentfolderid'
}
})
const result = await ensureKonnectorFolder(mockClient, {
konnector,
account,
Expand All @@ -288,4 +312,96 @@ describe('ensureKonnectorFolder', () => {
{ _id: 'createdfolderid', dir_id: 'parentfolderid' }
])
})
it('should add proper references to konnector folder with proper name if any', async () => {
const existingKonnectorFolder = {
_id: 'konnectorFolderId',
path: '/Administratif/' + konnector.name,
type: 'directory',
referenced_by: []
}
const createdAccountFolder = {
_id: 'accountfolderid',
path: '/Administratif/' + konnector.name + '/testAccountName',
type: 'directory',
referenced_by: []
}
mockClient.query.mockResolvedValue({
included: []
})
mockClient.statByPath
.mockResolvedValueOnce({
data: existingKonnectorFolder
})
.mockResolvedValueOnce({ data: false })
mockClient.createDirectoryByPath.mockResolvedValueOnce({
data: createdAccountFolder
})

await ensureKonnectorFolder(mockClient, {
konnector,
account,
lang: 'fr'
})
expect(mockClient.addReferencesTo).toHaveBeenNthCalledWith(1, konnector, [
existingKonnectorFolder
])
expect(mockClient.addReferencesTo).toHaveBeenNthCalledWith(2, konnector, [
createdAccountFolder
])
expect(mockClient.addReferencesTo).toHaveBeenNthCalledWith(
3,
{
_id: 'testAccountName',
_type: 'io.cozy.accounts.sourceAccountIdentifier'
},
[createdAccountFolder]
)
expect(mockClient.addReferencesTo).toHaveBeenCalledTimes(3)
})

it('should add proper references to konnector folder even if account folder has konnector reference', async () => {
const existingKonnectorFolder = {
_id: 'konnectorFolderId',
path: '/Administratif/' + konnector.name,
type: 'directory',
referenced_by: []
}
const existingAccountFolder = {
_id: 'accountfolderid',
path: '/Administratif/' + konnector.name + '/testAccountName',
type: 'directory',
referenced_by: [
{
type: 'io.cozy.konnectors',
id: 'io.cozy.konnectors/konnectorSlug'
}
]
}
mockClient.query.mockResolvedValue({
included: []
})
mockClient.statByPath
.mockResolvedValueOnce({
data: existingKonnectorFolder
})
.mockResolvedValueOnce({ data: existingAccountFolder })
await ensureKonnectorFolder(mockClient, {
konnector,
account,
lang: 'fr'
})
expect(mockClient.addReferencesTo).toHaveBeenCalledTimes(2)
expect(mockClient.addReferencesTo).toHaveBeenNthCalledWith(1, konnector, [
existingKonnectorFolder
])
expect(mockClient.addReferencesTo).toHaveBeenNthCalledWith(
2,
{
_id: 'testAccountName',
_type: 'io.cozy.accounts.sourceAccountIdentifier'
},
[existingAccountFolder]
)
expect(mockClient.createDirectoryByPath).not.toHaveBeenCalled()
})
})

0 comments on commit 50e88f1

Please sign in to comment.