2023-02-25 15:12:03 +01:00
|
|
|
import {
|
|
|
|
assetEntityStub,
|
|
|
|
newAssetRepositoryMock,
|
|
|
|
newStorageRepositoryMock,
|
|
|
|
newSystemConfigRepositoryMock,
|
2023-05-22 05:18:10 +02:00
|
|
|
newUserRepositoryMock,
|
|
|
|
userEntityStub,
|
2023-06-08 17:01:07 +02:00
|
|
|
} from '@test';
|
2023-06-16 21:54:17 +02:00
|
|
|
import { when } from 'jest-when';
|
2023-06-08 17:01:07 +02:00
|
|
|
import { StorageTemplateService } from '.';
|
2023-06-16 21:54:17 +02:00
|
|
|
import { IAssetRepository } from '../asset';
|
2023-02-25 15:12:03 +01:00
|
|
|
import { IStorageRepository } from '../storage/storage.repository';
|
|
|
|
import { ISystemConfigRepository } from '../system-config';
|
2023-07-15 06:03:56 +02:00
|
|
|
import { defaults } from '../system-config/system-config.core';
|
2023-05-22 05:18:10 +02:00
|
|
|
import { IUserRepository } from '../user';
|
2023-02-25 15:12:03 +01:00
|
|
|
|
|
|
|
describe(StorageTemplateService.name, () => {
|
|
|
|
let sut: StorageTemplateService;
|
|
|
|
let assetMock: jest.Mocked<IAssetRepository>;
|
|
|
|
let configMock: jest.Mocked<ISystemConfigRepository>;
|
|
|
|
let storageMock: jest.Mocked<IStorageRepository>;
|
2023-05-22 05:18:10 +02:00
|
|
|
let userMock: jest.Mocked<IUserRepository>;
|
2023-02-25 15:12:03 +01:00
|
|
|
|
|
|
|
it('should work', () => {
|
|
|
|
expect(sut).toBeDefined();
|
|
|
|
});
|
|
|
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
assetMock = newAssetRepositoryMock();
|
|
|
|
configMock = newSystemConfigRepositoryMock();
|
|
|
|
storageMock = newStorageRepositoryMock();
|
2023-05-22 05:18:10 +02:00
|
|
|
userMock = newUserRepositoryMock();
|
|
|
|
|
2023-07-15 06:03:56 +02:00
|
|
|
sut = new StorageTemplateService(assetMock, configMock, defaults, storageMock, userMock);
|
2023-02-25 15:12:03 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('handle template migration', () => {
|
|
|
|
it('should handle no assets', async () => {
|
2023-05-22 20:05:06 +02:00
|
|
|
assetMock.getAll.mockResolvedValue({
|
|
|
|
items: [],
|
|
|
|
hasNextPage: false,
|
|
|
|
});
|
2023-05-22 05:18:10 +02:00
|
|
|
userMock.getList.mockResolvedValue([]);
|
2023-02-25 15:12:03 +01:00
|
|
|
|
2023-05-26 14:52:52 +02:00
|
|
|
await sut.handleMigration();
|
2023-02-25 15:12:03 +01:00
|
|
|
|
|
|
|
expect(assetMock.getAll).toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should handle an asset with a duplicate destination', async () => {
|
2023-05-22 20:05:06 +02:00
|
|
|
assetMock.getAll.mockResolvedValue({
|
|
|
|
items: [assetEntityStub.image],
|
|
|
|
hasNextPage: false,
|
|
|
|
});
|
2023-02-25 15:12:03 +01:00
|
|
|
assetMock.save.mockResolvedValue(assetEntityStub.image);
|
2023-05-22 05:18:10 +02:00
|
|
|
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
2023-02-25 15:12:03 +01:00
|
|
|
|
|
|
|
when(storageMock.checkFileExists)
|
2023-07-10 19:56:45 +02:00
|
|
|
.calledWith('upload/library/user-id/2023/2023-02-23/asset-id.jpg')
|
2023-02-25 15:12:03 +01:00
|
|
|
.mockResolvedValue(true);
|
|
|
|
|
|
|
|
when(storageMock.checkFileExists)
|
2023-07-10 19:56:45 +02:00
|
|
|
.calledWith('upload/library/user-id/2023/2023-02-23/asset-id+1.jpg')
|
2023-02-25 15:12:03 +01:00
|
|
|
.mockResolvedValue(false);
|
|
|
|
|
2023-05-26 14:52:52 +02:00
|
|
|
await sut.handleMigration();
|
2023-02-25 15:12:03 +01:00
|
|
|
|
|
|
|
expect(assetMock.getAll).toHaveBeenCalled();
|
|
|
|
expect(storageMock.checkFileExists).toHaveBeenCalledTimes(2);
|
|
|
|
expect(assetMock.save).toHaveBeenCalledWith({
|
|
|
|
id: assetEntityStub.image.id,
|
2023-07-10 19:56:45 +02:00
|
|
|
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.jpg',
|
2023-02-25 15:12:03 +01:00
|
|
|
});
|
2023-05-22 05:18:10 +02:00
|
|
|
expect(userMock.getList).toHaveBeenCalled();
|
2023-02-25 15:12:03 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
it('should skip when an asset already matches the template', async () => {
|
2023-05-22 20:05:06 +02:00
|
|
|
assetMock.getAll.mockResolvedValue({
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
...assetEntityStub.image,
|
2023-07-10 19:56:45 +02:00
|
|
|
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
|
2023-05-22 20:05:06 +02:00
|
|
|
},
|
|
|
|
],
|
|
|
|
hasNextPage: false,
|
|
|
|
});
|
2023-05-22 05:18:10 +02:00
|
|
|
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
2023-02-25 15:12:03 +01:00
|
|
|
|
2023-05-26 14:52:52 +02:00
|
|
|
await sut.handleMigration();
|
2023-02-25 15:12:03 +01:00
|
|
|
|
|
|
|
expect(assetMock.getAll).toHaveBeenCalled();
|
|
|
|
expect(storageMock.moveFile).not.toHaveBeenCalled();
|
|
|
|
expect(storageMock.checkFileExists).not.toHaveBeenCalledTimes(2);
|
|
|
|
expect(assetMock.save).not.toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should skip when an asset is probably a duplicate', async () => {
|
2023-05-22 20:05:06 +02:00
|
|
|
assetMock.getAll.mockResolvedValue({
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
...assetEntityStub.image,
|
2023-07-10 19:56:45 +02:00
|
|
|
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.jpg',
|
2023-05-22 20:05:06 +02:00
|
|
|
},
|
|
|
|
],
|
|
|
|
hasNextPage: false,
|
|
|
|
});
|
2023-05-22 05:18:10 +02:00
|
|
|
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
2023-02-25 15:12:03 +01:00
|
|
|
|
2023-05-26 14:52:52 +02:00
|
|
|
await sut.handleMigration();
|
2023-02-25 15:12:03 +01:00
|
|
|
|
|
|
|
expect(assetMock.getAll).toHaveBeenCalled();
|
|
|
|
expect(storageMock.moveFile).not.toHaveBeenCalled();
|
|
|
|
expect(storageMock.checkFileExists).not.toHaveBeenCalledTimes(2);
|
|
|
|
expect(assetMock.save).not.toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should move an asset', async () => {
|
2023-05-22 20:05:06 +02:00
|
|
|
assetMock.getAll.mockResolvedValue({
|
|
|
|
items: [assetEntityStub.image],
|
|
|
|
hasNextPage: false,
|
|
|
|
});
|
2023-02-25 15:12:03 +01:00
|
|
|
assetMock.save.mockResolvedValue(assetEntityStub.image);
|
2023-05-22 05:18:10 +02:00
|
|
|
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
2023-02-25 15:12:03 +01:00
|
|
|
|
2023-05-26 14:52:52 +02:00
|
|
|
await sut.handleMigration();
|
2023-02-25 15:12:03 +01:00
|
|
|
|
|
|
|
expect(assetMock.getAll).toHaveBeenCalled();
|
|
|
|
expect(storageMock.moveFile).toHaveBeenCalledWith(
|
2023-07-10 19:56:45 +02:00
|
|
|
'/original/path.jpg',
|
|
|
|
'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
|
2023-02-25 15:12:03 +01:00
|
|
|
);
|
|
|
|
expect(assetMock.save).toHaveBeenCalledWith({
|
|
|
|
id: assetEntityStub.image.id,
|
2023-07-10 19:56:45 +02:00
|
|
|
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
|
2023-02-25 15:12:03 +01:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2023-05-22 05:18:10 +02:00
|
|
|
it('should use the user storage label', async () => {
|
2023-05-22 20:05:06 +02:00
|
|
|
assetMock.getAll.mockResolvedValue({
|
|
|
|
items: [assetEntityStub.image],
|
|
|
|
hasNextPage: false,
|
|
|
|
});
|
2023-05-22 05:18:10 +02:00
|
|
|
assetMock.save.mockResolvedValue(assetEntityStub.image);
|
|
|
|
userMock.getList.mockResolvedValue([userEntityStub.storageLabel]);
|
|
|
|
|
2023-05-26 14:52:52 +02:00
|
|
|
await sut.handleMigration();
|
2023-05-22 05:18:10 +02:00
|
|
|
|
|
|
|
expect(assetMock.getAll).toHaveBeenCalled();
|
|
|
|
expect(storageMock.moveFile).toHaveBeenCalledWith(
|
2023-07-10 19:56:45 +02:00
|
|
|
'/original/path.jpg',
|
|
|
|
'upload/library/label-1/2023/2023-02-23/asset-id.jpg',
|
2023-05-22 05:18:10 +02:00
|
|
|
);
|
|
|
|
expect(assetMock.save).toHaveBeenCalledWith({
|
|
|
|
id: assetEntityStub.image.id,
|
2023-07-10 19:56:45 +02:00
|
|
|
originalPath: 'upload/library/label-1/2023/2023-02-23/asset-id.jpg',
|
2023-05-22 05:18:10 +02:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2023-02-25 15:12:03 +01:00
|
|
|
it('should not update the database if the move fails', async () => {
|
2023-05-22 20:05:06 +02:00
|
|
|
assetMock.getAll.mockResolvedValue({
|
|
|
|
items: [assetEntityStub.image],
|
|
|
|
hasNextPage: false,
|
|
|
|
});
|
2023-02-25 15:12:03 +01:00
|
|
|
storageMock.moveFile.mockRejectedValue(new Error('Read only system'));
|
2023-05-22 05:18:10 +02:00
|
|
|
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
2023-02-25 15:12:03 +01:00
|
|
|
|
2023-05-26 14:52:52 +02:00
|
|
|
await sut.handleMigration();
|
2023-02-25 15:12:03 +01:00
|
|
|
|
|
|
|
expect(assetMock.getAll).toHaveBeenCalled();
|
|
|
|
expect(storageMock.moveFile).toHaveBeenCalledWith(
|
2023-07-10 19:56:45 +02:00
|
|
|
'/original/path.jpg',
|
|
|
|
'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
|
2023-02-25 15:12:03 +01:00
|
|
|
);
|
|
|
|
expect(assetMock.save).not.toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should move the asset back if the database fails', async () => {
|
2023-05-22 20:05:06 +02:00
|
|
|
assetMock.getAll.mockResolvedValue({
|
|
|
|
items: [assetEntityStub.image],
|
|
|
|
hasNextPage: false,
|
|
|
|
});
|
2023-02-25 15:12:03 +01:00
|
|
|
assetMock.save.mockRejectedValue('Connection Error!');
|
2023-05-22 05:18:10 +02:00
|
|
|
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
2023-02-25 15:12:03 +01:00
|
|
|
|
2023-05-26 14:52:52 +02:00
|
|
|
await sut.handleMigration();
|
2023-02-25 15:12:03 +01:00
|
|
|
|
|
|
|
expect(assetMock.getAll).toHaveBeenCalled();
|
|
|
|
expect(assetMock.save).toHaveBeenCalledWith({
|
|
|
|
id: assetEntityStub.image.id,
|
2023-07-10 19:56:45 +02:00
|
|
|
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
|
2023-02-25 15:12:03 +01:00
|
|
|
});
|
|
|
|
expect(storageMock.moveFile.mock.calls).toEqual([
|
2023-07-10 19:56:45 +02:00
|
|
|
['/original/path.jpg', 'upload/library/user-id/2023/2023-02-23/asset-id.jpg'],
|
|
|
|
['upload/library/user-id/2023/2023-02-23/asset-id.jpg', '/original/path.jpg'],
|
2023-02-25 15:12:03 +01:00
|
|
|
]);
|
|
|
|
});
|
feat(server): support for read-only assets and importing existing items in the filesystem (#2715)
* Added read-only flag for assets, endpoint to trigger file import vs upload
* updated fixtures with new property
* if upload is 'read-only', ensure there is no existing asset at the designated originalPath
* added test for file import as well as detecting existing image at read-only destination location
* Added storage service test for a case where it should not move read-only assets
* upload doesn't need the read-only flag available, just importing
* default isReadOnly on import endpoint to true
* formatting fixes
* create-asset dto needs isReadOnly, so set it to false by default on create, updated api generation
* updated code to reflect changes in MR
* fixed read stream promise return type
* new index for originalPath, check for existing path on import, reglardless of user, to prevent duplicates
* refactor: import asset
* chore: open api
* chore: tests
* Added externalPath support for individual users, updated UI to allow this to be set by admin
* added missing var for externalPath in ui
* chore: open api
* fix: compilation issues
* fix: server test
* built api, fixed user-response dto to include externalPath
* reverted accidental commit
* bad commit of duplicate externalPath in user response dto
* fixed tests to include externalPath on expected result
* fix: unit tests
* centralized supported filetypes, perform file type checking of asset and sidecar during file import process
* centralized supported filetype check method to keep regex DRY
* fixed typo
* combined migrations into one
* update api
* Removed externalPath from shared-link code, added column to admin user page whether external paths / import is enabled or not
* update mimetype
* Fixed detect correct mimetype
* revert asset-upload config
* reverted domain.constant
* refactor
* fix mime-type issue
* fix format
---------
Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-06-22 04:33:20 +02:00
|
|
|
|
|
|
|
it('should not move read-only asset', async () => {
|
|
|
|
assetMock.getAll.mockResolvedValue({
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
...assetEntityStub.image,
|
2023-07-10 19:56:45 +02:00
|
|
|
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.jpg',
|
feat(server): support for read-only assets and importing existing items in the filesystem (#2715)
* Added read-only flag for assets, endpoint to trigger file import vs upload
* updated fixtures with new property
* if upload is 'read-only', ensure there is no existing asset at the designated originalPath
* added test for file import as well as detecting existing image at read-only destination location
* Added storage service test for a case where it should not move read-only assets
* upload doesn't need the read-only flag available, just importing
* default isReadOnly on import endpoint to true
* formatting fixes
* create-asset dto needs isReadOnly, so set it to false by default on create, updated api generation
* updated code to reflect changes in MR
* fixed read stream promise return type
* new index for originalPath, check for existing path on import, reglardless of user, to prevent duplicates
* refactor: import asset
* chore: open api
* chore: tests
* Added externalPath support for individual users, updated UI to allow this to be set by admin
* added missing var for externalPath in ui
* chore: open api
* fix: compilation issues
* fix: server test
* built api, fixed user-response dto to include externalPath
* reverted accidental commit
* bad commit of duplicate externalPath in user response dto
* fixed tests to include externalPath on expected result
* fix: unit tests
* centralized supported filetypes, perform file type checking of asset and sidecar during file import process
* centralized supported filetype check method to keep regex DRY
* fixed typo
* combined migrations into one
* update api
* Removed externalPath from shared-link code, added column to admin user page whether external paths / import is enabled or not
* update mimetype
* Fixed detect correct mimetype
* revert asset-upload config
* reverted domain.constant
* refactor
* fix mime-type issue
* fix format
---------
Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-06-22 04:33:20 +02:00
|
|
|
isReadOnly: true,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
hasNextPage: false,
|
|
|
|
});
|
|
|
|
assetMock.save.mockResolvedValue(assetEntityStub.image);
|
|
|
|
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
|
|
|
|
|
|
|
await sut.handleMigration();
|
|
|
|
|
|
|
|
expect(assetMock.getAll).toHaveBeenCalled();
|
|
|
|
expect(storageMock.moveFile).not.toHaveBeenCalled();
|
|
|
|
expect(assetMock.save).not.toHaveBeenCalled();
|
|
|
|
});
|
2023-02-25 15:12:03 +01:00
|
|
|
});
|
|
|
|
});
|