mirror of
https://github.com/immich-app/immich.git
synced 2024-11-15 18:08:48 -07:00
fix(server): proper asset sync (#10019)
* fix(server,mobile): proper asset sync * fix CI issues * only use id instead of createdAt+id * remove createdAt index * fix typo * cleanup createdAt usage --------- Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
parent
69795a3763
commit
972c66d467
@ -101,7 +101,6 @@ class AssetService {
|
|||||||
const int chunkSize = 10000;
|
const int chunkSize = 10000;
|
||||||
try {
|
try {
|
||||||
final List<Asset> allAssets = [];
|
final List<Asset> allAssets = [];
|
||||||
DateTime? lastCreationDate;
|
|
||||||
String? lastId;
|
String? lastId;
|
||||||
// will break on error or once all assets are loaded
|
// will break on error or once all assets are loaded
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -109,15 +108,17 @@ class AssetService {
|
|||||||
limit: chunkSize,
|
limit: chunkSize,
|
||||||
updatedUntil: until,
|
updatedUntil: until,
|
||||||
lastId: lastId,
|
lastId: lastId,
|
||||||
lastCreationDate: lastCreationDate,
|
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
);
|
);
|
||||||
|
log.fine("Requesting $chunkSize assets from $lastId");
|
||||||
final List<AssetResponseDto>? assets =
|
final List<AssetResponseDto>? assets =
|
||||||
await _apiService.syncApi.getFullSyncForUser(dto);
|
await _apiService.syncApi.getFullSyncForUser(dto);
|
||||||
if (assets == null) return null;
|
if (assets == null) return null;
|
||||||
|
log.fine(
|
||||||
|
"Received ${assets.length} assets from ${assets.firstOrNull?.id} to ${assets.lastOrNull?.id}",
|
||||||
|
);
|
||||||
allAssets.addAll(assets.map(Asset.remote));
|
allAssets.addAll(assets.map(Asset.remote));
|
||||||
if (assets.isEmpty) break;
|
if (assets.length != chunkSize) break;
|
||||||
lastCreationDate = assets.last.fileCreatedAt;
|
|
||||||
lastId = assets.last.id;
|
lastId = assets.last.id;
|
||||||
}
|
}
|
||||||
return allAssets;
|
return allAssets;
|
||||||
|
19
mobile/openapi/lib/model/asset_full_sync_dto.dart
generated
19
mobile/openapi/lib/model/asset_full_sync_dto.dart
generated
@ -13,21 +13,12 @@ part of openapi.api;
|
|||||||
class AssetFullSyncDto {
|
class AssetFullSyncDto {
|
||||||
/// Returns a new [AssetFullSyncDto] instance.
|
/// Returns a new [AssetFullSyncDto] instance.
|
||||||
AssetFullSyncDto({
|
AssetFullSyncDto({
|
||||||
this.lastCreationDate,
|
|
||||||
this.lastId,
|
this.lastId,
|
||||||
required this.limit,
|
required this.limit,
|
||||||
required this.updatedUntil,
|
required this.updatedUntil,
|
||||||
this.userId,
|
this.userId,
|
||||||
});
|
});
|
||||||
|
|
||||||
///
|
|
||||||
/// Please note: This property should have been non-nullable! Since the specification file
|
|
||||||
/// does not include a default value (using the "default:" property), however, the generated
|
|
||||||
/// source code must fall back to having a nullable type.
|
|
||||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
|
||||||
///
|
|
||||||
DateTime? lastCreationDate;
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Please note: This property should have been non-nullable! Since the specification file
|
/// Please note: This property should have been non-nullable! Since the specification file
|
||||||
/// does not include a default value (using the "default:" property), however, the generated
|
/// does not include a default value (using the "default:" property), however, the generated
|
||||||
@ -51,7 +42,6 @@ class AssetFullSyncDto {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) => identical(this, other) || other is AssetFullSyncDto &&
|
bool operator ==(Object other) => identical(this, other) || other is AssetFullSyncDto &&
|
||||||
other.lastCreationDate == lastCreationDate &&
|
|
||||||
other.lastId == lastId &&
|
other.lastId == lastId &&
|
||||||
other.limit == limit &&
|
other.limit == limit &&
|
||||||
other.updatedUntil == updatedUntil &&
|
other.updatedUntil == updatedUntil &&
|
||||||
@ -60,22 +50,16 @@ class AssetFullSyncDto {
|
|||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
// ignore: unnecessary_parenthesis
|
// ignore: unnecessary_parenthesis
|
||||||
(lastCreationDate == null ? 0 : lastCreationDate!.hashCode) +
|
|
||||||
(lastId == null ? 0 : lastId!.hashCode) +
|
(lastId == null ? 0 : lastId!.hashCode) +
|
||||||
(limit.hashCode) +
|
(limit.hashCode) +
|
||||||
(updatedUntil.hashCode) +
|
(updatedUntil.hashCode) +
|
||||||
(userId == null ? 0 : userId!.hashCode);
|
(userId == null ? 0 : userId!.hashCode);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'AssetFullSyncDto[lastCreationDate=$lastCreationDate, lastId=$lastId, limit=$limit, updatedUntil=$updatedUntil, userId=$userId]';
|
String toString() => 'AssetFullSyncDto[lastId=$lastId, limit=$limit, updatedUntil=$updatedUntil, userId=$userId]';
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final json = <String, dynamic>{};
|
final json = <String, dynamic>{};
|
||||||
if (this.lastCreationDate != null) {
|
|
||||||
json[r'lastCreationDate'] = this.lastCreationDate!.toUtc().toIso8601String();
|
|
||||||
} else {
|
|
||||||
// json[r'lastCreationDate'] = null;
|
|
||||||
}
|
|
||||||
if (this.lastId != null) {
|
if (this.lastId != null) {
|
||||||
json[r'lastId'] = this.lastId;
|
json[r'lastId'] = this.lastId;
|
||||||
} else {
|
} else {
|
||||||
@ -99,7 +83,6 @@ class AssetFullSyncDto {
|
|||||||
final json = value.cast<String, dynamic>();
|
final json = value.cast<String, dynamic>();
|
||||||
|
|
||||||
return AssetFullSyncDto(
|
return AssetFullSyncDto(
|
||||||
lastCreationDate: mapDateTime(json, r'lastCreationDate', r''),
|
|
||||||
lastId: mapValueOfType<String>(json, r'lastId'),
|
lastId: mapValueOfType<String>(json, r'lastId'),
|
||||||
limit: mapValueOfType<int>(json, r'limit')!,
|
limit: mapValueOfType<int>(json, r'limit')!,
|
||||||
updatedUntil: mapDateTime(json, r'updatedUntil', r'')!,
|
updatedUntil: mapDateTime(json, r'updatedUntil', r'')!,
|
||||||
|
@ -7432,10 +7432,6 @@
|
|||||||
},
|
},
|
||||||
"AssetFullSyncDto": {
|
"AssetFullSyncDto": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"lastCreationDate": {
|
|
||||||
"format": "date-time",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"lastId": {
|
"lastId": {
|
||||||
"format": "uuid",
|
"format": "uuid",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
@ -904,7 +904,6 @@ export type AssetDeltaSyncResponseDto = {
|
|||||||
upserted: AssetResponseDto[];
|
upserted: AssetResponseDto[];
|
||||||
};
|
};
|
||||||
export type AssetFullSyncDto = {
|
export type AssetFullSyncDto = {
|
||||||
lastCreationDate?: string;
|
|
||||||
lastId?: string;
|
lastId?: string;
|
||||||
limit: number;
|
limit: number;
|
||||||
updatedUntil: string;
|
updatedUntil: string;
|
||||||
|
@ -127,10 +127,10 @@ export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): As
|
|||||||
stackParentId: withStack ? entity.stack?.primaryAssetId : undefined,
|
stackParentId: withStack ? entity.stack?.primaryAssetId : undefined,
|
||||||
stack: withStack
|
stack: withStack
|
||||||
? entity.stack?.assets
|
? entity.stack?.assets
|
||||||
.filter((a) => a.id !== entity.stack?.primaryAssetId)
|
?.filter((a) => a.id !== entity.stack?.primaryAssetId)
|
||||||
.map((a) => mapAsset(a, { stripMetadata, auth: options.auth }))
|
?.map((a) => mapAsset(a, { stripMetadata, auth: options.auth }))
|
||||||
: undefined,
|
: undefined,
|
||||||
stackCount: entity.stack?.assets?.length ?? null,
|
stackCount: entity.stack?.assetCount ?? entity.stack?.assets?.length ?? null,
|
||||||
isOffline: entity.isOffline,
|
isOffline: entity.isOffline,
|
||||||
hasMetadata: true,
|
hasMetadata: true,
|
||||||
duplicateId: entity.duplicateId,
|
duplicateId: entity.duplicateId,
|
||||||
|
@ -7,9 +7,6 @@ export class AssetFullSyncDto {
|
|||||||
@ValidateUUID({ optional: true })
|
@ValidateUUID({ optional: true })
|
||||||
lastId?: string;
|
lastId?: string;
|
||||||
|
|
||||||
@ValidateDate({ optional: true })
|
|
||||||
lastCreationDate?: Date;
|
|
||||||
|
|
||||||
@ValidateDate()
|
@ValidateDate()
|
||||||
updatedUntil!: Date;
|
updatedUntil!: Date;
|
||||||
|
|
||||||
|
@ -16,4 +16,6 @@ export class AssetStackEntity {
|
|||||||
|
|
||||||
@Column({ nullable: false })
|
@Column({ nullable: false })
|
||||||
primaryAssetId!: string;
|
primaryAssetId!: string;
|
||||||
|
|
||||||
|
assetCount?: number;
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,6 @@ export interface AssetExploreOptions extends AssetExploreFieldOptions {
|
|||||||
|
|
||||||
export interface AssetFullSyncOptions {
|
export interface AssetFullSyncOptions {
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
lastCreationDate?: Date;
|
|
||||||
lastId?: string;
|
lastId?: string;
|
||||||
updatedUntil: Date;
|
updatedUntil: Date;
|
||||||
limit: number;
|
limit: number;
|
||||||
|
@ -1049,50 +1049,18 @@ SELECT
|
|||||||
"exifInfo"."bitsPerSample" AS "exifInfo_bitsPerSample",
|
"exifInfo"."bitsPerSample" AS "exifInfo_bitsPerSample",
|
||||||
"exifInfo"."fps" AS "exifInfo_fps",
|
"exifInfo"."fps" AS "exifInfo_fps",
|
||||||
"stack"."id" AS "stack_id",
|
"stack"."id" AS "stack_id",
|
||||||
"stack"."primaryAssetId" AS "stack_primaryAssetId",
|
"stack"."primaryAssetId" AS "stack_primaryAssetId"
|
||||||
"stackedAssets"."id" AS "stackedAssets_id",
|
|
||||||
"stackedAssets"."deviceAssetId" AS "stackedAssets_deviceAssetId",
|
|
||||||
"stackedAssets"."ownerId" AS "stackedAssets_ownerId",
|
|
||||||
"stackedAssets"."libraryId" AS "stackedAssets_libraryId",
|
|
||||||
"stackedAssets"."deviceId" AS "stackedAssets_deviceId",
|
|
||||||
"stackedAssets"."type" AS "stackedAssets_type",
|
|
||||||
"stackedAssets"."originalPath" AS "stackedAssets_originalPath",
|
|
||||||
"stackedAssets"."previewPath" AS "stackedAssets_previewPath",
|
|
||||||
"stackedAssets"."thumbnailPath" AS "stackedAssets_thumbnailPath",
|
|
||||||
"stackedAssets"."thumbhash" AS "stackedAssets_thumbhash",
|
|
||||||
"stackedAssets"."encodedVideoPath" AS "stackedAssets_encodedVideoPath",
|
|
||||||
"stackedAssets"."createdAt" AS "stackedAssets_createdAt",
|
|
||||||
"stackedAssets"."updatedAt" AS "stackedAssets_updatedAt",
|
|
||||||
"stackedAssets"."deletedAt" AS "stackedAssets_deletedAt",
|
|
||||||
"stackedAssets"."fileCreatedAt" AS "stackedAssets_fileCreatedAt",
|
|
||||||
"stackedAssets"."localDateTime" AS "stackedAssets_localDateTime",
|
|
||||||
"stackedAssets"."fileModifiedAt" AS "stackedAssets_fileModifiedAt",
|
|
||||||
"stackedAssets"."isFavorite" AS "stackedAssets_isFavorite",
|
|
||||||
"stackedAssets"."isArchived" AS "stackedAssets_isArchived",
|
|
||||||
"stackedAssets"."isExternal" AS "stackedAssets_isExternal",
|
|
||||||
"stackedAssets"."isOffline" AS "stackedAssets_isOffline",
|
|
||||||
"stackedAssets"."checksum" AS "stackedAssets_checksum",
|
|
||||||
"stackedAssets"."duration" AS "stackedAssets_duration",
|
|
||||||
"stackedAssets"."isVisible" AS "stackedAssets_isVisible",
|
|
||||||
"stackedAssets"."livePhotoVideoId" AS "stackedAssets_livePhotoVideoId",
|
|
||||||
"stackedAssets"."originalFileName" AS "stackedAssets_originalFileName",
|
|
||||||
"stackedAssets"."sidecarPath" AS "stackedAssets_sidecarPath",
|
|
||||||
"stackedAssets"."stackId" AS "stackedAssets_stackId",
|
|
||||||
"stackedAssets"."duplicateId" AS "stackedAssets_duplicateId"
|
|
||||||
FROM
|
FROM
|
||||||
"assets" "asset"
|
"assets" "asset"
|
||||||
LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id"
|
LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id"
|
||||||
LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId"
|
LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId"
|
||||||
LEFT JOIN "assets" "stackedAssets" ON "stackedAssets"."stackId" = "stack"."id"
|
|
||||||
AND ("stackedAssets"."deletedAt" IS NULL)
|
|
||||||
WHERE
|
WHERE
|
||||||
"asset"."isVisible" = true
|
"asset"."isVisible" = true
|
||||||
AND "asset"."ownerId" IN ($1)
|
AND "asset"."ownerId" IN ($1)
|
||||||
AND ("asset"."fileCreatedAt", "asset"."id") < ($2, $3)
|
AND "asset"."id" > $2
|
||||||
AND "asset"."updatedAt" <= $4
|
AND "asset"."updatedAt" <= $3
|
||||||
ORDER BY
|
ORDER BY
|
||||||
"asset"."fileCreatedAt" DESC,
|
"asset"."id" ASC
|
||||||
"asset"."id" DESC
|
|
||||||
LIMIT
|
LIMIT
|
||||||
10
|
10
|
||||||
|
|
||||||
@ -1156,42 +1124,11 @@ SELECT
|
|||||||
"exifInfo"."bitsPerSample" AS "exifInfo_bitsPerSample",
|
"exifInfo"."bitsPerSample" AS "exifInfo_bitsPerSample",
|
||||||
"exifInfo"."fps" AS "exifInfo_fps",
|
"exifInfo"."fps" AS "exifInfo_fps",
|
||||||
"stack"."id" AS "stack_id",
|
"stack"."id" AS "stack_id",
|
||||||
"stack"."primaryAssetId" AS "stack_primaryAssetId",
|
"stack"."primaryAssetId" AS "stack_primaryAssetId"
|
||||||
"stackedAssets"."id" AS "stackedAssets_id",
|
|
||||||
"stackedAssets"."deviceAssetId" AS "stackedAssets_deviceAssetId",
|
|
||||||
"stackedAssets"."ownerId" AS "stackedAssets_ownerId",
|
|
||||||
"stackedAssets"."libraryId" AS "stackedAssets_libraryId",
|
|
||||||
"stackedAssets"."deviceId" AS "stackedAssets_deviceId",
|
|
||||||
"stackedAssets"."type" AS "stackedAssets_type",
|
|
||||||
"stackedAssets"."originalPath" AS "stackedAssets_originalPath",
|
|
||||||
"stackedAssets"."previewPath" AS "stackedAssets_previewPath",
|
|
||||||
"stackedAssets"."thumbnailPath" AS "stackedAssets_thumbnailPath",
|
|
||||||
"stackedAssets"."thumbhash" AS "stackedAssets_thumbhash",
|
|
||||||
"stackedAssets"."encodedVideoPath" AS "stackedAssets_encodedVideoPath",
|
|
||||||
"stackedAssets"."createdAt" AS "stackedAssets_createdAt",
|
|
||||||
"stackedAssets"."updatedAt" AS "stackedAssets_updatedAt",
|
|
||||||
"stackedAssets"."deletedAt" AS "stackedAssets_deletedAt",
|
|
||||||
"stackedAssets"."fileCreatedAt" AS "stackedAssets_fileCreatedAt",
|
|
||||||
"stackedAssets"."localDateTime" AS "stackedAssets_localDateTime",
|
|
||||||
"stackedAssets"."fileModifiedAt" AS "stackedAssets_fileModifiedAt",
|
|
||||||
"stackedAssets"."isFavorite" AS "stackedAssets_isFavorite",
|
|
||||||
"stackedAssets"."isArchived" AS "stackedAssets_isArchived",
|
|
||||||
"stackedAssets"."isExternal" AS "stackedAssets_isExternal",
|
|
||||||
"stackedAssets"."isOffline" AS "stackedAssets_isOffline",
|
|
||||||
"stackedAssets"."checksum" AS "stackedAssets_checksum",
|
|
||||||
"stackedAssets"."duration" AS "stackedAssets_duration",
|
|
||||||
"stackedAssets"."isVisible" AS "stackedAssets_isVisible",
|
|
||||||
"stackedAssets"."livePhotoVideoId" AS "stackedAssets_livePhotoVideoId",
|
|
||||||
"stackedAssets"."originalFileName" AS "stackedAssets_originalFileName",
|
|
||||||
"stackedAssets"."sidecarPath" AS "stackedAssets_sidecarPath",
|
|
||||||
"stackedAssets"."stackId" AS "stackedAssets_stackId",
|
|
||||||
"stackedAssets"."duplicateId" AS "stackedAssets_duplicateId"
|
|
||||||
FROM
|
FROM
|
||||||
"assets" "asset"
|
"assets" "asset"
|
||||||
LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id"
|
LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id"
|
||||||
LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId"
|
LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId"
|
||||||
LEFT JOIN "assets" "stackedAssets" ON "stackedAssets"."stackId" = "stack"."id"
|
|
||||||
AND ("stackedAssets"."deletedAt" IS NULL)
|
|
||||||
WHERE
|
WHERE
|
||||||
"asset"."isVisible" = true
|
"asset"."isVisible" = true
|
||||||
AND "asset"."ownerId" IN ($1)
|
AND "asset"."ownerId" IN ($1)
|
||||||
|
@ -763,36 +763,40 @@ export class AssetRepository implements IAssetRepository {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
getAllForUserFullSync(options: AssetFullSyncOptions): Promise<AssetEntity[]> {
|
getAllForUserFullSync(options: AssetFullSyncOptions): Promise<AssetEntity[]> {
|
||||||
const { ownerId, lastCreationDate, lastId, updatedUntil, limit } = options;
|
const { ownerId, lastId, updatedUntil, limit } = options;
|
||||||
const builder = this.getBuilder({
|
const builder = this.getBuilder({
|
||||||
userIds: [ownerId],
|
userIds: [ownerId],
|
||||||
exifInfo: true, // also joins stack information
|
exifInfo: false, // need to do this manually because `exifInfo: true` also loads stacked assets messing with `limit`
|
||||||
withStacked: false, // return all assets individually as expected by the app
|
withStacked: false, // return all assets individually as expected by the app
|
||||||
});
|
})
|
||||||
|
.leftJoinAndSelect('asset.exifInfo', 'exifInfo')
|
||||||
|
.leftJoinAndSelect('asset.stack', 'stack')
|
||||||
|
.loadRelationCountAndMap('stack.assetCount', 'stack.assets', 'stackedAssetsCount');
|
||||||
|
|
||||||
if (lastCreationDate !== undefined && lastId !== undefined) {
|
if (lastId !== undefined) {
|
||||||
builder.andWhere('(asset.fileCreatedAt, asset.id) < (:lastCreationDate, :lastId)', {
|
builder.andWhere('asset.id > :lastId', { lastId });
|
||||||
lastCreationDate,
|
|
||||||
lastId,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
builder
|
||||||
return builder
|
|
||||||
.andWhere('asset.updatedAt <= :updatedUntil', { updatedUntil })
|
.andWhere('asset.updatedAt <= :updatedUntil', { updatedUntil })
|
||||||
.orderBy('asset.fileCreatedAt', 'DESC')
|
.orderBy('asset.id', 'ASC')
|
||||||
.addOrderBy('asset.id', 'DESC')
|
.limit(limit) // cannot use `take` for performance reasons
|
||||||
.limit(limit)
|
.withDeleted();
|
||||||
.withDeleted()
|
return builder.getMany();
|
||||||
.getMany();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [{ userIds: [DummyValue.UUID], updatedAfter: DummyValue.DATE }] })
|
@GenerateSql({ params: [{ userIds: [DummyValue.UUID], updatedAfter: DummyValue.DATE }] })
|
||||||
getChangedDeltaSync(options: AssetDeltaSyncOptions): Promise<AssetEntity[]> {
|
getChangedDeltaSync(options: AssetDeltaSyncOptions): Promise<AssetEntity[]> {
|
||||||
const builder = this.getBuilder({ userIds: options.userIds, exifInfo: true, withStacked: false })
|
const builder = this.getBuilder({
|
||||||
|
userIds: options.userIds,
|
||||||
|
exifInfo: false, // need to do this manually because `exifInfo: true` also loads stacked assets messing with `limit`
|
||||||
|
withStacked: false, // return all assets individually as expected by the app
|
||||||
|
})
|
||||||
|
.leftJoinAndSelect('asset.exifInfo', 'exifInfo')
|
||||||
|
.leftJoinAndSelect('asset.stack', 'stack')
|
||||||
|
.loadRelationCountAndMap('stack.assetCount', 'stack.assets', 'stackedAssetsCount')
|
||||||
.andWhere({ updatedAt: MoreThan(options.updatedAfter) })
|
.andWhere({ updatedAt: MoreThan(options.updatedAfter) })
|
||||||
.limit(options.limit)
|
.limit(options.limit) // cannot use `take` for performance reasons
|
||||||
.withDeleted();
|
.withDeleted();
|
||||||
|
|
||||||
return builder.getMany();
|
return builder.getMany();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ export class SyncService {
|
|||||||
await this.access.requirePermission(auth, Permission.TIMELINE_READ, userId);
|
await this.access.requirePermission(auth, Permission.TIMELINE_READ, userId);
|
||||||
const assets = await this.assetRepository.getAllForUserFullSync({
|
const assets = await this.assetRepository.getAllForUserFullSync({
|
||||||
ownerId: userId,
|
ownerId: userId,
|
||||||
lastCreationDate: dto.lastCreationDate,
|
|
||||||
updatedUntil: dto.updatedUntil,
|
updatedUntil: dto.updatedUntil,
|
||||||
lastId: dto.lastId,
|
lastId: dto.lastId,
|
||||||
limit: dto.limit,
|
limit: dto.limit,
|
||||||
|
Loading…
Reference in New Issue
Block a user