mirror of
https://github.com/immich-app/immich.git
synced 2025-12-17 17:23:20 +03:00
refactor: better typings for integrity API
This commit is contained in:
2
mobile/openapi/README.md
generated
2
mobile/openapi/README.md
generated
@@ -419,6 +419,7 @@ Class | Method | HTTP request | Description
|
||||
- [MaintenanceListBackupsResponseDto](doc//MaintenanceListBackupsResponseDto.md)
|
||||
- [MaintenanceLoginDto](doc//MaintenanceLoginDto.md)
|
||||
- [MaintenanceStatusResponseDto](doc//MaintenanceStatusResponseDto.md)
|
||||
- [MaintenanceStorageFolderIntegrityDto](doc//MaintenanceStorageFolderIntegrityDto.md)
|
||||
- [ManualJobName](doc//ManualJobName.md)
|
||||
- [MapMarkerResponseDto](doc//MapMarkerResponseDto.md)
|
||||
- [MapReverseGeocodeResponseDto](doc//MapReverseGeocodeResponseDto.md)
|
||||
@@ -525,6 +526,7 @@ Class | Method | HTTP request | Description
|
||||
- [StackResponseDto](doc//StackResponseDto.md)
|
||||
- [StackUpdateDto](doc//StackUpdateDto.md)
|
||||
- [StatisticsSearchDto](doc//StatisticsSearchDto.md)
|
||||
- [StorageFolder](doc//StorageFolder.md)
|
||||
- [SyncAckDeleteDto](doc//SyncAckDeleteDto.md)
|
||||
- [SyncAckDto](doc//SyncAckDto.md)
|
||||
- [SyncAckSetDto](doc//SyncAckSetDto.md)
|
||||
|
||||
2
mobile/openapi/lib/api.dart
generated
2
mobile/openapi/lib/api.dart
generated
@@ -170,6 +170,7 @@ part 'model/maintenance_integrity_response_dto.dart';
|
||||
part 'model/maintenance_list_backups_response_dto.dart';
|
||||
part 'model/maintenance_login_dto.dart';
|
||||
part 'model/maintenance_status_response_dto.dart';
|
||||
part 'model/maintenance_storage_folder_integrity_dto.dart';
|
||||
part 'model/manual_job_name.dart';
|
||||
part 'model/map_marker_response_dto.dart';
|
||||
part 'model/map_reverse_geocode_response_dto.dart';
|
||||
@@ -276,6 +277,7 @@ part 'model/stack_create_dto.dart';
|
||||
part 'model/stack_response_dto.dart';
|
||||
part 'model/stack_update_dto.dart';
|
||||
part 'model/statistics_search_dto.dart';
|
||||
part 'model/storage_folder.dart';
|
||||
part 'model/sync_ack_delete_dto.dart';
|
||||
part 'model/sync_ack_dto.dart';
|
||||
part 'model/sync_ack_set_dto.dart';
|
||||
|
||||
4
mobile/openapi/lib/api_client.dart
generated
4
mobile/openapi/lib/api_client.dart
generated
@@ -390,6 +390,8 @@ class ApiClient {
|
||||
return MaintenanceLoginDto.fromJson(value);
|
||||
case 'MaintenanceStatusResponseDto':
|
||||
return MaintenanceStatusResponseDto.fromJson(value);
|
||||
case 'MaintenanceStorageFolderIntegrityDto':
|
||||
return MaintenanceStorageFolderIntegrityDto.fromJson(value);
|
||||
case 'ManualJobName':
|
||||
return ManualJobNameTypeTransformer().decode(value);
|
||||
case 'MapMarkerResponseDto':
|
||||
@@ -602,6 +604,8 @@ class ApiClient {
|
||||
return StackUpdateDto.fromJson(value);
|
||||
case 'StatisticsSearchDto':
|
||||
return StatisticsSearchDto.fromJson(value);
|
||||
case 'StorageFolder':
|
||||
return StorageFolderTypeTransformer().decode(value);
|
||||
case 'SyncAckDeleteDto':
|
||||
return SyncAckDeleteDto.fromJson(value);
|
||||
case 'SyncAckDto':
|
||||
|
||||
3
mobile/openapi/lib/api_helper.dart
generated
3
mobile/openapi/lib/api_helper.dart
generated
@@ -151,6 +151,9 @@ String parameterToString(dynamic value) {
|
||||
if (value is SourceType) {
|
||||
return SourceTypeTypeTransformer().encode(value).toString();
|
||||
}
|
||||
if (value is StorageFolder) {
|
||||
return StorageFolderTypeTransformer().encode(value).toString();
|
||||
}
|
||||
if (value is SyncEntityType) {
|
||||
return SyncEntityTypeTypeTransformer().encode(value).toString();
|
||||
}
|
||||
|
||||
@@ -13,32 +13,26 @@ part of openapi.api;
|
||||
class MaintenanceIntegrityResponseDto {
|
||||
/// Returns a new [MaintenanceIntegrityResponseDto] instance.
|
||||
MaintenanceIntegrityResponseDto({
|
||||
required this.storageHeuristics,
|
||||
required this.storageIntegrity,
|
||||
this.storage = const [],
|
||||
});
|
||||
|
||||
Object storageHeuristics;
|
||||
|
||||
Object storageIntegrity;
|
||||
List<MaintenanceStorageFolderIntegrityDto> storage;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is MaintenanceIntegrityResponseDto &&
|
||||
other.storageHeuristics == storageHeuristics &&
|
||||
other.storageIntegrity == storageIntegrity;
|
||||
_deepEquality.equals(other.storage, storage);
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(storageHeuristics.hashCode) +
|
||||
(storageIntegrity.hashCode);
|
||||
(storage.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'MaintenanceIntegrityResponseDto[storageHeuristics=$storageHeuristics, storageIntegrity=$storageIntegrity]';
|
||||
String toString() => 'MaintenanceIntegrityResponseDto[storage=$storage]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'storageHeuristics'] = this.storageHeuristics;
|
||||
json[r'storageIntegrity'] = this.storageIntegrity;
|
||||
json[r'storage'] = this.storage;
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -51,8 +45,7 @@ class MaintenanceIntegrityResponseDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return MaintenanceIntegrityResponseDto(
|
||||
storageHeuristics: mapValueOfType<Object>(json, r'storageHeuristics')!,
|
||||
storageIntegrity: mapValueOfType<Object>(json, r'storageIntegrity')!,
|
||||
storage: MaintenanceStorageFolderIntegrityDto.listFromJson(json[r'storage']),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
@@ -100,8 +93,7 @@ class MaintenanceIntegrityResponseDto {
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'storageHeuristics',
|
||||
'storageIntegrity',
|
||||
'storage',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
123
mobile/openapi/lib/model/maintenance_storage_folder_integrity_dto.dart
generated
Normal file
123
mobile/openapi/lib/model/maintenance_storage_folder_integrity_dto.dart
generated
Normal file
@@ -0,0 +1,123 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// 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 MaintenanceStorageFolderIntegrityDto {
|
||||
/// Returns a new [MaintenanceStorageFolderIntegrityDto] instance.
|
||||
MaintenanceStorageFolderIntegrityDto({
|
||||
required this.files,
|
||||
required this.folder,
|
||||
required this.readable,
|
||||
required this.writable,
|
||||
});
|
||||
|
||||
num files;
|
||||
|
||||
StorageFolder folder;
|
||||
|
||||
bool readable;
|
||||
|
||||
bool writable;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is MaintenanceStorageFolderIntegrityDto &&
|
||||
other.files == files &&
|
||||
other.folder == folder &&
|
||||
other.readable == readable &&
|
||||
other.writable == writable;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(files.hashCode) +
|
||||
(folder.hashCode) +
|
||||
(readable.hashCode) +
|
||||
(writable.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'MaintenanceStorageFolderIntegrityDto[files=$files, folder=$folder, readable=$readable, writable=$writable]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'files'] = this.files;
|
||||
json[r'folder'] = this.folder;
|
||||
json[r'readable'] = this.readable;
|
||||
json[r'writable'] = this.writable;
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [MaintenanceStorageFolderIntegrityDto] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static MaintenanceStorageFolderIntegrityDto? fromJson(dynamic value) {
|
||||
upgradeDto(value, "MaintenanceStorageFolderIntegrityDto");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return MaintenanceStorageFolderIntegrityDto(
|
||||
files: num.parse('${json[r'files']}'),
|
||||
folder: StorageFolder.fromJson(json[r'folder'])!,
|
||||
readable: mapValueOfType<bool>(json, r'readable')!,
|
||||
writable: mapValueOfType<bool>(json, r'writable')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<MaintenanceStorageFolderIntegrityDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <MaintenanceStorageFolderIntegrityDto>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = MaintenanceStorageFolderIntegrityDto.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, MaintenanceStorageFolderIntegrityDto> mapFromJson(dynamic json) {
|
||||
final map = <String, MaintenanceStorageFolderIntegrityDto>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = MaintenanceStorageFolderIntegrityDto.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of MaintenanceStorageFolderIntegrityDto-objects as value to a dart map
|
||||
static Map<String, List<MaintenanceStorageFolderIntegrityDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<MaintenanceStorageFolderIntegrityDto>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = MaintenanceStorageFolderIntegrityDto.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'files',
|
||||
'folder',
|
||||
'readable',
|
||||
'writable',
|
||||
};
|
||||
}
|
||||
|
||||
97
mobile/openapi/lib/model/storage_folder.dart
generated
Normal file
97
mobile/openapi/lib/model/storage_folder.dart
generated
Normal file
@@ -0,0 +1,97 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// 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 StorageFolder {
|
||||
/// Instantiate a new enum with the provided [value].
|
||||
const StorageFolder._(this.value);
|
||||
|
||||
/// The underlying value of this enum member.
|
||||
final String value;
|
||||
|
||||
@override
|
||||
String toString() => value;
|
||||
|
||||
String toJson() => value;
|
||||
|
||||
static const encodedVideo = StorageFolder._(r'encoded-video');
|
||||
static const library_ = StorageFolder._(r'library');
|
||||
static const upload = StorageFolder._(r'upload');
|
||||
static const profile = StorageFolder._(r'profile');
|
||||
static const thumbs = StorageFolder._(r'thumbs');
|
||||
static const backups = StorageFolder._(r'backups');
|
||||
|
||||
/// List of all possible values in this [enum][StorageFolder].
|
||||
static const values = <StorageFolder>[
|
||||
encodedVideo,
|
||||
library_,
|
||||
upload,
|
||||
profile,
|
||||
thumbs,
|
||||
backups,
|
||||
];
|
||||
|
||||
static StorageFolder? fromJson(dynamic value) => StorageFolderTypeTransformer().decode(value);
|
||||
|
||||
static List<StorageFolder> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <StorageFolder>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = StorageFolder.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
}
|
||||
|
||||
/// Transformation class that can [encode] an instance of [StorageFolder] to String,
|
||||
/// and [decode] dynamic data back to [StorageFolder].
|
||||
class StorageFolderTypeTransformer {
|
||||
factory StorageFolderTypeTransformer() => _instance ??= const StorageFolderTypeTransformer._();
|
||||
|
||||
const StorageFolderTypeTransformer._();
|
||||
|
||||
String encode(StorageFolder data) => data.value;
|
||||
|
||||
/// Decodes a [dynamic value][data] to a StorageFolder.
|
||||
///
|
||||
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
|
||||
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
|
||||
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
|
||||
///
|
||||
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
|
||||
/// and users are still using an old app with the old code.
|
||||
StorageFolder? decode(dynamic data, {bool allowNull = true}) {
|
||||
if (data != null) {
|
||||
switch (data) {
|
||||
case r'encoded-video': return StorageFolder.encodedVideo;
|
||||
case r'library': return StorageFolder.library_;
|
||||
case r'upload': return StorageFolder.upload;
|
||||
case r'profile': return StorageFolder.profile;
|
||||
case r'thumbs': return StorageFolder.thumbs;
|
||||
case r'backups': return StorageFolder.backups;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
throw ArgumentError('Unknown enum value to decode: $data');
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Singleton [StorageFolderTypeTransformer] instance.
|
||||
static StorageFolderTypeTransformer? _instance;
|
||||
}
|
||||
|
||||
@@ -16843,16 +16843,15 @@
|
||||
},
|
||||
"MaintenanceIntegrityResponseDto": {
|
||||
"properties": {
|
||||
"storageHeuristics": {
|
||||
"type": "object"
|
||||
"storage": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/MaintenanceStorageFolderIntegrityDto"
|
||||
},
|
||||
"storageIntegrity": {
|
||||
"type": "object"
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"storageHeuristics",
|
||||
"storageIntegrity"
|
||||
"storage"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
@@ -16902,6 +16901,33 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"MaintenanceStorageFolderIntegrityDto": {
|
||||
"properties": {
|
||||
"files": {
|
||||
"type": "number"
|
||||
},
|
||||
"folder": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/StorageFolder"
|
||||
}
|
||||
]
|
||||
},
|
||||
"readable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"writable": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"files",
|
||||
"folder",
|
||||
"readable",
|
||||
"writable"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"MaintenanceUploadBackupDto": {
|
||||
"properties": {
|
||||
"file": {
|
||||
@@ -20070,6 +20096,17 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"StorageFolder": {
|
||||
"enum": [
|
||||
"encoded-video",
|
||||
"library",
|
||||
"upload",
|
||||
"profile",
|
||||
"thumbs",
|
||||
"backups"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"SyncAckDeleteDto": {
|
||||
"properties": {
|
||||
"types": {
|
||||
|
||||
@@ -50,9 +50,14 @@ export type MaintenanceListBackupsResponseDto = {
|
||||
export type MaintenanceUploadBackupDto = {
|
||||
file?: Blob;
|
||||
};
|
||||
export type MaintenanceStorageFolderIntegrityDto = {
|
||||
files: number;
|
||||
folder: StorageFolder;
|
||||
readable: boolean;
|
||||
writable: boolean;
|
||||
};
|
||||
export type MaintenanceIntegrityResponseDto = {
|
||||
storageHeuristics: object;
|
||||
storageIntegrity: object;
|
||||
storage: MaintenanceStorageFolderIntegrityDto[];
|
||||
};
|
||||
export type MaintenanceLoginDto = {
|
||||
token?: string;
|
||||
@@ -5151,6 +5156,14 @@ export enum MaintenanceAction {
|
||||
End = "end",
|
||||
RestoreDatabase = "restore_database"
|
||||
}
|
||||
export enum StorageFolder {
|
||||
EncodedVideo = "encoded-video",
|
||||
Library = "library",
|
||||
Upload = "upload",
|
||||
Profile = "profile",
|
||||
Thumbs = "thumbs",
|
||||
Backups = "backups"
|
||||
}
|
||||
export enum NotificationLevel {
|
||||
Success = "success",
|
||||
Error = "error",
|
||||
|
||||
@@ -28,20 +28,16 @@ export class MaintenanceStatusResponseDto {
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export class MaintenanceStorageFolderIntegrityDto {
|
||||
@ValidateEnum({ enum: StorageFolder, name: 'StorageFolder' })
|
||||
folder!: StorageFolder;
|
||||
readable!: boolean;
|
||||
writable!: boolean;
|
||||
files!: number;
|
||||
}
|
||||
|
||||
export class MaintenanceIntegrityResponseDto {
|
||||
storageIntegrity!: Record<
|
||||
StorageFolder,
|
||||
{
|
||||
readable: boolean;
|
||||
writable: boolean;
|
||||
}
|
||||
>;
|
||||
storageHeuristics!: Record<
|
||||
StorageFolder,
|
||||
{
|
||||
files: number;
|
||||
}
|
||||
>;
|
||||
storage!: MaintenanceStorageFolderIntegrityDto[];
|
||||
}
|
||||
|
||||
export class MaintenanceListBackupsResponseDto {
|
||||
|
||||
@@ -79,44 +79,36 @@ export function generateMaintenanceSecret(): string {
|
||||
|
||||
export async function integrityCheck(storageRepository: StorageRepository): Promise<MaintenanceIntegrityResponseDto> {
|
||||
return {
|
||||
storageIntegrity: Object.fromEntries(
|
||||
await Promise.all(
|
||||
Object.values(StorageFolder).map(async (folder) => {
|
||||
const path = join(StorageCore.getBaseFolder(folder), '.immich');
|
||||
|
||||
try {
|
||||
await storageRepository.readFile(path);
|
||||
|
||||
try {
|
||||
await storageRepository.overwriteFile(path, Buffer.from(`${Date.now()}`));
|
||||
return [folder, { readable: true, writable: true }];
|
||||
} catch {
|
||||
return [folder, { readable: true, writable: false }];
|
||||
}
|
||||
} catch {
|
||||
return [folder, { readable: false, writable: false }];
|
||||
}
|
||||
}),
|
||||
),
|
||||
),
|
||||
storageHeuristics: Object.fromEntries(
|
||||
await Promise.all(
|
||||
storage: await Promise.all(
|
||||
Object.values(StorageFolder).map(async (folder) => {
|
||||
const path = StorageCore.getBaseFolder(folder);
|
||||
const files = await storageRepository.readdir(path);
|
||||
const fn = join(StorageCore.getBaseFolder(folder), '.immich');
|
||||
|
||||
let readable = false,
|
||||
writable = false;
|
||||
|
||||
try {
|
||||
return [
|
||||
folder,
|
||||
{
|
||||
files: files.filter((fn) => fn !== '.immich').length,
|
||||
},
|
||||
];
|
||||
await storageRepository.readFile(fn);
|
||||
readable = true;
|
||||
|
||||
try {
|
||||
await storageRepository.overwriteFile(fn, Buffer.from(`${Date.now()}`));
|
||||
writable = true;
|
||||
} catch {
|
||||
return [folder, { files: 0 }];
|
||||
// no-op
|
||||
}
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
|
||||
return {
|
||||
folder,
|
||||
readable,
|
||||
writable,
|
||||
files: files.filter((fn) => fn !== '.immich').length,
|
||||
};
|
||||
}),
|
||||
),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<CardBody>
|
||||
<Stack>
|
||||
{#if integrity}
|
||||
{#each Object.entries(integrity.storageIntegrity) as [folder, { readable, writable }] (folder)}
|
||||
{#each integrity.storage as { folder, readable, writable } (folder)}
|
||||
<HStack>
|
||||
<Icon
|
||||
icon={writable ? mdiCheck : mdiClose}
|
||||
@@ -54,7 +54,7 @@
|
||||
>
|
||||
</HStack>
|
||||
{/each}
|
||||
{#each Object.entries(integrity.storageHeuristics) as [folder, { files }] (folder)}
|
||||
{#each integrity.storage as { folder, files } (folder)}
|
||||
{#if folder !== 'backups'}
|
||||
<HStack class="items-start">
|
||||
<Icon
|
||||
|
||||
Reference in New Issue
Block a user