mirror of
https://github.com/immich-app/immich.git
synced 2025-05-31 04:05:39 -04:00
feat(mobile) duplicated asset upload handling mechanism (#853)
This commit is contained in:
parent
f1af17bf4d
commit
6159c83fd2
@ -24,6 +24,7 @@
|
|||||||
"backup_controller_page_backup_selected": "Ausgewählt: ",
|
"backup_controller_page_backup_selected": "Ausgewählt: ",
|
||||||
"backup_controller_page_backup_sub": "Gesicherte Fotos und Videos",
|
"backup_controller_page_backup_sub": "Gesicherte Fotos und Videos",
|
||||||
"backup_controller_page_cancel": "Abbrechen",
|
"backup_controller_page_cancel": "Abbrechen",
|
||||||
|
"backup_background_service_default_notification": "Suche nach neuen assets…",
|
||||||
"backup_controller_page_created": "Erstellt: {}",
|
"backup_controller_page_created": "Erstellt: {}",
|
||||||
"backup_controller_page_desc_backup": "Aktiviere die Sicherung um Elemente automatisch auf den Server zu laden.",
|
"backup_controller_page_desc_backup": "Aktiviere die Sicherung um Elemente automatisch auf den Server zu laden.",
|
||||||
"backup_controller_page_excluded": "Ausgeschlossen: ",
|
"backup_controller_page_excluded": "Ausgeschlossen: ",
|
||||||
@ -123,4 +124,4 @@
|
|||||||
"version_announcement_overlay_text_2": "Bitte nehm dir die Zeit und lese das ",
|
"version_announcement_overlay_text_2": "Bitte nehm dir die Zeit und lese das ",
|
||||||
"version_announcement_overlay_text_3": " und achte darauf, dass deine docker-compose und .env Dateien aktuell sind, vor allem wenn du ein System für automatische Updates benutzt (z.B. Watchtower).",
|
"version_announcement_overlay_text_3": " und achte darauf, dass deine docker-compose und .env Dateien aktuell sind, vor allem wenn du ein System für automatische Updates benutzt (z.B. Watchtower).",
|
||||||
"version_announcement_overlay_title": "Neue Server-Version verfügbar \uD83C\uDF89"
|
"version_announcement_overlay_title": "Neue Server-Version verfügbar \uD83C\uDF89"
|
||||||
}
|
}
|
||||||
|
@ -25,3 +25,7 @@ const String backgroundBackupInfoBox = "immichBackgroundBackupInfoBox"; // Box
|
|||||||
const String backupFailedSince = "immichBackupFailedSince"; // Key 1
|
const String backupFailedSince = "immichBackupFailedSince"; // Key 1
|
||||||
const String backupRequireWifi = "immichBackupRequireWifi"; // Key 2
|
const String backupRequireWifi = "immichBackupRequireWifi"; // Key 2
|
||||||
const String backupRequireCharging = "immichBackupRequireCharging"; // Key 3
|
const String backupRequireCharging = "immichBackupRequireCharging"; // Key 3
|
||||||
|
|
||||||
|
// Duplicate asset
|
||||||
|
const String duplicatedAssetsBox = "immichDuplicatedAssetsBox"; // Box
|
||||||
|
const String duplicatedAssetsKey = "immichDuplicatedAssetsKey"; // Key 1
|
||||||
|
@ -10,6 +10,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:immich_mobile/constants/locales.dart';
|
import 'package:immich_mobile/constants/locales.dart';
|
||||||
import 'package:immich_mobile/modules/backup/background_service/background.service.dart';
|
import 'package:immich_mobile/modules/backup/background_service/background.service.dart';
|
||||||
import 'package:immich_mobile/modules/backup/models/hive_backup_albums.model.dart';
|
import 'package:immich_mobile/modules/backup/models/hive_backup_albums.model.dart';
|
||||||
|
import 'package:immich_mobile/modules/backup/models/hive_duplicated_assets.model.dart';
|
||||||
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
|
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
|
||||||
import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart';
|
import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart';
|
||||||
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
||||||
@ -30,12 +31,14 @@ void main() async {
|
|||||||
|
|
||||||
Hive.registerAdapter(HiveSavedLoginInfoAdapter());
|
Hive.registerAdapter(HiveSavedLoginInfoAdapter());
|
||||||
Hive.registerAdapter(HiveBackupAlbumsAdapter());
|
Hive.registerAdapter(HiveBackupAlbumsAdapter());
|
||||||
|
Hive.registerAdapter(HiveDuplicatedAssetsAdapter());
|
||||||
|
|
||||||
await Hive.openBox(userInfoBox);
|
await Hive.openBox(userInfoBox);
|
||||||
await Hive.openBox<HiveSavedLoginInfo>(hiveLoginInfoBox);
|
await Hive.openBox<HiveSavedLoginInfo>(hiveLoginInfoBox);
|
||||||
await Hive.openBox<HiveBackupAlbums>(hiveBackupInfoBox);
|
await Hive.openBox<HiveBackupAlbums>(hiveBackupInfoBox);
|
||||||
await Hive.openBox(hiveGithubReleaseInfoBox);
|
await Hive.openBox(hiveGithubReleaseInfoBox);
|
||||||
await Hive.openBox(userSettingInfoBox);
|
await Hive.openBox(userSettingInfoBox);
|
||||||
|
await Hive.openBox<HiveDuplicatedAssets>(duplicatedAssetsBox);
|
||||||
|
|
||||||
SystemChrome.setSystemUIOverlayStyle(
|
SystemChrome.setSystemUIOverlayStyle(
|
||||||
const SystemUiOverlayStyle(
|
const SystemUiOverlayStyle(
|
||||||
|
@ -14,6 +14,7 @@ import 'package:immich_mobile/modules/backup/background_service/localization.dar
|
|||||||
import 'package:immich_mobile/modules/backup/models/current_upload_asset.model.dart';
|
import 'package:immich_mobile/modules/backup/models/current_upload_asset.model.dart';
|
||||||
import 'package:immich_mobile/modules/backup/models/error_upload_asset.model.dart';
|
import 'package:immich_mobile/modules/backup/models/error_upload_asset.model.dart';
|
||||||
import 'package:immich_mobile/modules/backup/models/hive_backup_albums.model.dart';
|
import 'package:immich_mobile/modules/backup/models/hive_backup_albums.model.dart';
|
||||||
|
import 'package:immich_mobile/modules/backup/models/hive_duplicated_assets.model.dart';
|
||||||
import 'package:immich_mobile/modules/backup/services/backup.service.dart';
|
import 'package:immich_mobile/modules/backup/services/backup.service.dart';
|
||||||
import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart';
|
import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart';
|
||||||
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
||||||
@ -316,10 +317,13 @@ class BackgroundService {
|
|||||||
|
|
||||||
Hive.registerAdapter(HiveSavedLoginInfoAdapter());
|
Hive.registerAdapter(HiveSavedLoginInfoAdapter());
|
||||||
Hive.registerAdapter(HiveBackupAlbumsAdapter());
|
Hive.registerAdapter(HiveBackupAlbumsAdapter());
|
||||||
|
Hive.registerAdapter(HiveDuplicatedAssetsAdapter());
|
||||||
|
|
||||||
await Hive.openBox(userInfoBox);
|
await Hive.openBox(userInfoBox);
|
||||||
await Hive.openBox<HiveSavedLoginInfo>(hiveLoginInfoBox);
|
await Hive.openBox<HiveSavedLoginInfo>(hiveLoginInfoBox);
|
||||||
await Hive.openBox(userSettingInfoBox);
|
await Hive.openBox(userSettingInfoBox);
|
||||||
await Hive.openBox(backgroundBackupInfoBox);
|
await Hive.openBox(backgroundBackupInfoBox);
|
||||||
|
await Hive.openBox<HiveDuplicatedAssets>(duplicatedAssetsBox);
|
||||||
|
|
||||||
ApiService apiService = ApiService();
|
ApiService apiService = ApiService();
|
||||||
apiService.setEndpoint(Hive.box(userInfoBox).get(serverEndpointKey));
|
apiService.setEndpoint(Hive.box(userInfoBox).get(serverEndpointKey));
|
||||||
@ -410,7 +414,7 @@ class BackgroundService {
|
|||||||
final bool ok = await backupService.backupAsset(
|
final bool ok = await backupService.backupAsset(
|
||||||
toUpload,
|
toUpload,
|
||||||
_cancellationToken!,
|
_cancellationToken!,
|
||||||
notifyTotalProgress ? _onAssetUploaded : (assetId, deviceId) {},
|
notifyTotalProgress ? _onAssetUploaded : (assetId, deviceId, isDup) {},
|
||||||
notifySingleProgress ? _onProgress : (sent, total) {},
|
notifySingleProgress ? _onProgress : (sent, total) {},
|
||||||
notifySingleProgress ? _onSetCurrentBackupAsset : (asset) {},
|
notifySingleProgress ? _onSetCurrentBackupAsset : (asset) {},
|
||||||
_onBackupError,
|
_onBackupError,
|
||||||
@ -429,7 +433,7 @@ class BackgroundService {
|
|||||||
return "$percent% ($_uploadedAssetsCount/$_assetsToUploadCount)";
|
return "$percent% ($_uploadedAssetsCount/$_assetsToUploadCount)";
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onAssetUploaded(String deviceAssetId, String deviceId) {
|
void _onAssetUploaded(String deviceAssetId, String deviceId, bool isDup) {
|
||||||
debugPrint("Uploaded $deviceAssetId from $deviceId");
|
debugPrint("Uploaded $deviceAssetId from $deviceId");
|
||||||
_uploadedAssetsCount++;
|
_uploadedAssetsCount++;
|
||||||
_updateNotification(
|
_updateNotification(
|
||||||
|
@ -45,8 +45,10 @@ class ErrorUploadAsset extends Equatable {
|
|||||||
List<Object> get props {
|
List<Object> get props {
|
||||||
return [
|
return [
|
||||||
id,
|
id,
|
||||||
|
createdAt,
|
||||||
fileName,
|
fileName,
|
||||||
fileType,
|
fileType,
|
||||||
|
asset,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
|
||||||
|
part 'hive_duplicated_assets.model.g.dart';
|
||||||
|
|
||||||
|
@HiveType(typeId: 2)
|
||||||
|
class HiveDuplicatedAssets {
|
||||||
|
@HiveField(0, defaultValue: [])
|
||||||
|
List<String> duplicatedAssetIds;
|
||||||
|
|
||||||
|
HiveDuplicatedAssets({
|
||||||
|
required this.duplicatedAssetIds,
|
||||||
|
});
|
||||||
|
|
||||||
|
HiveDuplicatedAssets copyWith({
|
||||||
|
List<String>? duplicatedAssetIds,
|
||||||
|
}) {
|
||||||
|
return HiveDuplicatedAssets(
|
||||||
|
duplicatedAssetIds: duplicatedAssetIds ?? this.duplicatedAssetIds,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'duplicatedAssetIds': duplicatedAssetIds,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory HiveDuplicatedAssets.fromMap(Map<String, dynamic> map) {
|
||||||
|
return HiveDuplicatedAssets(
|
||||||
|
duplicatedAssetIds: List<String>.from(map['duplicatedAssetIds']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String toJson() => json.encode(toMap());
|
||||||
|
|
||||||
|
factory HiveDuplicatedAssets.fromJson(String source) =>
|
||||||
|
HiveDuplicatedAssets.fromMap(json.decode(source));
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() =>
|
||||||
|
'HiveDuplicatedAssets(duplicatedAssetIds: $duplicatedAssetIds)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
final listEquals = const DeepCollectionEquality().equals;
|
||||||
|
|
||||||
|
return other is HiveDuplicatedAssets &&
|
||||||
|
listEquals(other.duplicatedAssetIds, duplicatedAssetIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => duplicatedAssetIds.hashCode;
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'hive_duplicated_assets.model.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// TypeAdapterGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
class HiveDuplicatedAssetsAdapter extends TypeAdapter<HiveDuplicatedAssets> {
|
||||||
|
@override
|
||||||
|
final int typeId = 2;
|
||||||
|
|
||||||
|
@override
|
||||||
|
HiveDuplicatedAssets read(BinaryReader reader) {
|
||||||
|
final numOfFields = reader.readByte();
|
||||||
|
final fields = <int, dynamic>{
|
||||||
|
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||||
|
};
|
||||||
|
return HiveDuplicatedAssets(
|
||||||
|
duplicatedAssetIds:
|
||||||
|
fields[0] == null ? [] : (fields[0] as List).cast<String>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void write(BinaryWriter writer, HiveDuplicatedAssets obj) {
|
||||||
|
writer
|
||||||
|
..writeByte(1)
|
||||||
|
..writeByte(0)
|
||||||
|
..write(obj.duplicatedAssetIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => typeId.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is HiveDuplicatedAssetsAdapter &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
typeId == other.typeId;
|
||||||
|
}
|
@ -10,6 +10,7 @@ import 'package:immich_mobile/modules/backup/models/backup_state.model.dart';
|
|||||||
import 'package:immich_mobile/modules/backup/models/current_upload_asset.model.dart';
|
import 'package:immich_mobile/modules/backup/models/current_upload_asset.model.dart';
|
||||||
import 'package:immich_mobile/modules/backup/models/error_upload_asset.model.dart';
|
import 'package:immich_mobile/modules/backup/models/error_upload_asset.model.dart';
|
||||||
import 'package:immich_mobile/modules/backup/models/hive_backup_albums.model.dart';
|
import 'package:immich_mobile/modules/backup/models/hive_backup_albums.model.dart';
|
||||||
|
import 'package:immich_mobile/modules/backup/models/hive_duplicated_assets.model.dart';
|
||||||
import 'package:immich_mobile/modules/backup/providers/error_backup_list.provider.dart';
|
import 'package:immich_mobile/modules/backup/providers/error_backup_list.provider.dart';
|
||||||
import 'package:immich_mobile/modules/backup/background_service/background.service.dart';
|
import 'package:immich_mobile/modules/backup/background_service/background.service.dart';
|
||||||
import 'package:immich_mobile/modules/backup/services/backup.service.dart';
|
import 'package:immich_mobile/modules/backup/services/backup.service.dart';
|
||||||
@ -296,6 +297,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|||||||
/// Those assets are unique and are used as the total assets
|
/// Those assets are unique and are used as the total assets
|
||||||
///
|
///
|
||||||
Future<void> _updateBackupAssetCount() async {
|
Future<void> _updateBackupAssetCount() async {
|
||||||
|
Set<String> duplicatedAssetIds = _backupService.getDuplicatedAssetIds();
|
||||||
Set<AssetEntity> assetsFromSelectedAlbums = {};
|
Set<AssetEntity> assetsFromSelectedAlbums = {};
|
||||||
Set<AssetEntity> assetsFromExcludedAlbums = {};
|
Set<AssetEntity> assetsFromExcludedAlbums = {};
|
||||||
|
|
||||||
@ -326,9 +328,15 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|||||||
// Find asset that were backup from selected albums
|
// Find asset that were backup from selected albums
|
||||||
Set<String> selectedAlbumsBackupAssets =
|
Set<String> selectedAlbumsBackupAssets =
|
||||||
Set.from(allUniqueAssets.map((e) => e.id));
|
Set.from(allUniqueAssets.map((e) => e.id));
|
||||||
|
|
||||||
selectedAlbumsBackupAssets
|
selectedAlbumsBackupAssets
|
||||||
.removeWhere((assetId) => !allAssetsInDatabase.contains(assetId));
|
.removeWhere((assetId) => !allAssetsInDatabase.contains(assetId));
|
||||||
|
|
||||||
|
// Remove duplicated asset from all unique assets
|
||||||
|
allUniqueAssets.removeWhere(
|
||||||
|
(asset) => duplicatedAssetIds.contains(asset.id),
|
||||||
|
);
|
||||||
|
|
||||||
if (allUniqueAssets.isEmpty) {
|
if (allUniqueAssets.isEmpty) {
|
||||||
debugPrint("No Asset On Device");
|
debugPrint("No Asset On Device");
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
@ -455,14 +463,26 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onAssetUploaded(String deviceAssetId, String deviceId) {
|
void _onAssetUploaded(
|
||||||
state = state.copyWith(
|
String deviceAssetId,
|
||||||
selectedAlbumsBackupAssetsIds: {
|
String deviceId,
|
||||||
...state.selectedAlbumsBackupAssetsIds,
|
bool isDuplicated,
|
||||||
deviceAssetId
|
) {
|
||||||
},
|
if (isDuplicated) {
|
||||||
allAssetsInDatabase: [...state.allAssetsInDatabase, deviceAssetId],
|
state = state.copyWith(
|
||||||
);
|
allUniqueAssets: state.allUniqueAssets
|
||||||
|
.where((asset) => asset.id != deviceAssetId)
|
||||||
|
.toSet(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
state = state.copyWith(
|
||||||
|
selectedAlbumsBackupAssetsIds: {
|
||||||
|
...state.selectedAlbumsBackupAssetsIds,
|
||||||
|
deviceAssetId
|
||||||
|
},
|
||||||
|
allAssetsInDatabase: [...state.allAssetsInDatabase, deviceAssetId],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (state.allUniqueAssets.length -
|
if (state.allUniqueAssets.length -
|
||||||
state.selectedAlbumsBackupAssetsIds.length ==
|
state.selectedAlbumsBackupAssetsIds.length ==
|
||||||
@ -564,6 +584,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|||||||
albums.lastExcludedBackupTime,
|
albums.lastExcludedBackupTime,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
await Hive.openBox<HiveDuplicatedAssets>(duplicatedAssetsBox);
|
||||||
final Box backgroundBox = await Hive.openBox(backgroundBackupInfoBox);
|
final Box backgroundBox = await Hive.openBox(backgroundBackupInfoBox);
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
backupProgress: previous,
|
backupProgress: previous,
|
||||||
@ -608,6 +629,13 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
debugPrint("[_notifyBackgroundServiceCanRun] failed to close box");
|
debugPrint("[_notifyBackgroundServiceCanRun] failed to close box");
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
if (Hive.isBoxOpen(duplicatedAssetsBox)) {
|
||||||
|
await Hive.box<HiveDuplicatedAssets>(duplicatedAssetsBox).close();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
debugPrint("[_notifyBackgroundServiceCanRun] failed to close box");
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (Hive.isBoxOpen(backgroundBackupInfoBox)) {
|
if (Hive.isBoxOpen(backgroundBackupInfoBox)) {
|
||||||
await Hive.box(backgroundBackupInfoBox).close();
|
await Hive.box(backgroundBackupInfoBox).close();
|
||||||
|
@ -19,6 +19,8 @@ import 'package:http_parser/http_parser.dart';
|
|||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
import 'package:cancellation_token_http/http.dart' as http;
|
import 'package:cancellation_token_http/http.dart' as http;
|
||||||
|
|
||||||
|
import '../models/hive_duplicated_assets.model.dart';
|
||||||
|
|
||||||
final backupServiceProvider = Provider(
|
final backupServiceProvider = Provider(
|
||||||
(ref) => BackupService(
|
(ref) => BackupService(
|
||||||
ref.watch(apiServiceProvider),
|
ref.watch(apiServiceProvider),
|
||||||
@ -41,6 +43,29 @@ class BackupService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _saveDuplicatedAssetIdToLocalStorage(List<String> deviceAssetIds) {
|
||||||
|
HiveDuplicatedAssets duplicatedAssets =
|
||||||
|
Hive.box<HiveDuplicatedAssets>(duplicatedAssetsBox)
|
||||||
|
.get(duplicatedAssetsKey) ??
|
||||||
|
HiveDuplicatedAssets(duplicatedAssetIds: []);
|
||||||
|
|
||||||
|
duplicatedAssets.duplicatedAssetIds =
|
||||||
|
{...duplicatedAssets.duplicatedAssetIds, ...deviceAssetIds}.toList();
|
||||||
|
|
||||||
|
Hive.box<HiveDuplicatedAssets>(duplicatedAssetsBox)
|
||||||
|
.put(duplicatedAssetsKey, duplicatedAssets);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get duplicated asset id from Hive storage
|
||||||
|
Set<String> getDuplicatedAssetIds() {
|
||||||
|
HiveDuplicatedAssets duplicatedAssets =
|
||||||
|
Hive.box<HiveDuplicatedAssets>(duplicatedAssetsBox)
|
||||||
|
.get(duplicatedAssetsKey) ??
|
||||||
|
HiveDuplicatedAssets(duplicatedAssetIds: []);
|
||||||
|
|
||||||
|
return duplicatedAssets.duplicatedAssetIds.toSet();
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns all assets newer than the last successful backup per album
|
/// Returns all assets newer than the last successful backup per album
|
||||||
Future<List<AssetEntity>> buildUploadCandidates(
|
Future<List<AssetEntity>> buildUploadCandidates(
|
||||||
HiveBackupAlbums backupAlbums,
|
HiveBackupAlbums backupAlbums,
|
||||||
@ -140,34 +165,47 @@ class BackupService {
|
|||||||
Future<List<AssetEntity>> removeAlreadyUploadedAssets(
|
Future<List<AssetEntity>> removeAlreadyUploadedAssets(
|
||||||
List<AssetEntity> candidates,
|
List<AssetEntity> candidates,
|
||||||
) async {
|
) async {
|
||||||
final String deviceId = Hive.box(userInfoBox).get(deviceIdKey);
|
if (candidates.isEmpty) {
|
||||||
if (candidates.length < 10) {
|
return candidates;
|
||||||
final List<CheckDuplicateAssetResponseDto?> duplicateResponse =
|
}
|
||||||
await Future.wait(
|
final Set<String> duplicatedAssetIds = getDuplicatedAssetIds();
|
||||||
candidates.map(
|
candidates = duplicatedAssetIds.isEmpty
|
||||||
(e) => _apiService.assetApi.checkDuplicateAsset(
|
? candidates
|
||||||
CheckDuplicateAssetDto(deviceAssetId: e.id, deviceId: deviceId),
|
: candidates
|
||||||
),
|
.whereNot((asset) => duplicatedAssetIds.contains(asset.id))
|
||||||
|
.toList();
|
||||||
|
if (candidates.isEmpty) {
|
||||||
|
return candidates;
|
||||||
|
}
|
||||||
|
final Set<String> existing = {};
|
||||||
|
try {
|
||||||
|
final String deviceId = Hive.box(userInfoBox).get(deviceIdKey);
|
||||||
|
final CheckExistingAssetsResponseDto? duplicates =
|
||||||
|
await _apiService.assetApi.checkExistingAssets(
|
||||||
|
CheckExistingAssetsDto(
|
||||||
|
deviceAssetIds: candidates.map((e) => e.id).toList(),
|
||||||
|
deviceId: deviceId,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return candidates
|
if (duplicates != null) {
|
||||||
.whereIndexed((i, e) => duplicateResponse[i]?.isExist == false)
|
existing.addAll(duplicates.existingIds);
|
||||||
.toList();
|
}
|
||||||
} else {
|
} on ApiException {
|
||||||
final List<String>? allAssetsInDatabase = await getDeviceBackupAsset();
|
// workaround for older server versions or when checking for too many assets at once
|
||||||
|
final List<String>? allAssetsInDatabase = await getDeviceBackupAsset();
|
||||||
if (allAssetsInDatabase == null) {
|
if (allAssetsInDatabase != null) {
|
||||||
return candidates;
|
existing.addAll(allAssetsInDatabase);
|
||||||
}
|
}
|
||||||
final Set<String> inDb = allAssetsInDatabase.toSet();
|
|
||||||
return candidates.whereNot((e) => inDb.contains(e.id)).toList();
|
|
||||||
}
|
}
|
||||||
|
return existing.isEmpty
|
||||||
|
? candidates
|
||||||
|
: candidates.whereNot((e) => existing.contains(e.id)).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> backupAsset(
|
Future<bool> backupAsset(
|
||||||
Iterable<AssetEntity> assetList,
|
Iterable<AssetEntity> assetList,
|
||||||
http.CancellationToken cancelToken,
|
http.CancellationToken cancelToken,
|
||||||
Function(String, String) singleAssetDoneCb,
|
Function(String, String, bool) uploadSuccessCb,
|
||||||
Function(int, int) uploadProgressCb,
|
Function(int, int) uploadProgressCb,
|
||||||
Function(CurrentUploadAsset) setCurrentUploadAssetCb,
|
Function(CurrentUploadAsset) setCurrentUploadAssetCb,
|
||||||
Function(ErrorUploadAsset) errorCb,
|
Function(ErrorUploadAsset) errorCb,
|
||||||
@ -176,6 +214,7 @@ class BackupService {
|
|||||||
String savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
|
String savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
|
||||||
File? file;
|
File? file;
|
||||||
bool anyErrors = false;
|
bool anyErrors = false;
|
||||||
|
final List<String> duplicatedAssetIds = [];
|
||||||
|
|
||||||
for (var entity in assetList) {
|
for (var entity in assetList) {
|
||||||
try {
|
try {
|
||||||
@ -235,8 +274,13 @@ class BackupService {
|
|||||||
|
|
||||||
var response = await req.send(cancellationToken: cancelToken);
|
var response = await req.send(cancellationToken: cancelToken);
|
||||||
|
|
||||||
if (response.statusCode == 201) {
|
if (response.statusCode == 200) {
|
||||||
singleAssetDoneCb(entity.id, deviceId);
|
// asset is a duplicate (already exists on the server)
|
||||||
|
duplicatedAssetIds.add(entity.id);
|
||||||
|
uploadSuccessCb(entity.id, deviceId, true);
|
||||||
|
} else if (response.statusCode == 201) {
|
||||||
|
// stored a new asset on the server
|
||||||
|
uploadSuccessCb(entity.id, deviceId, false);
|
||||||
} else {
|
} else {
|
||||||
var data = await response.stream.bytesToString();
|
var data = await response.stream.bytesToString();
|
||||||
var error = jsonDecode(data);
|
var error = jsonDecode(data);
|
||||||
@ -260,7 +304,8 @@ class BackupService {
|
|||||||
}
|
}
|
||||||
} on http.CancelledException {
|
} on http.CancelledException {
|
||||||
debugPrint("Backup was cancelled by the user");
|
debugPrint("Backup was cancelled by the user");
|
||||||
return false;
|
anyErrors = true;
|
||||||
|
break;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint("ERROR backupAsset: ${e.toString()}");
|
debugPrint("ERROR backupAsset: ${e.toString()}");
|
||||||
anyErrors = true;
|
anyErrors = true;
|
||||||
@ -271,6 +316,9 @@ class BackupService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (duplicatedAssetIds.isNotEmpty) {
|
||||||
|
_saveDuplicatedAssetIdToLocalStorage(duplicatedAssetIds);
|
||||||
|
}
|
||||||
return !anyErrors;
|
return !anyErrors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,7 +419,6 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||||||
ActionChip(
|
ActionChip(
|
||||||
avatar: Icon(
|
avatar: Icon(
|
||||||
Icons.info,
|
Icons.info,
|
||||||
size: 24,
|
|
||||||
color: Colors.red[400],
|
color: Colors.red[400],
|
||||||
),
|
),
|
||||||
elevation: 1,
|
elevation: 1,
|
||||||
|
@ -19,6 +19,8 @@ doc/AssetTypeEnum.md
|
|||||||
doc/AuthenticationApi.md
|
doc/AuthenticationApi.md
|
||||||
doc/CheckDuplicateAssetDto.md
|
doc/CheckDuplicateAssetDto.md
|
||||||
doc/CheckDuplicateAssetResponseDto.md
|
doc/CheckDuplicateAssetResponseDto.md
|
||||||
|
doc/CheckExistingAssetsDto.md
|
||||||
|
doc/CheckExistingAssetsResponseDto.md
|
||||||
doc/CreateAlbumDto.md
|
doc/CreateAlbumDto.md
|
||||||
doc/CreateDeviceInfoDto.md
|
doc/CreateDeviceInfoDto.md
|
||||||
doc/CreateProfileImageResponseDto.md
|
doc/CreateProfileImageResponseDto.md
|
||||||
@ -93,6 +95,8 @@ lib/model/asset_response_dto.dart
|
|||||||
lib/model/asset_type_enum.dart
|
lib/model/asset_type_enum.dart
|
||||||
lib/model/check_duplicate_asset_dto.dart
|
lib/model/check_duplicate_asset_dto.dart
|
||||||
lib/model/check_duplicate_asset_response_dto.dart
|
lib/model/check_duplicate_asset_response_dto.dart
|
||||||
|
lib/model/check_existing_assets_dto.dart
|
||||||
|
lib/model/check_existing_assets_response_dto.dart
|
||||||
lib/model/create_album_dto.dart
|
lib/model/create_album_dto.dart
|
||||||
lib/model/create_device_info_dto.dart
|
lib/model/create_device_info_dto.dart
|
||||||
lib/model/create_profile_image_response_dto.dart
|
lib/model/create_profile_image_response_dto.dart
|
||||||
@ -133,3 +137,5 @@ lib/model/user_count_response_dto.dart
|
|||||||
lib/model/user_response_dto.dart
|
lib/model/user_response_dto.dart
|
||||||
lib/model/validate_access_token_response_dto.dart
|
lib/model/validate_access_token_response_dto.dart
|
||||||
pubspec.yaml
|
pubspec.yaml
|
||||||
|
test/check_existing_assets_dto_test.dart
|
||||||
|
test/check_existing_assets_response_dto_test.dart
|
||||||
|
@ -76,6 +76,7 @@ Class | Method | HTTP request | Description
|
|||||||
*AlbumApi* | [**removeUserFromAlbum**](doc//AlbumApi.md#removeuserfromalbum) | **DELETE** /album/{albumId}/user/{userId} |
|
*AlbumApi* | [**removeUserFromAlbum**](doc//AlbumApi.md#removeuserfromalbum) | **DELETE** /album/{albumId}/user/{userId} |
|
||||||
*AlbumApi* | [**updateAlbumInfo**](doc//AlbumApi.md#updatealbuminfo) | **PATCH** /album/{albumId} |
|
*AlbumApi* | [**updateAlbumInfo**](doc//AlbumApi.md#updatealbuminfo) | **PATCH** /album/{albumId} |
|
||||||
*AssetApi* | [**checkDuplicateAsset**](doc//AssetApi.md#checkduplicateasset) | **POST** /asset/check |
|
*AssetApi* | [**checkDuplicateAsset**](doc//AssetApi.md#checkduplicateasset) | **POST** /asset/check |
|
||||||
|
*AssetApi* | [**checkExistingAssets**](doc//AssetApi.md#checkexistingassets) | **POST** /asset/exist |
|
||||||
*AssetApi* | [**deleteAsset**](doc//AssetApi.md#deleteasset) | **DELETE** /asset |
|
*AssetApi* | [**deleteAsset**](doc//AssetApi.md#deleteasset) | **DELETE** /asset |
|
||||||
*AssetApi* | [**downloadFile**](doc//AssetApi.md#downloadfile) | **GET** /asset/download |
|
*AssetApi* | [**downloadFile**](doc//AssetApi.md#downloadfile) | **GET** /asset/download |
|
||||||
*AssetApi* | [**getAllAssets**](doc//AssetApi.md#getallassets) | **GET** /asset |
|
*AssetApi* | [**getAllAssets**](doc//AssetApi.md#getallassets) | **GET** /asset |
|
||||||
@ -130,6 +131,8 @@ Class | Method | HTTP request | Description
|
|||||||
- [AssetTypeEnum](doc//AssetTypeEnum.md)
|
- [AssetTypeEnum](doc//AssetTypeEnum.md)
|
||||||
- [CheckDuplicateAssetDto](doc//CheckDuplicateAssetDto.md)
|
- [CheckDuplicateAssetDto](doc//CheckDuplicateAssetDto.md)
|
||||||
- [CheckDuplicateAssetResponseDto](doc//CheckDuplicateAssetResponseDto.md)
|
- [CheckDuplicateAssetResponseDto](doc//CheckDuplicateAssetResponseDto.md)
|
||||||
|
- [CheckExistingAssetsDto](doc//CheckExistingAssetsDto.md)
|
||||||
|
- [CheckExistingAssetsResponseDto](doc//CheckExistingAssetsResponseDto.md)
|
||||||
- [CreateAlbumDto](doc//CreateAlbumDto.md)
|
- [CreateAlbumDto](doc//CreateAlbumDto.md)
|
||||||
- [CreateDeviceInfoDto](doc//CreateDeviceInfoDto.md)
|
- [CreateDeviceInfoDto](doc//CreateDeviceInfoDto.md)
|
||||||
- [CreateProfileImageResponseDto](doc//CreateProfileImageResponseDto.md)
|
- [CreateProfileImageResponseDto](doc//CreateProfileImageResponseDto.md)
|
||||||
|
@ -10,6 +10,7 @@ All URIs are relative to */api*
|
|||||||
Method | HTTP request | Description
|
Method | HTTP request | Description
|
||||||
------------- | ------------- | -------------
|
------------- | ------------- | -------------
|
||||||
[**checkDuplicateAsset**](AssetApi.md#checkduplicateasset) | **POST** /asset/check |
|
[**checkDuplicateAsset**](AssetApi.md#checkduplicateasset) | **POST** /asset/check |
|
||||||
|
[**checkExistingAssets**](AssetApi.md#checkexistingassets) | **POST** /asset/exist |
|
||||||
[**deleteAsset**](AssetApi.md#deleteasset) | **DELETE** /asset |
|
[**deleteAsset**](AssetApi.md#deleteasset) | **DELETE** /asset |
|
||||||
[**downloadFile**](AssetApi.md#downloadfile) | **GET** /asset/download |
|
[**downloadFile**](AssetApi.md#downloadfile) | **GET** /asset/download |
|
||||||
[**getAllAssets**](AssetApi.md#getallassets) | **GET** /asset |
|
[**getAllAssets**](AssetApi.md#getallassets) | **GET** /asset |
|
||||||
@ -76,6 +77,55 @@ Name | Type | Description | Notes
|
|||||||
|
|
||||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||||
|
|
||||||
|
# **checkExistingAssets**
|
||||||
|
> CheckExistingAssetsResponseDto checkExistingAssets(checkExistingAssetsDto)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Checks if multiple assets exist on the server and returns all existing - used by background backup
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```dart
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
// TODO Configure HTTP Bearer authorization: bearer
|
||||||
|
// Case 1. Use String Token
|
||||||
|
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||||
|
// Case 2. Use Function which generate token.
|
||||||
|
// String yourTokenGeneratorFunction() { ... }
|
||||||
|
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||||
|
|
||||||
|
final api_instance = AssetApi();
|
||||||
|
final checkExistingAssetsDto = CheckExistingAssetsDto(); // CheckExistingAssetsDto |
|
||||||
|
|
||||||
|
try {
|
||||||
|
final result = api_instance.checkExistingAssets(checkExistingAssetsDto);
|
||||||
|
print(result);
|
||||||
|
} catch (e) {
|
||||||
|
print('Exception when calling AssetApi->checkExistingAssets: $e\n');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
Name | Type | Description | Notes
|
||||||
|
------------- | ------------- | ------------- | -------------
|
||||||
|
**checkExistingAssetsDto** | [**CheckExistingAssetsDto**](CheckExistingAssetsDto.md)| |
|
||||||
|
|
||||||
|
### Return type
|
||||||
|
|
||||||
|
[**CheckExistingAssetsResponseDto**](CheckExistingAssetsResponseDto.md)
|
||||||
|
|
||||||
|
### Authorization
|
||||||
|
|
||||||
|
[bearer](../README.md#bearer)
|
||||||
|
|
||||||
|
### HTTP request headers
|
||||||
|
|
||||||
|
- **Content-Type**: application/json
|
||||||
|
- **Accept**: application/json
|
||||||
|
|
||||||
|
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||||
|
|
||||||
# **deleteAsset**
|
# **deleteAsset**
|
||||||
> List<DeleteAssetResponseDto> deleteAsset(deleteAssetDto)
|
> List<DeleteAssetResponseDto> deleteAsset(deleteAssetDto)
|
||||||
|
|
||||||
|
16
mobile/openapi/doc/CheckExistingAssetsDto.md
Normal file
16
mobile/openapi/doc/CheckExistingAssetsDto.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# openapi.model.CheckExistingAssetsDto
|
||||||
|
|
||||||
|
## Load the model package
|
||||||
|
```dart
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
Name | Type | Description | Notes
|
||||||
|
------------ | ------------- | ------------- | -------------
|
||||||
|
**deviceAssetIds** | **List<String>** | | [default to const []]
|
||||||
|
**deviceId** | **String** | |
|
||||||
|
|
||||||
|
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||||
|
|
||||||
|
|
15
mobile/openapi/doc/CheckExistingAssetsResponseDto.md
Normal file
15
mobile/openapi/doc/CheckExistingAssetsResponseDto.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# openapi.model.CheckExistingAssetsResponseDto
|
||||||
|
|
||||||
|
## Load the model package
|
||||||
|
```dart
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
Name | Type | Description | Notes
|
||||||
|
------------ | ------------- | ------------- | -------------
|
||||||
|
**existingIds** | **List<String>** | | [default to const []]
|
||||||
|
|
||||||
|
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||||
|
|
||||||
|
|
@ -49,6 +49,8 @@ part 'model/asset_response_dto.dart';
|
|||||||
part 'model/asset_type_enum.dart';
|
part 'model/asset_type_enum.dart';
|
||||||
part 'model/check_duplicate_asset_dto.dart';
|
part 'model/check_duplicate_asset_dto.dart';
|
||||||
part 'model/check_duplicate_asset_response_dto.dart';
|
part 'model/check_duplicate_asset_response_dto.dart';
|
||||||
|
part 'model/check_existing_assets_dto.dart';
|
||||||
|
part 'model/check_existing_assets_response_dto.dart';
|
||||||
part 'model/create_album_dto.dart';
|
part 'model/create_album_dto.dart';
|
||||||
part 'model/create_device_info_dto.dart';
|
part 'model/create_device_info_dto.dart';
|
||||||
part 'model/create_profile_image_response_dto.dart';
|
part 'model/create_profile_image_response_dto.dart';
|
||||||
|
@ -72,6 +72,62 @@ class AssetApi {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// Checks if multiple assets exist on the server and returns all existing - used by background backup
|
||||||
|
///
|
||||||
|
/// Note: This method returns the HTTP [Response].
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [CheckExistingAssetsDto] checkExistingAssetsDto (required):
|
||||||
|
Future<Response> checkExistingAssetsWithHttpInfo(CheckExistingAssetsDto checkExistingAssetsDto,) async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final path = r'/asset/exist';
|
||||||
|
|
||||||
|
// ignore: prefer_final_locals
|
||||||
|
Object? postBody = checkExistingAssetsDto;
|
||||||
|
|
||||||
|
final queryParams = <QueryParam>[];
|
||||||
|
final headerParams = <String, String>{};
|
||||||
|
final formParams = <String, String>{};
|
||||||
|
|
||||||
|
const contentTypes = <String>['application/json'];
|
||||||
|
|
||||||
|
|
||||||
|
return apiClient.invokeAPI(
|
||||||
|
path,
|
||||||
|
'POST',
|
||||||
|
queryParams,
|
||||||
|
postBody,
|
||||||
|
headerParams,
|
||||||
|
formParams,
|
||||||
|
contentTypes.isEmpty ? null : contentTypes.first,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// Checks if multiple assets exist on the server and returns all existing - used by background backup
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [CheckExistingAssetsDto] checkExistingAssetsDto (required):
|
||||||
|
Future<CheckExistingAssetsResponseDto?> checkExistingAssets(CheckExistingAssetsDto checkExistingAssetsDto,) async {
|
||||||
|
final response = await checkExistingAssetsWithHttpInfo(checkExistingAssetsDto,);
|
||||||
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
|
}
|
||||||
|
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||||
|
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||||
|
// FormatException when trying to decode an empty string.
|
||||||
|
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||||
|
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'CheckExistingAssetsResponseDto',) as CheckExistingAssetsResponseDto;
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/// Performs an HTTP 'DELETE /asset' operation and returns the [Response].
|
/// Performs an HTTP 'DELETE /asset' operation and returns the [Response].
|
||||||
/// Parameters:
|
/// Parameters:
|
||||||
///
|
///
|
||||||
|
@ -220,6 +220,10 @@ class ApiClient {
|
|||||||
return CheckDuplicateAssetDto.fromJson(value);
|
return CheckDuplicateAssetDto.fromJson(value);
|
||||||
case 'CheckDuplicateAssetResponseDto':
|
case 'CheckDuplicateAssetResponseDto':
|
||||||
return CheckDuplicateAssetResponseDto.fromJson(value);
|
return CheckDuplicateAssetResponseDto.fromJson(value);
|
||||||
|
case 'CheckExistingAssetsDto':
|
||||||
|
return CheckExistingAssetsDto.fromJson(value);
|
||||||
|
case 'CheckExistingAssetsResponseDto':
|
||||||
|
return CheckExistingAssetsResponseDto.fromJson(value);
|
||||||
case 'CreateAlbumDto':
|
case 'CreateAlbumDto':
|
||||||
return CreateAlbumDto.fromJson(value);
|
return CreateAlbumDto.fromJson(value);
|
||||||
case 'CreateDeviceInfoDto':
|
case 'CreateDeviceInfoDto':
|
||||||
|
121
mobile/openapi/lib/model/check_existing_assets_dto.dart
Normal file
121
mobile/openapi/lib/model/check_existing_assets_dto.dart
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
//
|
||||||
|
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||||
|
//
|
||||||
|
// @dart=2.12
|
||||||
|
|
||||||
|
// ignore_for_file: unused_element, unused_import
|
||||||
|
// ignore_for_file: always_put_required_named_parameters_first
|
||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
|
|
||||||
|
part of openapi.api;
|
||||||
|
|
||||||
|
class CheckExistingAssetsDto {
|
||||||
|
/// Returns a new [CheckExistingAssetsDto] instance.
|
||||||
|
CheckExistingAssetsDto({
|
||||||
|
this.deviceAssetIds = const [],
|
||||||
|
required this.deviceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
List<String> deviceAssetIds;
|
||||||
|
|
||||||
|
String deviceId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) => identical(this, other) || other is CheckExistingAssetsDto &&
|
||||||
|
other.deviceAssetIds == deviceAssetIds &&
|
||||||
|
other.deviceId == deviceId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
// ignore: unnecessary_parenthesis
|
||||||
|
(deviceAssetIds.hashCode) +
|
||||||
|
(deviceId.hashCode);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'CheckExistingAssetsDto[deviceAssetIds=$deviceAssetIds, deviceId=$deviceId]';
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final _json = <String, dynamic>{};
|
||||||
|
_json[r'deviceAssetIds'] = deviceAssetIds;
|
||||||
|
_json[r'deviceId'] = deviceId;
|
||||||
|
return _json;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new [CheckExistingAssetsDto] instance and imports its values from
|
||||||
|
/// [value] if it's a [Map], null otherwise.
|
||||||
|
// ignore: prefer_constructors_over_static_methods
|
||||||
|
static CheckExistingAssetsDto? fromJson(dynamic value) {
|
||||||
|
if (value is Map) {
|
||||||
|
final json = value.cast<String, dynamic>();
|
||||||
|
|
||||||
|
// Ensure that the map contains the required keys.
|
||||||
|
// Note 1: the values aren't checked for validity beyond being non-null.
|
||||||
|
// Note 2: this code is stripped in release mode!
|
||||||
|
assert(() {
|
||||||
|
requiredKeys.forEach((key) {
|
||||||
|
assert(json.containsKey(key), 'Required key "CheckExistingAssetsDto[$key]" is missing from JSON.');
|
||||||
|
assert(json[key] != null, 'Required key "CheckExistingAssetsDto[$key]" has a null value in JSON.');
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}());
|
||||||
|
|
||||||
|
return CheckExistingAssetsDto(
|
||||||
|
deviceAssetIds: json[r'deviceAssetIds'] is List
|
||||||
|
? (json[r'deviceAssetIds'] as List).cast<String>()
|
||||||
|
: const [],
|
||||||
|
deviceId: mapValueOfType<String>(json, r'deviceId')!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<CheckExistingAssetsDto>? listFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final result = <CheckExistingAssetsDto>[];
|
||||||
|
if (json is List && json.isNotEmpty) {
|
||||||
|
for (final row in json) {
|
||||||
|
final value = CheckExistingAssetsDto.fromJson(row);
|
||||||
|
if (value != null) {
|
||||||
|
result.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toList(growable: growable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, CheckExistingAssetsDto> mapFromJson(dynamic json) {
|
||||||
|
final map = <String, CheckExistingAssetsDto>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
final value = CheckExistingAssetsDto.fromJson(entry.value);
|
||||||
|
if (value != null) {
|
||||||
|
map[entry.key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// maps a json object with a list of CheckExistingAssetsDto-objects as value to a dart map
|
||||||
|
static Map<String, List<CheckExistingAssetsDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final map = <String, List<CheckExistingAssetsDto>>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
final value = CheckExistingAssetsDto.listFromJson(entry.value, growable: growable,);
|
||||||
|
if (value != null) {
|
||||||
|
map[entry.key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The list of required keys that must be present in a JSON.
|
||||||
|
static const requiredKeys = <String>{
|
||||||
|
'deviceAssetIds',
|
||||||
|
'deviceId',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
113
mobile/openapi/lib/model/check_existing_assets_response_dto.dart
Normal file
113
mobile/openapi/lib/model/check_existing_assets_response_dto.dart
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
//
|
||||||
|
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||||
|
//
|
||||||
|
// @dart=2.12
|
||||||
|
|
||||||
|
// ignore_for_file: unused_element, unused_import
|
||||||
|
// ignore_for_file: always_put_required_named_parameters_first
|
||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
|
|
||||||
|
part of openapi.api;
|
||||||
|
|
||||||
|
class CheckExistingAssetsResponseDto {
|
||||||
|
/// Returns a new [CheckExistingAssetsResponseDto] instance.
|
||||||
|
CheckExistingAssetsResponseDto({
|
||||||
|
this.existingIds = const [],
|
||||||
|
});
|
||||||
|
|
||||||
|
List<String> existingIds;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) => identical(this, other) || other is CheckExistingAssetsResponseDto &&
|
||||||
|
other.existingIds == existingIds;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
// ignore: unnecessary_parenthesis
|
||||||
|
(existingIds.hashCode);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'CheckExistingAssetsResponseDto[existingIds=$existingIds]';
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final _json = <String, dynamic>{};
|
||||||
|
_json[r'existingIds'] = existingIds;
|
||||||
|
return _json;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new [CheckExistingAssetsResponseDto] instance and imports its values from
|
||||||
|
/// [value] if it's a [Map], null otherwise.
|
||||||
|
// ignore: prefer_constructors_over_static_methods
|
||||||
|
static CheckExistingAssetsResponseDto? fromJson(dynamic value) {
|
||||||
|
if (value is Map) {
|
||||||
|
final json = value.cast<String, dynamic>();
|
||||||
|
|
||||||
|
// Ensure that the map contains the required keys.
|
||||||
|
// Note 1: the values aren't checked for validity beyond being non-null.
|
||||||
|
// Note 2: this code is stripped in release mode!
|
||||||
|
assert(() {
|
||||||
|
requiredKeys.forEach((key) {
|
||||||
|
assert(json.containsKey(key), 'Required key "CheckExistingAssetsResponseDto[$key]" is missing from JSON.');
|
||||||
|
assert(json[key] != null, 'Required key "CheckExistingAssetsResponseDto[$key]" has a null value in JSON.');
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}());
|
||||||
|
|
||||||
|
return CheckExistingAssetsResponseDto(
|
||||||
|
existingIds: json[r'existingIds'] is List
|
||||||
|
? (json[r'existingIds'] as List).cast<String>()
|
||||||
|
: const [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<CheckExistingAssetsResponseDto>? listFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final result = <CheckExistingAssetsResponseDto>[];
|
||||||
|
if (json is List && json.isNotEmpty) {
|
||||||
|
for (final row in json) {
|
||||||
|
final value = CheckExistingAssetsResponseDto.fromJson(row);
|
||||||
|
if (value != null) {
|
||||||
|
result.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toList(growable: growable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, CheckExistingAssetsResponseDto> mapFromJson(dynamic json) {
|
||||||
|
final map = <String, CheckExistingAssetsResponseDto>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
final value = CheckExistingAssetsResponseDto.fromJson(entry.value);
|
||||||
|
if (value != null) {
|
||||||
|
map[entry.key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// maps a json object with a list of CheckExistingAssetsResponseDto-objects as value to a dart map
|
||||||
|
static Map<String, List<CheckExistingAssetsResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final map = <String, List<CheckExistingAssetsResponseDto>>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
final value = CheckExistingAssetsResponseDto.listFromJson(entry.value, growable: growable,);
|
||||||
|
if (value != null) {
|
||||||
|
map[entry.key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The list of required keys that must be present in a JSON.
|
||||||
|
static const requiredKeys = <String>{
|
||||||
|
'existingIds',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
32
mobile/openapi/test/check_existing_assets_dto_test.dart
Normal file
32
mobile/openapi/test/check_existing_assets_dto_test.dart
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
//
|
||||||
|
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||||
|
//
|
||||||
|
// @dart=2.12
|
||||||
|
|
||||||
|
// ignore_for_file: unused_element, unused_import
|
||||||
|
// ignore_for_file: always_put_required_named_parameters_first
|
||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
|
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
// tests for CheckExistingAssetsDto
|
||||||
|
void main() {
|
||||||
|
// final instance = CheckExistingAssetsDto();
|
||||||
|
|
||||||
|
group('test CheckExistingAssetsDto', () {
|
||||||
|
// List<String> deviceAssetIds (default value: const [])
|
||||||
|
test('to test the property `deviceAssetIds`', () async {
|
||||||
|
// TODO
|
||||||
|
});
|
||||||
|
|
||||||
|
// String deviceId
|
||||||
|
test('to test the property `deviceId`', () async {
|
||||||
|
// TODO
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||||
|
//
|
||||||
|
// @dart=2.12
|
||||||
|
|
||||||
|
// ignore_for_file: unused_element, unused_import
|
||||||
|
// ignore_for_file: always_put_required_named_parameters_first
|
||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
|
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
// tests for CheckExistingAssetsResponseDto
|
||||||
|
void main() {
|
||||||
|
// final instance = CheckExistingAssetsResponseDto();
|
||||||
|
|
||||||
|
group('test CheckExistingAssetsResponseDto', () {
|
||||||
|
// List<String> existingIds (default value: const [])
|
||||||
|
test('to test the property `existingIds`', () async {
|
||||||
|
// TODO
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
@ -137,6 +137,7 @@ describe('Album service', () => {
|
|||||||
getAssetWithNoEXIF: jest.fn(),
|
getAssetWithNoEXIF: jest.fn(),
|
||||||
getAssetWithNoThumbnail: jest.fn(),
|
getAssetWithNoThumbnail: jest.fn(),
|
||||||
getAssetWithNoSmartInfo: jest.fn(),
|
getAssetWithNoSmartInfo: jest.fn(),
|
||||||
|
getExistingAssets: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
sut = new AlbumService(albumRepositoryMock, assetRepositoryMock);
|
sut = new AlbumService(albumRepositoryMock, assetRepositoryMock);
|
||||||
|
@ -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 { TimeGroupEnum } from './dto/get-asset-count-by-time-bucket.dto';
|
||||||
import { GetAssetByTimeBucketDto } from './dto/get-asset-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 { 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 {
|
export interface IAssetRepository {
|
||||||
create(
|
create(
|
||||||
@ -32,6 +35,7 @@ export interface IAssetRepository {
|
|||||||
getAssetWithNoThumbnail(): Promise<AssetEntity[]>;
|
getAssetWithNoThumbnail(): Promise<AssetEntity[]>;
|
||||||
getAssetWithNoEXIF(): Promise<AssetEntity[]>;
|
getAssetWithNoEXIF(): Promise<AssetEntity[]>;
|
||||||
getAssetWithNoSmartInfo(): Promise<AssetEntity[]>;
|
getAssetWithNoSmartInfo(): Promise<AssetEntity[]>;
|
||||||
|
getExistingAssets(userId: string, checkDuplicateAssetDto: CheckExistingAssetsDto): Promise<CheckExistingAssetsResponseDto>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ASSET_REPOSITORY = 'ASSET_REPOSITORY';
|
export const ASSET_REPOSITORY = 'ASSET_REPOSITORY';
|
||||||
@ -279,4 +283,17 @@ export class AssetRepository implements IAssetRepository {
|
|||||||
relations: ['exifInfo'],
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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 { GetAssetByTimeBucketDto } from './dto/get-asset-by-time-bucket.dto';
|
||||||
import { QueryFailedError } from 'typeorm';
|
import { QueryFailedError } from 'typeorm';
|
||||||
import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-user-id-response.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';
|
||||||
|
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@ -74,6 +76,7 @@ export class AssetController {
|
|||||||
@GetAuthUser() authUser: AuthUserDto,
|
@GetAuthUser() authUser: AuthUserDto,
|
||||||
@UploadedFile() file: Express.Multer.File,
|
@UploadedFile() file: Express.Multer.File,
|
||||||
@Body(ValidationPipe) assetInfo: CreateAssetDto,
|
@Body(ValidationPipe) assetInfo: CreateAssetDto,
|
||||||
|
@Response({ passthrough: true }) res: Res,
|
||||||
): Promise<AssetFileUploadResponseDto> {
|
): Promise<AssetFileUploadResponseDto> {
|
||||||
const checksum = await this.assetService.calculateChecksum(file.path);
|
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') {
|
if (err instanceof QueryFailedError && (err as any).constraint === 'UQ_userid_checksum') {
|
||||||
const existedAsset = await this.assetService.getAssetByChecksum(authUser.id, 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);
|
return new AssetFileUploadResponseDto(existedAsset.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,4 +258,16 @@ export class AssetController {
|
|||||||
): Promise<CheckDuplicateAssetResponseDto> {
|
): Promise<CheckDuplicateAssetResponseDto> {
|
||||||
return await this.assetService.checkDuplicatedAsset(authUser, checkDuplicateAssetDto);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,7 @@ describe('AssetService', () => {
|
|||||||
getAssetWithNoEXIF: jest.fn(),
|
getAssetWithNoEXIF: jest.fn(),
|
||||||
getAssetWithNoThumbnail: jest.fn(),
|
getAssetWithNoThumbnail: jest.fn(),
|
||||||
getAssetWithNoSmartInfo: jest.fn(),
|
getAssetWithNoSmartInfo: jest.fn(),
|
||||||
|
getExistingAssets: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
sui = new AssetService(assetRepositoryMock, a);
|
sui = new AssetService(assetRepositoryMock, a);
|
||||||
|
@ -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 { GetAssetByTimeBucketDto } from './dto/get-asset-by-time-bucket.dto';
|
||||||
import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-user-id-response.dto';
|
import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-user-id-response.dto';
|
||||||
import { timeUtils } from '@app/common/utils';
|
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);
|
const fileInfo = promisify(stat);
|
||||||
|
|
||||||
@ -466,6 +468,13 @@ export class AssetService {
|
|||||||
return new CheckDuplicateAssetResponseDto(isDuplicated, res?.id);
|
return new CheckDuplicateAssetResponseDto(isDuplicated, res?.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async checkExistingAssets(
|
||||||
|
authUser: AuthUserDto,
|
||||||
|
checkExistingAssetsDto: CheckExistingAssetsDto,
|
||||||
|
): Promise<CheckExistingAssetsResponseDto> {
|
||||||
|
return this._assetRepository.getExistingAssets(authUser.id, checkExistingAssetsDto);
|
||||||
|
}
|
||||||
|
|
||||||
async getAssetCountByTimeBucket(
|
async getAssetCountByTimeBucket(
|
||||||
authUser: AuthUserDto,
|
authUser: AuthUserDto,
|
||||||
getAssetCountByTimeBucketDto: GetAssetCountByTimeBucketDto,
|
getAssetCountByTimeBucketDto: GetAssetCountByTimeBucketDto,
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
import { IsNotEmpty } from 'class-validator';
|
||||||
|
|
||||||
|
export class CheckExistingAssetsDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
deviceAssetIds!: string[];
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
deviceId!: string;
|
||||||
|
}
|
@ -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
@ -452,6 +452,38 @@ export interface CheckDuplicateAssetResponseDto {
|
|||||||
*/
|
*/
|
||||||
'id'?: string;
|
'id'?: string;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface CheckExistingAssetsDto
|
||||||
|
*/
|
||||||
|
export interface CheckExistingAssetsDto {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {Array<string>}
|
||||||
|
* @memberof CheckExistingAssetsDto
|
||||||
|
*/
|
||||||
|
'deviceAssetIds': Array<string>;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof CheckExistingAssetsDto
|
||||||
|
*/
|
||||||
|
'deviceId': string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface CheckExistingAssetsResponseDto
|
||||||
|
*/
|
||||||
|
export interface CheckExistingAssetsResponseDto {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {Array<string>}
|
||||||
|
* @memberof CheckExistingAssetsResponseDto
|
||||||
|
*/
|
||||||
|
'existingIds': Array<string>;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
@ -2334,6 +2366,46 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
|
|||||||
options: localVarRequestOptions,
|
options: localVarRequestOptions,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Checks if multiple assets exist on the server and returns all existing - used by background backup
|
||||||
|
* @summary
|
||||||
|
* @param {CheckExistingAssetsDto} checkExistingAssetsDto
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
checkExistingAssets: async (checkExistingAssetsDto: CheckExistingAssetsDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||||
|
// verify required parameter 'checkExistingAssetsDto' is not null or undefined
|
||||||
|
assertParamExists('checkExistingAssets', 'checkExistingAssetsDto', checkExistingAssetsDto)
|
||||||
|
const localVarPath = `/asset/exist`;
|
||||||
|
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||||
|
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||||
|
let baseOptions;
|
||||||
|
if (configuration) {
|
||||||
|
baseOptions = configuration.baseOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
||||||
|
const localVarHeaderParameter = {} as any;
|
||||||
|
const localVarQueryParameter = {} as any;
|
||||||
|
|
||||||
|
// authentication bearer required
|
||||||
|
// http bearer authentication required
|
||||||
|
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||||
|
|
||||||
|
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||||
|
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||||
|
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||||
|
localVarRequestOptions.data = serializeDataIfNeeded(checkExistingAssetsDto, localVarRequestOptions, configuration)
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: toPathString(localVarUrlObj),
|
||||||
|
options: localVarRequestOptions,
|
||||||
|
};
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {DeleteAssetDto} deleteAssetDto
|
* @param {DeleteAssetDto} deleteAssetDto
|
||||||
@ -2953,6 +3025,17 @@ export const AssetApiFp = function(configuration?: Configuration) {
|
|||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.checkDuplicateAsset(checkDuplicateAssetDto, options);
|
const localVarAxiosArgs = await localVarAxiosParamCreator.checkDuplicateAsset(checkDuplicateAssetDto, options);
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Checks if multiple assets exist on the server and returns all existing - used by background backup
|
||||||
|
* @summary
|
||||||
|
* @param {CheckExistingAssetsDto} checkExistingAssetsDto
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
async checkExistingAssets(checkExistingAssetsDto: CheckExistingAssetsDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<CheckExistingAssetsResponseDto>> {
|
||||||
|
const localVarAxiosArgs = await localVarAxiosParamCreator.checkExistingAssets(checkExistingAssetsDto, options);
|
||||||
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {DeleteAssetDto} deleteAssetDto
|
* @param {DeleteAssetDto} deleteAssetDto
|
||||||
@ -3128,6 +3211,16 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
|
|||||||
checkDuplicateAsset(checkDuplicateAssetDto: CheckDuplicateAssetDto, options?: any): AxiosPromise<CheckDuplicateAssetResponseDto> {
|
checkDuplicateAsset(checkDuplicateAssetDto: CheckDuplicateAssetDto, options?: any): AxiosPromise<CheckDuplicateAssetResponseDto> {
|
||||||
return localVarFp.checkDuplicateAsset(checkDuplicateAssetDto, options).then((request) => request(axios, basePath));
|
return localVarFp.checkDuplicateAsset(checkDuplicateAssetDto, options).then((request) => request(axios, basePath));
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Checks if multiple assets exist on the server and returns all existing - used by background backup
|
||||||
|
* @summary
|
||||||
|
* @param {CheckExistingAssetsDto} checkExistingAssetsDto
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
checkExistingAssets(checkExistingAssetsDto: CheckExistingAssetsDto, options?: any): AxiosPromise<CheckExistingAssetsResponseDto> {
|
||||||
|
return localVarFp.checkExistingAssets(checkExistingAssetsDto, options).then((request) => request(axios, basePath));
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {DeleteAssetDto} deleteAssetDto
|
* @param {DeleteAssetDto} deleteAssetDto
|
||||||
@ -3290,6 +3383,18 @@ export class AssetApi extends BaseAPI {
|
|||||||
return AssetApiFp(this.configuration).checkDuplicateAsset(checkDuplicateAssetDto, options).then((request) => request(this.axios, this.basePath));
|
return AssetApiFp(this.configuration).checkDuplicateAsset(checkDuplicateAssetDto, options).then((request) => request(this.axios, this.basePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if multiple assets exist on the server and returns all existing - used by background backup
|
||||||
|
* @summary
|
||||||
|
* @param {CheckExistingAssetsDto} checkExistingAssetsDto
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
* @memberof AssetApi
|
||||||
|
*/
|
||||||
|
public checkExistingAssets(checkExistingAssetsDto: CheckExistingAssetsDto, options?: AxiosRequestConfig) {
|
||||||
|
return AssetApiFp(this.configuration).checkExistingAssets(checkExistingAssetsDto, options).then((request) => request(this.axios, this.basePath));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {DeleteAssetDto} deleteAssetDto
|
* @param {DeleteAssetDto} deleteAssetDto
|
||||||
|
@ -200,12 +200,12 @@ async function fileUploader(asset: File, uploadType: UploadType) {
|
|||||||
}
|
}
|
||||||
// TODO: This should have a proper type
|
// TODO: This should have a proper type
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
function handleUploadError(asset: File, respBody?: any) {
|
function handleUploadError(asset: File, respBody: any, extraMessage?: string) {
|
||||||
const extraMsg = respBody ? ' ' + respBody.message : '';
|
const extraMsg = respBody ? ' ' + respBody?.message : '';
|
||||||
|
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
type: NotificationType.Error,
|
type: NotificationType.Error,
|
||||||
message: `Cannot upload file ${asset.name}!${extraMsg}`,
|
message: `Cannot upload file ${asset.name} ${extraMsg}${extraMessage}`,
|
||||||
timeout: 5000
|
timeout: 5000
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user