Skip to content

Commit

Permalink
Add check parent folder existence before restore entry (#185)
Browse files Browse the repository at this point in the history
* Add check parent folder existence before restore entry

* Add method to utils getFullParentFolderKeys, add unit tests

* Rm logs

* Fix name test

* Fix after review

* Fix after review

* Refactor

* fix
  • Loading branch information
Sergey-weber authored Oct 1, 2024
1 parent f60d53e commit ebc13ce
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 11 deletions.
60 changes: 55 additions & 5 deletions src/services/entry/actions/update-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import {WorkbookPermission} from '../../../entities/workbook';
import {Optional} from 'utility-types';
import {checkEntry} from './check-entry';
import {registry} from '../../../registry';
import {EntryScope} from '../../../db/models/new/entry/types';
import {EntryColumn} from '../../../db/models/new/entry';

type Mode = 'save' | 'publish' | 'recover';
const ModeValues: Mode[] = ['save', 'publish', 'recover'];
Expand Down Expand Up @@ -405,9 +407,61 @@ export async function updateEntry(ctx: CTX, updateData: UpdateEntryData) {
const entryObj: EntryColumns | undefined = entry ? entry.toJSON() : undefined;

if (entryObj) {
const entryTenantId = entryObj.tenantId;

const entryObjInnerMeta = entryObj.innerMeta as NonNullable<
EntryColumns['innerMeta']
>;
const newKey = entryObjInnerMeta.oldKey;

if (!entryObj.workbookId) {
const keyLowerCase = newKey.toLowerCase();
const isFolder = Utils.isFolder({scope: entryObj.scope});
const keyFormatted = Utils.formatKey(keyLowerCase, isFolder);

const parentFolderKeys = Utils.getFullParentFolderKeys(
keyFormatted,
).filter((key) => !Utils.isRoot(key));

if (parentFolderKeys.length) {
const parentFolders = await Entry.query(trx)
.where({
[EntryColumn.TenantId]: entryTenantId,
[EntryColumn.Scope]: EntryScope.Folder,
[EntryColumn.IsDeleted]: false,
})
.whereIn(EntryColumn.Key, parentFolderKeys)
.timeout(Entry.DEFAULT_QUERY_TIMEOUT);

const existingKeysParentFolders = new Set<string>();

const notFoundParentFolders: string[] = [];

parentFolders.forEach((folder) => {
existingKeysParentFolders.add(folder.key);
});

parentFolderKeys.forEach((key) => {
if (!existingKeysParentFolders.has(key)) {
notFoundParentFolders.push(key);
}
});

if (notFoundParentFolders.length) {
throw new AppError(
`Couldn't found these parent folders - '${notFoundParentFolders.join(
', ',
)}'`,
{
code: US_ERRORS.PARENT_FOLDER_NOT_EXIST,
},
);
}
}
}

if (entryObj.scope === 'folder') {
const entryObjKey = entryObj.key;
const entryTenantId = entryObj.tenantId;

const children = await Entry.query(trx)
.select()
Expand Down Expand Up @@ -445,10 +499,6 @@ export async function updateEntry(ctx: CTX, updateData: UpdateEntryData) {
}),
);
} else {
const entryObjInnerMeta = entryObj.innerMeta as NonNullable<
EntryColumns['innerMeta']
>;
const newKey = entryObjInnerMeta.oldKey;
const newDisplayKey = entryObjInnerMeta.oldDisplayKey;
const newInnerMeta: Optional<typeof entryObjInnerMeta> =
entryObjInnerMeta;
Expand Down
56 changes: 50 additions & 6 deletions src/tests/unit/utils/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import Utils from '../../../utils';

describe('Utils', () => {
describe('Utils.getCopyNumber', () => {
test('Shoud return 0 if name does not have prefix COPY', () => {
test('Should return 0 if name does not have prefix COPY', () => {
const name = 'DASH';
const copyNameNumber = Utils.getCopyNumber(name);

expect(copyNameNumber).toBe(0);
});

test('Shoud return number if name has prefix COPY', () => {
test('Should return number if name has prefix COPY', () => {
const name = 'DASH (COPY 7)';
const copyNameNumber = Utils.getCopyNumber(name);

Expand All @@ -18,14 +18,14 @@ describe('Utils', () => {
});

describe('Utils.setCopyNumber', () => {
test('Shoud return name without prefix COPY, if count === 0', () => {
test('Should return name without prefix COPY, if count === 0', () => {
const name = 'DASH';
const replacedCopyNumber = Utils.setCopyNumber(name, 0);

expect(replacedCopyNumber).toBe('DASH');
});

test('Shoud increment counter if name has prefix COPY', () => {
test('Should increment counter if name has prefix COPY', () => {
const name = 'DASH';
const replacedCopyNumber = Utils.setCopyNumber(name, 1);

Expand All @@ -34,16 +34,60 @@ describe('Utils', () => {
});

describe('Utils.getNameWithoutCopyNumber', () => {
test('Shoud return name without prefix, if name has prefix', () => {
test('Should return name without prefix, if name has prefix', () => {
const nameWithoutCopy = Utils.getNameWithoutCopyNumber('DASH (COPY 7)');

expect(nameWithoutCopy).toBe('DASH');
});

test('Shoud return name without prefix, if name has not prefix', () => {
test('Should return name without prefix, if name has not prefix', () => {
const nameWithoutCopy = Utils.getNameWithoutCopyNumber('DASH');

expect(nameWithoutCopy).toBe('DASH');
});
});

describe('Utils.getFullParentFolderKeys', () => {
test('Should return all full parent folder keys', () => {
const parentFolderKeys = Utils.getFullParentFolderKeys(
'foldername/nestedfolder/nestedfolder2/nestedfolder3',
);

expect(parentFolderKeys).toEqual([
'foldername/nestedfolder/nestedfolder2/',
'foldername/nestedfolder/',
'foldername/',
]);
});

test('Should return parent folder, if the entity does not have / at the end ', () => {
const parentFolderKeys = Utils.getFullParentFolderKeys('entries-basic-tests/dataset');

expect(parentFolderKeys).toEqual(['entries-basic-tests/']);
});

test('Should return root folder, if no parent folder', () => {
const parentFolderKeys = Utils.getFullParentFolderKeys('foldername');

expect(parentFolderKeys).toEqual(['/']);
});

test('Should return root folder, when only / in input', () => {
const parentFolderKeys = Utils.getFullParentFolderKeys('/');

expect(parentFolderKeys).toEqual(['/']);
});

test('Should return root folder, when only 1 entity in string', () => {
const parentFolderKeys = Utils.getFullParentFolderKeys('foldername/');

expect(parentFolderKeys).toEqual(['/']);
});

test('Should return empty array, when input empty string', () => {
const parentFolderKeys = Utils.getFullParentFolderKeys('');

expect(parentFolderKeys).toEqual([]);
});
});
});
26 changes: 26 additions & 0 deletions src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,32 @@ export class Utils {
return parentFolderKey ? parentFolderKey : '/';
}

static getFullParentFolderKeys(keyFormatted = '') {
let parentFolderKey = keyFormatted;

const parentFolderKeys = [];

while (parentFolderKey.length) {
const newParentFolderKey = Utils.getParentFolderKey({
keyFormatted: parentFolderKey,
});

if (newParentFolderKey) {
parentFolderKey = newParentFolderKey;

parentFolderKeys.push(newParentFolderKey);

if (newParentFolderKey.split('/').length <= 2) {
break;
}
} else {
break;
}
}

return parentFolderKeys;
}

static isRoot(key: string | undefined) {
return key === '/';
}
Expand Down

0 comments on commit ebc13ce

Please sign in to comment.