feat(mobile) duplicated asset upload handling mechanism (#853)

This commit is contained in:
Alex
2022-10-25 09:51:03 -05:00
committed by GitHub
parent f1af17bf4d
commit 6159c83fd2
32 changed files with 836 additions and 38 deletions

View File

@@ -137,6 +137,7 @@ describe('Album service', () => {
getAssetWithNoEXIF: jest.fn(),
getAssetWithNoThumbnail: jest.fn(),
getAssetWithNoSmartInfo: jest.fn(),
getExistingAssets: jest.fn(),
};
sut = new AlbumService(albumRepositoryMock, assetRepositoryMock);

View File

@@ -10,6 +10,9 @@ import { AssetCountByTimeBucket } from './response-dto/asset-count-by-time-group
import { TimeGroupEnum } from './dto/get-asset-count-by-time-bucket.dto';
import { GetAssetByTimeBucketDto } from './dto/get-asset-by-time-bucket.dto';
import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-user-id-response.dto';
import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto';
import { CheckExistingAssetsResponseDto } from './response-dto/check-existing-assets-response.dto';
import { In } from 'typeorm/find-options/operator/In';
export interface IAssetRepository {
create(
@@ -32,6 +35,7 @@ export interface IAssetRepository {
getAssetWithNoThumbnail(): Promise<AssetEntity[]>;
getAssetWithNoEXIF(): Promise<AssetEntity[]>;
getAssetWithNoSmartInfo(): Promise<AssetEntity[]>;
getExistingAssets(userId: string, checkDuplicateAssetDto: CheckExistingAssetsDto): Promise<CheckExistingAssetsResponseDto>;
}
export const ASSET_REPOSITORY = 'ASSET_REPOSITORY';
@@ -279,4 +283,17 @@ export class AssetRepository implements IAssetRepository {
relations: ['exifInfo'],
});
}
async getExistingAssets(userId: string, checkDuplicateAssetDto: CheckExistingAssetsDto): Promise<CheckExistingAssetsResponseDto> {
const existingAssets = await this.assetRepository.find({
select: {deviceAssetId: true},
where: {
deviceAssetId: In(checkDuplicateAssetDto.deviceAssetIds),
deviceId: checkDuplicateAssetDto.deviceId,
userId,
},
});
return new CheckExistingAssetsResponseDto(existingAssets.map(a => a.deviceAssetId));
}
}

View File

@@ -48,6 +48,8 @@ import { GetAssetCountByTimeBucketDto } from './dto/get-asset-count-by-time-buck
import { GetAssetByTimeBucketDto } from './dto/get-asset-by-time-bucket.dto';
import { QueryFailedError } from 'typeorm';
import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-user-id-response.dto';
import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto';
import { CheckExistingAssetsResponseDto } from './response-dto/check-existing-assets-response.dto';
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@@ -74,6 +76,7 @@ export class AssetController {
@GetAuthUser() authUser: AuthUserDto,
@UploadedFile() file: Express.Multer.File,
@Body(ValidationPipe) assetInfo: CreateAssetDto,
@Response({ passthrough: true }) res: Res,
): Promise<AssetFileUploadResponseDto> {
const checksum = await this.assetService.calculateChecksum(file.path);
@@ -111,6 +114,7 @@ export class AssetController {
if (err instanceof QueryFailedError && (err as any).constraint === 'UQ_userid_checksum') {
const existedAsset = await this.assetService.getAssetByChecksum(authUser.id, checksum);
res.status(200); // normal POST is 201. we use 200 to indicate the asset already exists
return new AssetFileUploadResponseDto(existedAsset.id);
}
@@ -254,4 +258,16 @@ export class AssetController {
): Promise<CheckDuplicateAssetResponseDto> {
return await this.assetService.checkDuplicatedAsset(authUser, checkDuplicateAssetDto);
}
/**
* Checks if multiple assets exist on the server and returns all existing - used by background backup
*/
@Post('/exist')
@HttpCode(200)
async checkExistingAssets(
@GetAuthUser() authUser: AuthUserDto,
@Body(ValidationPipe) checkExistingAssetsDto: CheckExistingAssetsDto,
): Promise<CheckExistingAssetsResponseDto> {
return await this.assetService.checkExistingAssets(authUser, checkExistingAssetsDto);
}
}

View File

@@ -110,6 +110,7 @@ describe('AssetService', () => {
getAssetWithNoEXIF: jest.fn(),
getAssetWithNoThumbnail: jest.fn(),
getAssetWithNoSmartInfo: jest.fn(),
getExistingAssets: jest.fn(),
};
sui = new AssetService(assetRepositoryMock, a);

View File

@@ -37,6 +37,8 @@ import { GetAssetCountByTimeBucketDto } from './dto/get-asset-count-by-time-buck
import { GetAssetByTimeBucketDto } from './dto/get-asset-by-time-bucket.dto';
import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-user-id-response.dto';
import { timeUtils } from '@app/common/utils';
import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto';
import { CheckExistingAssetsResponseDto } from './response-dto/check-existing-assets-response.dto';
const fileInfo = promisify(stat);
@@ -466,6 +468,13 @@ export class AssetService {
return new CheckDuplicateAssetResponseDto(isDuplicated, res?.id);
}
async checkExistingAssets(
authUser: AuthUserDto,
checkExistingAssetsDto: CheckExistingAssetsDto,
): Promise<CheckExistingAssetsResponseDto> {
return this._assetRepository.getExistingAssets(authUser.id, checkExistingAssetsDto);
}
async getAssetCountByTimeBucket(
authUser: AuthUserDto,
getAssetCountByTimeBucketDto: GetAssetCountByTimeBucketDto,

View File

@@ -0,0 +1,9 @@
import { IsNotEmpty } from 'class-validator';
export class CheckExistingAssetsDto {
@IsNotEmpty()
deviceAssetIds!: string[];
@IsNotEmpty()
deviceId!: string;
}

View File

@@ -0,0 +1,7 @@
export class CheckExistingAssetsResponseDto {
constructor(existingIds: string[]) {
this.existingIds = existingIds;
}
existingIds: string[];
}

File diff suppressed because one or more lines are too long