forked from Cutlery/immich
		
	feat(mobile): use cached asset info if unchanged instead of downloading all assets (#1017)
* feat(mobile): use cached asset info if unchanged instead of downloading all assets This adds an HTTP ETag to the getAllAssets endpoint and client-side support in the app. If locally cache content is identical to the content on the server, the potentially large list of all assets does not need to be downloaded. * use ts import instead of require
This commit is contained in:
		
							parent
							
								
									efa7b3ba54
								
							
						
					
					
						commit
						47f5e4134e
					
				@ -4,6 +4,7 @@ const String accessTokenKey = "immichBoxAccessTokenKey"; // Key 1
 | 
				
			|||||||
const String deviceIdKey = 'immichBoxDeviceIdKey'; // Key 2
 | 
					const String deviceIdKey = 'immichBoxDeviceIdKey'; // Key 2
 | 
				
			||||||
const String isLoggedInKey = 'immichIsLoggedInKey'; // Key 3
 | 
					const String isLoggedInKey = 'immichIsLoggedInKey'; // Key 3
 | 
				
			||||||
const String serverEndpointKey = 'immichBoxServerEndpoint'; // Key 4
 | 
					const String serverEndpointKey = 'immichBoxServerEndpoint'; // Key 4
 | 
				
			||||||
 | 
					const String assetEtagKey = 'immichAssetEtagKey'; // Key 5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Login Info
 | 
					// Login Info
 | 
				
			||||||
const String hiveLoginInfoBox = "immichLoginInfoBox"; // Box
 | 
					const String hiveLoginInfoBox = "immichLoginInfoBox"; // Box
 | 
				
			||||||
 | 
				
			|||||||
@ -10,8 +10,9 @@ import 'package:immich_mobile/modules/backup/services/backup.service.dart';
 | 
				
			|||||||
import 'package:immich_mobile/shared/models/asset.dart';
 | 
					import 'package:immich_mobile/shared/models/asset.dart';
 | 
				
			||||||
import 'package:immich_mobile/shared/providers/api.provider.dart';
 | 
					import 'package:immich_mobile/shared/providers/api.provider.dart';
 | 
				
			||||||
import 'package:immich_mobile/shared/services/api.service.dart';
 | 
					import 'package:immich_mobile/shared/services/api.service.dart';
 | 
				
			||||||
 | 
					import 'package:immich_mobile/utils/openapi_extensions.dart';
 | 
				
			||||||
 | 
					import 'package:immich_mobile/utils/tuple.dart';
 | 
				
			||||||
import 'package:openapi/api.dart';
 | 
					import 'package:openapi/api.dart';
 | 
				
			||||||
import 'package:photo_manager/photo_manager.dart';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
final assetServiceProvider = Provider(
 | 
					final assetServiceProvider = Provider(
 | 
				
			||||||
  (ref) => AssetService(
 | 
					  (ref) => AssetService(
 | 
				
			||||||
@ -28,39 +29,22 @@ class AssetService {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  AssetService(this._apiService, this._backupService, this._backgroundService);
 | 
					  AssetService(this._apiService, this._backupService, this._backgroundService);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Returns all local, remote assets in that order
 | 
					  /// Returns `null` if the server state did not change, else list of assets
 | 
				
			||||||
  Future<List<Asset>> getAllAsset({bool urgent = false}) async {
 | 
					  Future<List<Asset>?> getRemoteAssets() async {
 | 
				
			||||||
    final List<Asset> assets = [];
 | 
					    final Box box = Hive.box(userInfoBox);
 | 
				
			||||||
    try {
 | 
					    final Pair<List<AssetResponseDto>, String?>? remote = await _apiService
 | 
				
			||||||
      // not using `await` here to fetch local & remote assets concurrently
 | 
					        .assetApi
 | 
				
			||||||
      final Future<List<AssetResponseDto>?> remoteTask =
 | 
					        .getAllAssetsWithETag(eTag: box.get(assetEtagKey));
 | 
				
			||||||
          _apiService.assetApi.getAllAssets();
 | 
					    if (remote == null) {
 | 
				
			||||||
      final Iterable<AssetEntity> newLocalAssets;
 | 
					      return null;
 | 
				
			||||||
      final List<AssetEntity> localAssets = await _getLocalAssets(urgent);
 | 
					 | 
				
			||||||
      final List<AssetResponseDto> remoteAssets = await remoteTask ?? [];
 | 
					 | 
				
			||||||
      if (remoteAssets.isNotEmpty && localAssets.isNotEmpty) {
 | 
					 | 
				
			||||||
        final String deviceId = Hive.box(userInfoBox).get(deviceIdKey);
 | 
					 | 
				
			||||||
        final Set<String> existingIds = remoteAssets
 | 
					 | 
				
			||||||
            .where((e) => e.deviceId == deviceId)
 | 
					 | 
				
			||||||
            .map((e) => e.deviceAssetId)
 | 
					 | 
				
			||||||
            .toSet();
 | 
					 | 
				
			||||||
        newLocalAssets = localAssets.where((e) => !existingIds.contains(e.id));
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        newLocalAssets = localAssets;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      assets.addAll(newLocalAssets.map((e) => Asset.local(e)));
 | 
					 | 
				
			||||||
      // the order (first all local, then remote assets) is important!
 | 
					 | 
				
			||||||
      assets.addAll(remoteAssets.map((e) => Asset.remote(e)));
 | 
					 | 
				
			||||||
    } catch (e) {
 | 
					 | 
				
			||||||
      debugPrint("Error [getAllAsset]  ${e.toString()}");
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return assets;
 | 
					    box.put(assetEtagKey, remote.second);
 | 
				
			||||||
 | 
					    return remote.first.map(Asset.remote).toList(growable: false);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// if [urgent] is `true`, do not block by waiting on the background service
 | 
					  /// if [urgent] is `true`, do not block by waiting on the background service
 | 
				
			||||||
  /// to finish running. Returns an empty list instead after a timeout.
 | 
					  /// to finish running. Returns `null` instead after a timeout.
 | 
				
			||||||
  Future<List<AssetEntity>> _getLocalAssets(bool urgent) async {
 | 
					  Future<List<Asset>?> getLocalAssets({bool urgent = false}) async {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      final Future<bool> hasAccess = urgent
 | 
					      final Future<bool> hasAccess = urgent
 | 
				
			||||||
          ? _backgroundService.hasAccess
 | 
					          ? _backgroundService.hasAccess
 | 
				
			||||||
@ -71,15 +55,16 @@ class AssetService {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      final box = await Hive.openBox<HiveBackupAlbums>(hiveBackupInfoBox);
 | 
					      final box = await Hive.openBox<HiveBackupAlbums>(hiveBackupInfoBox);
 | 
				
			||||||
      final HiveBackupAlbums? backupAlbumInfo = box.get(backupInfoKey);
 | 
					      final HiveBackupAlbums? backupAlbumInfo = box.get(backupInfoKey);
 | 
				
			||||||
 | 
					      if (backupAlbumInfo != null) {
 | 
				
			||||||
      return backupAlbumInfo != null
 | 
					        return (await _backupService
 | 
				
			||||||
          ? await _backupService
 | 
					                .buildUploadCandidates(backupAlbumInfo.deepCopy()))
 | 
				
			||||||
              .buildUploadCandidates(backupAlbumInfo.deepCopy())
 | 
					            .map(Asset.local)
 | 
				
			||||||
          : [];
 | 
					            .toList(growable: false);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      debugPrint("Error [_getLocalAssets] ${e.toString()}");
 | 
					      debugPrint("Error [_getLocalAssets] ${e.toString()}");
 | 
				
			||||||
      return [];
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<Asset?> getAssetById(String assetId) async {
 | 
					  Future<Asset?> getAssetById(String assetId) async {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,9 @@
 | 
				
			|||||||
import 'dart:collection';
 | 
					import 'dart:collection';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import 'package:flutter/foundation.dart';
 | 
					import 'package:flutter/foundation.dart';
 | 
				
			||||||
 | 
					import 'package:hive/hive.dart';
 | 
				
			||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
					import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
				
			||||||
 | 
					import 'package:immich_mobile/constants/hive_box.dart';
 | 
				
			||||||
import 'package:immich_mobile/modules/home/services/asset.service.dart';
 | 
					import 'package:immich_mobile/modules/home/services/asset.service.dart';
 | 
				
			||||||
import 'package:immich_mobile/modules/home/services/asset_cache.service.dart';
 | 
					import 'package:immich_mobile/modules/home/services/asset_cache.service.dart';
 | 
				
			||||||
import 'package:immich_mobile/shared/models/asset.dart';
 | 
					import 'package:immich_mobile/shared/models/asset.dart';
 | 
				
			||||||
@ -33,10 +35,11 @@ class AssetNotifier extends StateNotifier<List<Asset>> {
 | 
				
			|||||||
    final stopwatch = Stopwatch();
 | 
					    final stopwatch = Stopwatch();
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      _getAllAssetInProgress = true;
 | 
					      _getAllAssetInProgress = true;
 | 
				
			||||||
 | 
					 | 
				
			||||||
      final bool isCacheValid = await _assetCacheService.isValid();
 | 
					      final bool isCacheValid = await _assetCacheService.isValid();
 | 
				
			||||||
 | 
					      stopwatch.start();
 | 
				
			||||||
 | 
					      final localTask = _assetService.getLocalAssets(urgent: !isCacheValid);
 | 
				
			||||||
 | 
					      final remoteTask = _assetService.getRemoteAssets();
 | 
				
			||||||
      if (isCacheValid && state.isEmpty) {
 | 
					      if (isCacheValid && state.isEmpty) {
 | 
				
			||||||
        stopwatch.start();
 | 
					 | 
				
			||||||
        state = await _assetCacheService.get();
 | 
					        state = await _assetCacheService.get();
 | 
				
			||||||
        debugPrint(
 | 
					        debugPrint(
 | 
				
			||||||
          "Reading assets from cache: ${stopwatch.elapsedMilliseconds}ms",
 | 
					          "Reading assets from cache: ${stopwatch.elapsedMilliseconds}ms",
 | 
				
			||||||
@ -44,21 +47,49 @@ class AssetNotifier extends StateNotifier<List<Asset>> {
 | 
				
			|||||||
        stopwatch.reset();
 | 
					        stopwatch.reset();
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      stopwatch.start();
 | 
					      int remoteBegin = state.indexWhere((a) => a.isRemote);
 | 
				
			||||||
      var allAssets = await _assetService.getAllAsset(urgent: !isCacheValid);
 | 
					      remoteBegin = remoteBegin == -1 ? state.length : remoteBegin;
 | 
				
			||||||
      debugPrint("Query assets from API: ${stopwatch.elapsedMilliseconds}ms");
 | 
					      final List<Asset> currentLocal = state.slice(0, remoteBegin);
 | 
				
			||||||
 | 
					      List<Asset>? newRemote = await remoteTask;
 | 
				
			||||||
 | 
					      List<Asset>? newLocal = await localTask;
 | 
				
			||||||
 | 
					      debugPrint("Load assets: ${stopwatch.elapsedMilliseconds}ms");
 | 
				
			||||||
      stopwatch.reset();
 | 
					      stopwatch.reset();
 | 
				
			||||||
 | 
					      if (newRemote == null &&
 | 
				
			||||||
      state = allAssets;
 | 
					          (newLocal == null || currentLocal.equals(newLocal))) {
 | 
				
			||||||
 | 
					        debugPrint("state is already up-to-date");
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      newRemote ??= state.slice(remoteBegin);
 | 
				
			||||||
 | 
					      newLocal ??= [];
 | 
				
			||||||
 | 
					      state = _combineLocalAndRemoteAssets(local: newLocal, remote: newRemote);
 | 
				
			||||||
 | 
					      debugPrint("Combining assets: ${stopwatch.elapsedMilliseconds}ms");
 | 
				
			||||||
    } finally {
 | 
					    } finally {
 | 
				
			||||||
      _getAllAssetInProgress = false;
 | 
					      _getAllAssetInProgress = false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    debugPrint("[getAllAsset] setting new asset state");
 | 
					    debugPrint("[getAllAsset] setting new asset state");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    stopwatch.start();
 | 
					    stopwatch.reset();
 | 
				
			||||||
    _cacheState();
 | 
					    _cacheState();
 | 
				
			||||||
    debugPrint("Store assets in cache: ${stopwatch.elapsedMilliseconds}ms");
 | 
					    debugPrint("Store assets in cache: ${stopwatch.elapsedMilliseconds}ms");
 | 
				
			||||||
    stopwatch.reset();
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  List<Asset> _combineLocalAndRemoteAssets({
 | 
				
			||||||
 | 
					    required Iterable<Asset> local,
 | 
				
			||||||
 | 
					    required List<Asset> remote,
 | 
				
			||||||
 | 
					  }) {
 | 
				
			||||||
 | 
					    final List<Asset> assets = [];
 | 
				
			||||||
 | 
					    if (remote.isNotEmpty && local.isNotEmpty) {
 | 
				
			||||||
 | 
					      final String deviceId = Hive.box(userInfoBox).get(deviceIdKey);
 | 
				
			||||||
 | 
					      final Set<String> existingIds = remote
 | 
				
			||||||
 | 
					          .where((e) => e.deviceId == deviceId)
 | 
				
			||||||
 | 
					          .map((e) => e.deviceAssetId)
 | 
				
			||||||
 | 
					          .toSet();
 | 
				
			||||||
 | 
					      local = local.where((e) => !existingIds.contains(e.id));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    assets.addAll(local);
 | 
				
			||||||
 | 
					    // the order (first all local, then remote assets) is important!
 | 
				
			||||||
 | 
					    assets.addAll(remote);
 | 
				
			||||||
 | 
					    return assets;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  clearAllAsset() {
 | 
					  clearAllAsset() {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										53
									
								
								mobile/lib/utils/openapi_extensions.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								mobile/lib/utils/openapi_extensions.dart
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					import 'dart:convert';
 | 
				
			||||||
 | 
					import 'dart:io';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'package:http/http.dart';
 | 
				
			||||||
 | 
					import 'package:openapi/api.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'tuple.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Extension methods to retrieve ETag together with the API call
 | 
				
			||||||
 | 
					extension WithETag on AssetApi {
 | 
				
			||||||
 | 
					  /// Get all AssetEntity belong to the user
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// Parameters:
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// * [String] eTag:
 | 
				
			||||||
 | 
					  ///   ETag of data already cached on the client
 | 
				
			||||||
 | 
					  Future<Pair<List<AssetResponseDto>, String?>?> getAllAssetsWithETag({
 | 
				
			||||||
 | 
					    String? eTag,
 | 
				
			||||||
 | 
					  }) async {
 | 
				
			||||||
 | 
					    final response = await getAllAssetsWithHttpInfo(
 | 
				
			||||||
 | 
					      ifNoneMatch: eTag,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    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) {
 | 
				
			||||||
 | 
					      final responseBody = await _decodeBodyBytes(response);
 | 
				
			||||||
 | 
					      final etag = response.headers[HttpHeaders.etagHeader];
 | 
				
			||||||
 | 
					      final data = (await apiClient.deserializeAsync(
 | 
				
			||||||
 | 
					              responseBody, 'List<AssetResponseDto>') as List)
 | 
				
			||||||
 | 
					          .cast<AssetResponseDto>()
 | 
				
			||||||
 | 
					          .toList();
 | 
				
			||||||
 | 
					      return Pair(data, etag);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Returns the decoded body as UTF-8 if the given headers indicate an 'application/json'
 | 
				
			||||||
 | 
					/// content type. Otherwise, returns the decoded body as decoded by dart:http package.
 | 
				
			||||||
 | 
					Future<String> _decodeBodyBytes(Response response) async {
 | 
				
			||||||
 | 
					  final contentType = response.headers['content-type'];
 | 
				
			||||||
 | 
					  return contentType != null &&
 | 
				
			||||||
 | 
					          contentType.toLowerCase().startsWith('application/json')
 | 
				
			||||||
 | 
					      ? response.bodyBytes.isEmpty
 | 
				
			||||||
 | 
					          ? ''
 | 
				
			||||||
 | 
					          : utf8.decode(response.bodyBytes)
 | 
				
			||||||
 | 
					      : response.body;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										8
									
								
								mobile/lib/utils/tuple.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								mobile/lib/utils/tuple.dart
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					/// An immutable pair or 2-tuple
 | 
				
			||||||
 | 
					/// TODO replace with Record once Dart 2.19 is available
 | 
				
			||||||
 | 
					class Pair<T1, T2> {
 | 
				
			||||||
 | 
					  final T1 first;
 | 
				
			||||||
 | 
					  final T2 second;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const Pair(this.first, this.second);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -274,7 +274,7 @@ 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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# **getAllAssets**
 | 
					# **getAllAssets**
 | 
				
			||||||
> List<AssetResponseDto> getAllAssets()
 | 
					> List<AssetResponseDto> getAllAssets(ifNoneMatch)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -291,9 +291,10 @@ import 'package:openapi/api.dart';
 | 
				
			|||||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
 | 
					//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
final api_instance = AssetApi();
 | 
					final api_instance = AssetApi();
 | 
				
			||||||
 | 
					final ifNoneMatch = ifNoneMatch_example; // String | ETag of data already cached on the client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
try {
 | 
					try {
 | 
				
			||||||
    final result = api_instance.getAllAssets();
 | 
					    final result = api_instance.getAllAssets(ifNoneMatch);
 | 
				
			||||||
    print(result);
 | 
					    print(result);
 | 
				
			||||||
} catch (e) {
 | 
					} catch (e) {
 | 
				
			||||||
    print('Exception when calling AssetApi->getAllAssets: $e\n');
 | 
					    print('Exception when calling AssetApi->getAllAssets: $e\n');
 | 
				
			||||||
@ -301,7 +302,10 @@ try {
 | 
				
			|||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Parameters
 | 
					### Parameters
 | 
				
			||||||
This endpoint does not need any parameter.
 | 
					
 | 
				
			||||||
 | 
					Name | Type | Description  | Notes
 | 
				
			||||||
 | 
					------------- | ------------- | ------------- | -------------
 | 
				
			||||||
 | 
					 **ifNoneMatch** | **String**| ETag of data already cached on the client | [optional] 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Return type
 | 
					### Return type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -21,10 +21,10 @@ Name | Type | Description | Notes
 | 
				
			|||||||
**mimeType** | **String** |  | 
 | 
					**mimeType** | **String** |  | 
 | 
				
			||||||
**duration** | **String** |  | 
 | 
					**duration** | **String** |  | 
 | 
				
			||||||
**webpPath** | **String** |  | 
 | 
					**webpPath** | **String** |  | 
 | 
				
			||||||
**encodedVideoPath** | **String** |  | 
 | 
					**encodedVideoPath** | **String** |  | [optional] 
 | 
				
			||||||
**exifInfo** | [**ExifResponseDto**](ExifResponseDto.md) |  | [optional] 
 | 
					**exifInfo** | [**ExifResponseDto**](ExifResponseDto.md) |  | [optional] 
 | 
				
			||||||
**smartInfo** | [**SmartInfoResponseDto**](SmartInfoResponseDto.md) |  | [optional] 
 | 
					**smartInfo** | [**SmartInfoResponseDto**](SmartInfoResponseDto.md) |  | [optional] 
 | 
				
			||||||
**livePhotoVideoId** | **String** |  | 
 | 
					**livePhotoVideoId** | **String** |  | [optional] 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
 | 
					[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,7 @@ Name | Type | Description | Notes
 | 
				
			|||||||
**profileImagePath** | **String** |  | 
 | 
					**profileImagePath** | **String** |  | 
 | 
				
			||||||
**shouldChangePassword** | **bool** |  | 
 | 
					**shouldChangePassword** | **bool** |  | 
 | 
				
			||||||
**isAdmin** | **bool** |  | 
 | 
					**isAdmin** | **bool** |  | 
 | 
				
			||||||
**deletedAt** | [**DateTime**](DateTime.md) |  | 
 | 
					**deletedAt** | [**DateTime**](DateTime.md) |  | [optional] 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
 | 
					[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -297,7 +297,12 @@ class AssetApi {
 | 
				
			|||||||
  /// Get all AssetEntity belong to the user
 | 
					  /// Get all AssetEntity belong to the user
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// Note: This method returns the HTTP [Response].
 | 
					  /// Note: This method returns the HTTP [Response].
 | 
				
			||||||
  Future<Response> getAllAssetsWithHttpInfo() async {
 | 
					  ///
 | 
				
			||||||
 | 
					  /// Parameters:
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// * [String] ifNoneMatch:
 | 
				
			||||||
 | 
					  ///   ETag of data already cached on the client
 | 
				
			||||||
 | 
					  Future<Response> getAllAssetsWithHttpInfo({ String? ifNoneMatch, }) async {
 | 
				
			||||||
    // ignore: prefer_const_declarations
 | 
					    // ignore: prefer_const_declarations
 | 
				
			||||||
    final path = r'/asset';
 | 
					    final path = r'/asset';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -308,6 +313,10 @@ class AssetApi {
 | 
				
			|||||||
    final headerParams = <String, String>{};
 | 
					    final headerParams = <String, String>{};
 | 
				
			||||||
    final formParams = <String, String>{};
 | 
					    final formParams = <String, String>{};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (ifNoneMatch != null) {
 | 
				
			||||||
 | 
					      headerParams[r'if-none-match'] = parameterToString(ifNoneMatch);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const contentTypes = <String>[];
 | 
					    const contentTypes = <String>[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -325,8 +334,13 @@ class AssetApi {
 | 
				
			|||||||
  /// 
 | 
					  /// 
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// Get all AssetEntity belong to the user
 | 
					  /// Get all AssetEntity belong to the user
 | 
				
			||||||
  Future<List<AssetResponseDto>?> getAllAssets() async {
 | 
					  ///
 | 
				
			||||||
    final response = await getAllAssetsWithHttpInfo();
 | 
					  /// Parameters:
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// * [String] ifNoneMatch:
 | 
				
			||||||
 | 
					  ///   ETag of data already cached on the client
 | 
				
			||||||
 | 
					  Future<List<AssetResponseDto>?> getAllAssets({ String? ifNoneMatch, }) async {
 | 
				
			||||||
 | 
					    final response = await getAllAssetsWithHttpInfo( ifNoneMatch: ifNoneMatch, );
 | 
				
			||||||
    if (response.statusCode >= HttpStatus.badRequest) {
 | 
					    if (response.statusCode >= HttpStatus.badRequest) {
 | 
				
			||||||
      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
					      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -26,10 +26,10 @@ class AssetResponseDto {
 | 
				
			|||||||
    required this.mimeType,
 | 
					    required this.mimeType,
 | 
				
			||||||
    required this.duration,
 | 
					    required this.duration,
 | 
				
			||||||
    required this.webpPath,
 | 
					    required this.webpPath,
 | 
				
			||||||
    required this.encodedVideoPath,
 | 
					    this.encodedVideoPath,
 | 
				
			||||||
    this.exifInfo,
 | 
					    this.exifInfo,
 | 
				
			||||||
    this.smartInfo,
 | 
					    this.smartInfo,
 | 
				
			||||||
    required this.livePhotoVideoId,
 | 
					    this.livePhotoVideoId,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  AssetTypeEnum type;
 | 
					  AssetTypeEnum type;
 | 
				
			||||||
@ -79,74 +79,71 @@ class AssetResponseDto {
 | 
				
			|||||||
  String? livePhotoVideoId;
 | 
					  String? livePhotoVideoId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  bool operator ==(Object other) =>
 | 
					  bool operator ==(Object other) => identical(this, other) || other is AssetResponseDto &&
 | 
				
			||||||
      identical(this, other) ||
 | 
					     other.type == type &&
 | 
				
			||||||
      other is AssetResponseDto &&
 | 
					     other.id == id &&
 | 
				
			||||||
          other.type == type &&
 | 
					     other.deviceAssetId == deviceAssetId &&
 | 
				
			||||||
          other.id == id &&
 | 
					     other.ownerId == ownerId &&
 | 
				
			||||||
          other.deviceAssetId == deviceAssetId &&
 | 
					     other.deviceId == deviceId &&
 | 
				
			||||||
          other.ownerId == ownerId &&
 | 
					     other.originalPath == originalPath &&
 | 
				
			||||||
          other.deviceId == deviceId &&
 | 
					     other.resizePath == resizePath &&
 | 
				
			||||||
          other.originalPath == originalPath &&
 | 
					     other.createdAt == createdAt &&
 | 
				
			||||||
          other.resizePath == resizePath &&
 | 
					     other.modifiedAt == modifiedAt &&
 | 
				
			||||||
          other.createdAt == createdAt &&
 | 
					     other.isFavorite == isFavorite &&
 | 
				
			||||||
          other.modifiedAt == modifiedAt &&
 | 
					     other.mimeType == mimeType &&
 | 
				
			||||||
          other.isFavorite == isFavorite &&
 | 
					     other.duration == duration &&
 | 
				
			||||||
          other.mimeType == mimeType &&
 | 
					     other.webpPath == webpPath &&
 | 
				
			||||||
          other.duration == duration &&
 | 
					     other.encodedVideoPath == encodedVideoPath &&
 | 
				
			||||||
          other.webpPath == webpPath &&
 | 
					     other.exifInfo == exifInfo &&
 | 
				
			||||||
          other.encodedVideoPath == encodedVideoPath &&
 | 
					     other.smartInfo == smartInfo &&
 | 
				
			||||||
          other.exifInfo == exifInfo &&
 | 
					     other.livePhotoVideoId == livePhotoVideoId;
 | 
				
			||||||
          other.smartInfo == smartInfo &&
 | 
					 | 
				
			||||||
          other.livePhotoVideoId == livePhotoVideoId;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  int get hashCode =>
 | 
					  int get hashCode =>
 | 
				
			||||||
      // ignore: unnecessary_parenthesis
 | 
					    // ignore: unnecessary_parenthesis
 | 
				
			||||||
      (type.hashCode) +
 | 
					    (type.hashCode) +
 | 
				
			||||||
      (id.hashCode) +
 | 
					    (id.hashCode) +
 | 
				
			||||||
      (deviceAssetId.hashCode) +
 | 
					    (deviceAssetId.hashCode) +
 | 
				
			||||||
      (ownerId.hashCode) +
 | 
					    (ownerId.hashCode) +
 | 
				
			||||||
      (deviceId.hashCode) +
 | 
					    (deviceId.hashCode) +
 | 
				
			||||||
      (originalPath.hashCode) +
 | 
					    (originalPath.hashCode) +
 | 
				
			||||||
      (resizePath == null ? 0 : resizePath!.hashCode) +
 | 
					    (resizePath == null ? 0 : resizePath!.hashCode) +
 | 
				
			||||||
      (createdAt.hashCode) +
 | 
					    (createdAt.hashCode) +
 | 
				
			||||||
      (modifiedAt.hashCode) +
 | 
					    (modifiedAt.hashCode) +
 | 
				
			||||||
      (isFavorite.hashCode) +
 | 
					    (isFavorite.hashCode) +
 | 
				
			||||||
      (mimeType == null ? 0 : mimeType!.hashCode) +
 | 
					    (mimeType == null ? 0 : mimeType!.hashCode) +
 | 
				
			||||||
      (duration.hashCode) +
 | 
					    (duration.hashCode) +
 | 
				
			||||||
      (webpPath == null ? 0 : webpPath!.hashCode) +
 | 
					    (webpPath == null ? 0 : webpPath!.hashCode) +
 | 
				
			||||||
      (encodedVideoPath == null ? 0 : encodedVideoPath!.hashCode) +
 | 
					    (encodedVideoPath == null ? 0 : encodedVideoPath!.hashCode) +
 | 
				
			||||||
      (exifInfo == null ? 0 : exifInfo!.hashCode) +
 | 
					    (exifInfo == null ? 0 : exifInfo!.hashCode) +
 | 
				
			||||||
      (smartInfo == null ? 0 : smartInfo!.hashCode) +
 | 
					    (smartInfo == null ? 0 : smartInfo!.hashCode) +
 | 
				
			||||||
      (livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode);
 | 
					    (livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  String toString() =>
 | 
					  String toString() => 'AssetResponseDto[type=$type, id=$id, deviceAssetId=$deviceAssetId, ownerId=$ownerId, deviceId=$deviceId, originalPath=$originalPath, resizePath=$resizePath, createdAt=$createdAt, modifiedAt=$modifiedAt, isFavorite=$isFavorite, mimeType=$mimeType, duration=$duration, webpPath=$webpPath, encodedVideoPath=$encodedVideoPath, exifInfo=$exifInfo, smartInfo=$smartInfo, livePhotoVideoId=$livePhotoVideoId]';
 | 
				
			||||||
      'AssetResponseDto[type=$type, id=$id, deviceAssetId=$deviceAssetId, ownerId=$ownerId, deviceId=$deviceId, originalPath=$originalPath, resizePath=$resizePath, createdAt=$createdAt, modifiedAt=$modifiedAt, isFavorite=$isFavorite, mimeType=$mimeType, duration=$duration, webpPath=$webpPath, encodedVideoPath=$encodedVideoPath, exifInfo=$exifInfo, smartInfo=$smartInfo, livePhotoVideoId=$livePhotoVideoId]';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Map<String, dynamic> toJson() {
 | 
					  Map<String, dynamic> toJson() {
 | 
				
			||||||
    final _json = <String, dynamic>{};
 | 
					    final _json = <String, dynamic>{};
 | 
				
			||||||
    _json[r'type'] = type;
 | 
					      _json[r'type'] = type;
 | 
				
			||||||
    _json[r'id'] = id;
 | 
					      _json[r'id'] = id;
 | 
				
			||||||
    _json[r'deviceAssetId'] = deviceAssetId;
 | 
					      _json[r'deviceAssetId'] = deviceAssetId;
 | 
				
			||||||
    _json[r'ownerId'] = ownerId;
 | 
					      _json[r'ownerId'] = ownerId;
 | 
				
			||||||
    _json[r'deviceId'] = deviceId;
 | 
					      _json[r'deviceId'] = deviceId;
 | 
				
			||||||
    _json[r'originalPath'] = originalPath;
 | 
					      _json[r'originalPath'] = originalPath;
 | 
				
			||||||
    if (resizePath != null) {
 | 
					    if (resizePath != null) {
 | 
				
			||||||
      _json[r'resizePath'] = resizePath;
 | 
					      _json[r'resizePath'] = resizePath;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      _json[r'resizePath'] = null;
 | 
					      _json[r'resizePath'] = null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    _json[r'createdAt'] = createdAt;
 | 
					      _json[r'createdAt'] = createdAt;
 | 
				
			||||||
    _json[r'modifiedAt'] = modifiedAt;
 | 
					      _json[r'modifiedAt'] = modifiedAt;
 | 
				
			||||||
    _json[r'isFavorite'] = isFavorite;
 | 
					      _json[r'isFavorite'] = isFavorite;
 | 
				
			||||||
    if (mimeType != null) {
 | 
					    if (mimeType != null) {
 | 
				
			||||||
      _json[r'mimeType'] = mimeType;
 | 
					      _json[r'mimeType'] = mimeType;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      _json[r'mimeType'] = null;
 | 
					      _json[r'mimeType'] = null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    _json[r'duration'] = duration;
 | 
					      _json[r'duration'] = duration;
 | 
				
			||||||
    if (webpPath != null) {
 | 
					    if (webpPath != null) {
 | 
				
			||||||
      _json[r'webpPath'] = webpPath;
 | 
					      _json[r'webpPath'] = webpPath;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
@ -185,13 +182,13 @@ class AssetResponseDto {
 | 
				
			|||||||
      // Ensure that the map contains the required keys.
 | 
					      // Ensure that the map contains the required keys.
 | 
				
			||||||
      // Note 1: the values aren't checked for validity beyond being non-null.
 | 
					      // Note 1: the values aren't checked for validity beyond being non-null.
 | 
				
			||||||
      // Note 2: this code is stripped in release mode!
 | 
					      // Note 2: this code is stripped in release mode!
 | 
				
			||||||
      // assert(() {
 | 
					      assert(() {
 | 
				
			||||||
      //   requiredKeys.forEach((key) {
 | 
					        requiredKeys.forEach((key) {
 | 
				
			||||||
      //     assert(json.containsKey(key), 'Required key "AssetResponseDto[$key]" is missing from JSON.');
 | 
					          assert(json.containsKey(key), 'Required key "AssetResponseDto[$key]" is missing from JSON.');
 | 
				
			||||||
      //     assert(json[key] != null, 'Required key "AssetResponseDto[$key]" has a null value in JSON.');
 | 
					          assert(json[key] != null, 'Required key "AssetResponseDto[$key]" has a null value in JSON.');
 | 
				
			||||||
      //   });
 | 
					        });
 | 
				
			||||||
      //   return true;
 | 
					        return true;
 | 
				
			||||||
      // }());
 | 
					      }());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return AssetResponseDto(
 | 
					      return AssetResponseDto(
 | 
				
			||||||
        type: AssetTypeEnum.fromJson(json[r'type'])!,
 | 
					        type: AssetTypeEnum.fromJson(json[r'type'])!,
 | 
				
			||||||
@ -216,10 +213,7 @@ class AssetResponseDto {
 | 
				
			|||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static List<AssetResponseDto>? listFromJson(
 | 
					  static List<AssetResponseDto>? listFromJson(dynamic json, {bool growable = false,}) {
 | 
				
			||||||
    dynamic json, {
 | 
					 | 
				
			||||||
    bool growable = false,
 | 
					 | 
				
			||||||
  }) {
 | 
					 | 
				
			||||||
    final result = <AssetResponseDto>[];
 | 
					    final result = <AssetResponseDto>[];
 | 
				
			||||||
    if (json is List && json.isNotEmpty) {
 | 
					    if (json is List && json.isNotEmpty) {
 | 
				
			||||||
      for (final row in json) {
 | 
					      for (final row in json) {
 | 
				
			||||||
@ -247,18 +241,12 @@ class AssetResponseDto {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // maps a json object with a list of AssetResponseDto-objects as value to a dart map
 | 
					  // maps a json object with a list of AssetResponseDto-objects as value to a dart map
 | 
				
			||||||
  static Map<String, List<AssetResponseDto>> mapListFromJson(
 | 
					  static Map<String, List<AssetResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
 | 
				
			||||||
    dynamic json, {
 | 
					 | 
				
			||||||
    bool growable = false,
 | 
					 | 
				
			||||||
  }) {
 | 
					 | 
				
			||||||
    final map = <String, List<AssetResponseDto>>{};
 | 
					    final map = <String, List<AssetResponseDto>>{};
 | 
				
			||||||
    if (json is Map && json.isNotEmpty) {
 | 
					    if (json is Map && json.isNotEmpty) {
 | 
				
			||||||
      json = json.cast<String, dynamic>(); // ignore: parameter_assignments
 | 
					      json = json.cast<String, dynamic>(); // ignore: parameter_assignments
 | 
				
			||||||
      for (final entry in json.entries) {
 | 
					      for (final entry in json.entries) {
 | 
				
			||||||
        final value = AssetResponseDto.listFromJson(
 | 
					        final value = AssetResponseDto.listFromJson(entry.value, growable: growable,);
 | 
				
			||||||
          entry.value,
 | 
					 | 
				
			||||||
          growable: growable,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        if (value != null) {
 | 
					        if (value != null) {
 | 
				
			||||||
          map[entry.key] = value;
 | 
					          map[entry.key] = value;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -282,7 +270,6 @@ class AssetResponseDto {
 | 
				
			|||||||
    'mimeType',
 | 
					    'mimeType',
 | 
				
			||||||
    'duration',
 | 
					    'duration',
 | 
				
			||||||
    'webpPath',
 | 
					    'webpPath',
 | 
				
			||||||
    'encodedVideoPath',
 | 
					 | 
				
			||||||
    'livePhotoVideoId',
 | 
					 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -21,7 +21,7 @@ class UserResponseDto {
 | 
				
			|||||||
    required this.profileImagePath,
 | 
					    required this.profileImagePath,
 | 
				
			||||||
    required this.shouldChangePassword,
 | 
					    required this.shouldChangePassword,
 | 
				
			||||||
    required this.isAdmin,
 | 
					    required this.isAdmin,
 | 
				
			||||||
    required this.deletedAt,
 | 
					    this.deletedAt,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  String id;
 | 
					  String id;
 | 
				
			||||||
@ -40,49 +40,52 @@ class UserResponseDto {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  bool isAdmin;
 | 
					  bool isAdmin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// 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? deletedAt;
 | 
					  DateTime? deletedAt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  bool operator ==(Object other) =>
 | 
					  bool operator ==(Object other) => identical(this, other) || other is UserResponseDto &&
 | 
				
			||||||
      identical(this, other) ||
 | 
					     other.id == id &&
 | 
				
			||||||
      other is UserResponseDto &&
 | 
					     other.email == email &&
 | 
				
			||||||
          other.id == id &&
 | 
					     other.firstName == firstName &&
 | 
				
			||||||
          other.email == email &&
 | 
					     other.lastName == lastName &&
 | 
				
			||||||
          other.firstName == firstName &&
 | 
					     other.createdAt == createdAt &&
 | 
				
			||||||
          other.lastName == lastName &&
 | 
					     other.profileImagePath == profileImagePath &&
 | 
				
			||||||
          other.createdAt == createdAt &&
 | 
					     other.shouldChangePassword == shouldChangePassword &&
 | 
				
			||||||
          other.profileImagePath == profileImagePath &&
 | 
					     other.isAdmin == isAdmin &&
 | 
				
			||||||
          other.shouldChangePassword == shouldChangePassword &&
 | 
					     other.deletedAt == deletedAt;
 | 
				
			||||||
          other.isAdmin == isAdmin &&
 | 
					 | 
				
			||||||
          other.deletedAt == deletedAt;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  int get hashCode =>
 | 
					  int get hashCode =>
 | 
				
			||||||
      // ignore: unnecessary_parenthesis
 | 
					    // ignore: unnecessary_parenthesis
 | 
				
			||||||
      (id.hashCode) +
 | 
					    (id.hashCode) +
 | 
				
			||||||
      (email.hashCode) +
 | 
					    (email.hashCode) +
 | 
				
			||||||
      (firstName.hashCode) +
 | 
					    (firstName.hashCode) +
 | 
				
			||||||
      (lastName.hashCode) +
 | 
					    (lastName.hashCode) +
 | 
				
			||||||
      (createdAt.hashCode) +
 | 
					    (createdAt.hashCode) +
 | 
				
			||||||
      (profileImagePath.hashCode) +
 | 
					    (profileImagePath.hashCode) +
 | 
				
			||||||
      (shouldChangePassword.hashCode) +
 | 
					    (shouldChangePassword.hashCode) +
 | 
				
			||||||
      (isAdmin.hashCode) +
 | 
					    (isAdmin.hashCode) +
 | 
				
			||||||
      (deletedAt == null ? 0 : deletedAt!.hashCode);
 | 
					    (deletedAt == null ? 0 : deletedAt!.hashCode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  String toString() =>
 | 
					  String toString() => 'UserResponseDto[id=$id, email=$email, firstName=$firstName, lastName=$lastName, createdAt=$createdAt, profileImagePath=$profileImagePath, shouldChangePassword=$shouldChangePassword, isAdmin=$isAdmin, deletedAt=$deletedAt]';
 | 
				
			||||||
      'UserResponseDto[id=$id, email=$email, firstName=$firstName, lastName=$lastName, createdAt=$createdAt, profileImagePath=$profileImagePath, shouldChangePassword=$shouldChangePassword, isAdmin=$isAdmin, deletedAt=$deletedAt]';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Map<String, dynamic> toJson() {
 | 
					  Map<String, dynamic> toJson() {
 | 
				
			||||||
    final _json = <String, dynamic>{};
 | 
					    final _json = <String, dynamic>{};
 | 
				
			||||||
    _json[r'id'] = id;
 | 
					      _json[r'id'] = id;
 | 
				
			||||||
    _json[r'email'] = email;
 | 
					      _json[r'email'] = email;
 | 
				
			||||||
    _json[r'firstName'] = firstName;
 | 
					      _json[r'firstName'] = firstName;
 | 
				
			||||||
    _json[r'lastName'] = lastName;
 | 
					      _json[r'lastName'] = lastName;
 | 
				
			||||||
    _json[r'createdAt'] = createdAt;
 | 
					      _json[r'createdAt'] = createdAt;
 | 
				
			||||||
    _json[r'profileImagePath'] = profileImagePath;
 | 
					      _json[r'profileImagePath'] = profileImagePath;
 | 
				
			||||||
    _json[r'shouldChangePassword'] = shouldChangePassword;
 | 
					      _json[r'shouldChangePassword'] = shouldChangePassword;
 | 
				
			||||||
    _json[r'isAdmin'] = isAdmin;
 | 
					      _json[r'isAdmin'] = isAdmin;
 | 
				
			||||||
    if (deletedAt != null) {
 | 
					    if (deletedAt != null) {
 | 
				
			||||||
      _json[r'deletedAt'] = deletedAt!.toUtc().toIso8601String();
 | 
					      _json[r'deletedAt'] = deletedAt!.toUtc().toIso8601String();
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
@ -101,13 +104,13 @@ class UserResponseDto {
 | 
				
			|||||||
      // Ensure that the map contains the required keys.
 | 
					      // Ensure that the map contains the required keys.
 | 
				
			||||||
      // Note 1: the values aren't checked for validity beyond being non-null.
 | 
					      // Note 1: the values aren't checked for validity beyond being non-null.
 | 
				
			||||||
      // Note 2: this code is stripped in release mode!
 | 
					      // Note 2: this code is stripped in release mode!
 | 
				
			||||||
      // assert(() {
 | 
					      assert(() {
 | 
				
			||||||
      //   requiredKeys.forEach((key) {
 | 
					        requiredKeys.forEach((key) {
 | 
				
			||||||
      //     assert(json.containsKey(key), 'Required key "UserResponseDto[$key]" is missing from JSON.');
 | 
					          assert(json.containsKey(key), 'Required key "UserResponseDto[$key]" is missing from JSON.');
 | 
				
			||||||
      //     assert(json[key] != null, 'Required key "UserResponseDto[$key]" has a null value in JSON.');
 | 
					          assert(json[key] != null, 'Required key "UserResponseDto[$key]" has a null value in JSON.');
 | 
				
			||||||
      //   });
 | 
					        });
 | 
				
			||||||
      //   return true;
 | 
					        return true;
 | 
				
			||||||
      // }());
 | 
					      }());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return UserResponseDto(
 | 
					      return UserResponseDto(
 | 
				
			||||||
        id: mapValueOfType<String>(json, r'id')!,
 | 
					        id: mapValueOfType<String>(json, r'id')!,
 | 
				
			||||||
@ -116,8 +119,7 @@ class UserResponseDto {
 | 
				
			|||||||
        lastName: mapValueOfType<String>(json, r'lastName')!,
 | 
					        lastName: mapValueOfType<String>(json, r'lastName')!,
 | 
				
			||||||
        createdAt: mapValueOfType<String>(json, r'createdAt')!,
 | 
					        createdAt: mapValueOfType<String>(json, r'createdAt')!,
 | 
				
			||||||
        profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
 | 
					        profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
 | 
				
			||||||
        shouldChangePassword:
 | 
					        shouldChangePassword: mapValueOfType<bool>(json, r'shouldChangePassword')!,
 | 
				
			||||||
            mapValueOfType<bool>(json, r'shouldChangePassword')!,
 | 
					 | 
				
			||||||
        isAdmin: mapValueOfType<bool>(json, r'isAdmin')!,
 | 
					        isAdmin: mapValueOfType<bool>(json, r'isAdmin')!,
 | 
				
			||||||
        deletedAt: mapDateTime(json, r'deletedAt', ''),
 | 
					        deletedAt: mapDateTime(json, r'deletedAt', ''),
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
@ -125,10 +127,7 @@ class UserResponseDto {
 | 
				
			|||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static List<UserResponseDto>? listFromJson(
 | 
					  static List<UserResponseDto>? listFromJson(dynamic json, {bool growable = false,}) {
 | 
				
			||||||
    dynamic json, {
 | 
					 | 
				
			||||||
    bool growable = false,
 | 
					 | 
				
			||||||
  }) {
 | 
					 | 
				
			||||||
    final result = <UserResponseDto>[];
 | 
					    final result = <UserResponseDto>[];
 | 
				
			||||||
    if (json is List && json.isNotEmpty) {
 | 
					    if (json is List && json.isNotEmpty) {
 | 
				
			||||||
      for (final row in json) {
 | 
					      for (final row in json) {
 | 
				
			||||||
@ -156,18 +155,12 @@ class UserResponseDto {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // maps a json object with a list of UserResponseDto-objects as value to a dart map
 | 
					  // maps a json object with a list of UserResponseDto-objects as value to a dart map
 | 
				
			||||||
  static Map<String, List<UserResponseDto>> mapListFromJson(
 | 
					  static Map<String, List<UserResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
 | 
				
			||||||
    dynamic json, {
 | 
					 | 
				
			||||||
    bool growable = false,
 | 
					 | 
				
			||||||
  }) {
 | 
					 | 
				
			||||||
    final map = <String, List<UserResponseDto>>{};
 | 
					    final map = <String, List<UserResponseDto>>{};
 | 
				
			||||||
    if (json is Map && json.isNotEmpty) {
 | 
					    if (json is Map && json.isNotEmpty) {
 | 
				
			||||||
      json = json.cast<String, dynamic>(); // ignore: parameter_assignments
 | 
					      json = json.cast<String, dynamic>(); // ignore: parameter_assignments
 | 
				
			||||||
      for (final entry in json.entries) {
 | 
					      for (final entry in json.entries) {
 | 
				
			||||||
        final value = UserResponseDto.listFromJson(
 | 
					        final value = UserResponseDto.listFromJson(entry.value, growable: growable,);
 | 
				
			||||||
          entry.value,
 | 
					 | 
				
			||||||
          growable: growable,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        if (value != null) {
 | 
					        if (value != null) {
 | 
				
			||||||
          map[entry.key] = value;
 | 
					          map[entry.key] = value;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -186,6 +179,6 @@ class UserResponseDto {
 | 
				
			|||||||
    'profileImagePath',
 | 
					    'profileImagePath',
 | 
				
			||||||
    'shouldChangePassword',
 | 
					    'shouldChangePassword',
 | 
				
			||||||
    'isAdmin',
 | 
					    'isAdmin',
 | 
				
			||||||
    'deletedAt',
 | 
					 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -14,6 +14,7 @@ import {
 | 
				
			|||||||
  Header,
 | 
					  Header,
 | 
				
			||||||
  Put,
 | 
					  Put,
 | 
				
			||||||
  UploadedFiles,
 | 
					  UploadedFiles,
 | 
				
			||||||
 | 
					  Request,
 | 
				
			||||||
} from '@nestjs/common';
 | 
					} from '@nestjs/common';
 | 
				
			||||||
import { Authenticated } from '../../decorators/authenticated.decorator';
 | 
					import { Authenticated } from '../../decorators/authenticated.decorator';
 | 
				
			||||||
import { AssetService } from './asset.service';
 | 
					import { AssetService } from './asset.service';
 | 
				
			||||||
@ -21,12 +22,12 @@ import { FileFieldsInterceptor } from '@nestjs/platform-express';
 | 
				
			|||||||
import { assetUploadOption } from '../../config/asset-upload.config';
 | 
					import { assetUploadOption } from '../../config/asset-upload.config';
 | 
				
			||||||
import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
 | 
					import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
 | 
				
			||||||
import { ServeFileDto } from './dto/serve-file.dto';
 | 
					import { ServeFileDto } from './dto/serve-file.dto';
 | 
				
			||||||
import { Response as Res } from 'express';
 | 
					import { Response as Res, Request as Req } from 'express';
 | 
				
			||||||
import { BackgroundTaskService } from '../../modules/background-task/background-task.service';
 | 
					import { BackgroundTaskService } from '../../modules/background-task/background-task.service';
 | 
				
			||||||
import { DeleteAssetDto } from './dto/delete-asset.dto';
 | 
					import { DeleteAssetDto } from './dto/delete-asset.dto';
 | 
				
			||||||
import { SearchAssetDto } from './dto/search-asset.dto';
 | 
					import { SearchAssetDto } from './dto/search-asset.dto';
 | 
				
			||||||
import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto';
 | 
					import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto';
 | 
				
			||||||
import { ApiBearerAuth, ApiBody, ApiConsumes, ApiTags } from '@nestjs/swagger';
 | 
					import { ApiBearerAuth, ApiBody, ApiConsumes, ApiHeader, ApiResponse, ApiTags } from '@nestjs/swagger';
 | 
				
			||||||
import { CuratedObjectsResponseDto } from './response-dto/curated-objects-response.dto';
 | 
					import { CuratedObjectsResponseDto } from './response-dto/curated-objects-response.dto';
 | 
				
			||||||
import { CuratedLocationsResponseDto } from './response-dto/curated-locations-response.dto';
 | 
					import { CuratedLocationsResponseDto } from './response-dto/curated-locations-response.dto';
 | 
				
			||||||
import { AssetResponseDto } from './response-dto/asset-response.dto';
 | 
					import { AssetResponseDto } from './response-dto/asset-response.dto';
 | 
				
			||||||
@ -49,6 +50,7 @@ import {
 | 
				
			|||||||
  IMMICH_ARCHIVE_FILE_COUNT,
 | 
					  IMMICH_ARCHIVE_FILE_COUNT,
 | 
				
			||||||
  IMMICH_CONTENT_LENGTH_HINT,
 | 
					  IMMICH_CONTENT_LENGTH_HINT,
 | 
				
			||||||
} from '../../constants/download.constant';
 | 
					} from '../../constants/download.constant';
 | 
				
			||||||
 | 
					import { etag } from '../../utils/etag';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Authenticated()
 | 
					@Authenticated()
 | 
				
			||||||
@ApiBearerAuth()
 | 
					@ApiBearerAuth()
 | 
				
			||||||
@ -168,8 +170,28 @@ export class AssetController {
 | 
				
			|||||||
   * Get all AssetEntity belong to the user
 | 
					   * Get all AssetEntity belong to the user
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  @Get('/')
 | 
					  @Get('/')
 | 
				
			||||||
  async getAllAssets(@GetAuthUser() authUser: AuthUserDto): Promise<AssetResponseDto[]> {
 | 
					  @ApiHeader({
 | 
				
			||||||
    return await this.assetService.getAllAssets(authUser);
 | 
					    name: 'if-none-match',
 | 
				
			||||||
 | 
					    description: 'ETag of data already cached on the client',
 | 
				
			||||||
 | 
					    required: false,
 | 
				
			||||||
 | 
					    schema: { type: 'string' },
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  @ApiResponse({
 | 
				
			||||||
 | 
					    status: 200,
 | 
				
			||||||
 | 
					    headers: { ETag: { required: true, schema: { type: 'string' } } },
 | 
				
			||||||
 | 
					    type: [AssetResponseDto],
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  async getAllAssets(@GetAuthUser() authUser: AuthUserDto, @Response() response: Res, @Request() request: Req) {
 | 
				
			||||||
 | 
					    const assets = await this.assetService.getAllAssets(authUser);
 | 
				
			||||||
 | 
					    const clientEtag = request.headers['if-none-match'];
 | 
				
			||||||
 | 
					    const json = JSON.stringify(assets);
 | 
				
			||||||
 | 
					    const serverEtag = await etag(json);
 | 
				
			||||||
 | 
					    response.setHeader('ETag', serverEtag);
 | 
				
			||||||
 | 
					    if (clientEtag === serverEtag) {
 | 
				
			||||||
 | 
					      response.status(304).end();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      response.contentType('application/json').status(200).send(json);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Post('/time-bucket')
 | 
					  @Post('/time-bucket')
 | 
				
			||||||
 | 
				
			|||||||
@ -19,10 +19,10 @@ export class AssetResponseDto {
 | 
				
			|||||||
  mimeType!: string | null;
 | 
					  mimeType!: string | null;
 | 
				
			||||||
  duration!: string;
 | 
					  duration!: string;
 | 
				
			||||||
  webpPath!: string | null;
 | 
					  webpPath!: string | null;
 | 
				
			||||||
  encodedVideoPath!: string | null;
 | 
					  encodedVideoPath?: string | null;
 | 
				
			||||||
  exifInfo?: ExifResponseDto;
 | 
					  exifInfo?: ExifResponseDto;
 | 
				
			||||||
  smartInfo?: SmartInfoResponseDto;
 | 
					  smartInfo?: SmartInfoResponseDto;
 | 
				
			||||||
  livePhotoVideoId!: string | null;
 | 
					  livePhotoVideoId?: string | null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function mapAsset(entity: AssetEntity): AssetResponseDto {
 | 
					export function mapAsset(entity: AssetEntity): AssetResponseDto {
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ export class UserResponseDto {
 | 
				
			|||||||
  profileImagePath!: string;
 | 
					  profileImagePath!: string;
 | 
				
			||||||
  shouldChangePassword!: boolean;
 | 
					  shouldChangePassword!: boolean;
 | 
				
			||||||
  isAdmin!: boolean;
 | 
					  isAdmin!: boolean;
 | 
				
			||||||
  deletedAt!: Date | null;
 | 
					  deletedAt?: Date;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function mapUser(entity: UserEntity): UserResponseDto {
 | 
					export function mapUser(entity: UserEntity): UserResponseDto {
 | 
				
			||||||
@ -22,6 +22,6 @@ export function mapUser(entity: UserEntity): UserResponseDto {
 | 
				
			|||||||
    profileImagePath: entity.profileImagePath,
 | 
					    profileImagePath: entity.profileImagePath,
 | 
				
			||||||
    shouldChangePassword: entity.shouldChangePassword,
 | 
					    shouldChangePassword: entity.shouldChangePassword,
 | 
				
			||||||
    isAdmin: entity.isAdmin,
 | 
					    isAdmin: entity.isAdmin,
 | 
				
			||||||
    deletedAt: entity.deletedAt || null,
 | 
					    deletedAt: entity.deletedAt,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										5
									
								
								server/apps/immich/src/types/index.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								server/apps/immich/src/types/index.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					declare module 'crypto' {
 | 
				
			||||||
 | 
					  namespace webcrypto {
 | 
				
			||||||
 | 
					    const subtle: SubtleCrypto;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										10
									
								
								server/apps/immich/src/utils/etag.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								server/apps/immich/src/utils/etag.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					import { webcrypto } from 'node:crypto';
 | 
				
			||||||
 | 
					const { subtle } = webcrypto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function etag(text: string): Promise<string> {
 | 
				
			||||||
 | 
					    const encoder = new TextEncoder();
 | 
				
			||||||
 | 
					    const data = encoder.encode(text);
 | 
				
			||||||
 | 
					    const buffer = await subtle.digest('SHA-1', data);
 | 
				
			||||||
 | 
					    const hash = Buffer.from(buffer).toString('base64').slice(0, 27);
 | 
				
			||||||
 | 
					    return `"${data.length}-${hash}"`;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@ -427,7 +427,7 @@ export interface AssetResponseDto {
 | 
				
			|||||||
     * @type {string}
 | 
					     * @type {string}
 | 
				
			||||||
     * @memberof AssetResponseDto
 | 
					     * @memberof AssetResponseDto
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    'encodedVideoPath': string | null;
 | 
					    'encodedVideoPath'?: string | null;
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 
 | 
					     * 
 | 
				
			||||||
     * @type {ExifResponseDto}
 | 
					     * @type {ExifResponseDto}
 | 
				
			||||||
@ -445,7 +445,7 @@ export interface AssetResponseDto {
 | 
				
			|||||||
     * @type {string}
 | 
					     * @type {string}
 | 
				
			||||||
     * @memberof AssetResponseDto
 | 
					     * @memberof AssetResponseDto
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    'livePhotoVideoId': string | null;
 | 
					    'livePhotoVideoId'?: string | null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 
 | 
					 * 
 | 
				
			||||||
@ -1729,7 +1729,7 @@ export interface UserResponseDto {
 | 
				
			|||||||
     * @type {string}
 | 
					     * @type {string}
 | 
				
			||||||
     * @memberof UserResponseDto
 | 
					     * @memberof UserResponseDto
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    'deletedAt': string | null;
 | 
					    'deletedAt'?: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 
 | 
					 * 
 | 
				
			||||||
@ -2788,10 +2788,11 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
 | 
				
			|||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * Get all AssetEntity belong to the user
 | 
					         * Get all AssetEntity belong to the user
 | 
				
			||||||
         * @summary 
 | 
					         * @summary 
 | 
				
			||||||
 | 
					         * @param {string} [ifNoneMatch] ETag of data already cached on the client
 | 
				
			||||||
         * @param {*} [options] Override http request option.
 | 
					         * @param {*} [options] Override http request option.
 | 
				
			||||||
         * @throws {RequiredError}
 | 
					         * @throws {RequiredError}
 | 
				
			||||||
         */
 | 
					         */
 | 
				
			||||||
        getAllAssets: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
 | 
					        getAllAssets: async (ifNoneMatch?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
 | 
				
			||||||
            const localVarPath = `/asset`;
 | 
					            const localVarPath = `/asset`;
 | 
				
			||||||
            // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | 
					            // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | 
				
			||||||
            const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
 | 
					            const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
 | 
				
			||||||
@ -2808,6 +2809,10 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
 | 
				
			|||||||
            // http bearer authentication required
 | 
					            // http bearer authentication required
 | 
				
			||||||
            await setBearerAuthToObject(localVarHeaderParameter, configuration)
 | 
					            await setBearerAuthToObject(localVarHeaderParameter, configuration)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (ifNoneMatch !== undefined && ifNoneMatch !== null) {
 | 
				
			||||||
 | 
					                localVarHeaderParameter['if-none-match'] = String(ifNoneMatch);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
            setSearchParams(localVarUrlObj, localVarQueryParameter);
 | 
					            setSearchParams(localVarUrlObj, localVarQueryParameter);
 | 
				
			||||||
@ -3388,11 +3393,12 @@ export const AssetApiFp = function(configuration?: Configuration) {
 | 
				
			|||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * Get all AssetEntity belong to the user
 | 
					         * Get all AssetEntity belong to the user
 | 
				
			||||||
         * @summary 
 | 
					         * @summary 
 | 
				
			||||||
 | 
					         * @param {string} [ifNoneMatch] ETag of data already cached on the client
 | 
				
			||||||
         * @param {*} [options] Override http request option.
 | 
					         * @param {*} [options] Override http request option.
 | 
				
			||||||
         * @throws {RequiredError}
 | 
					         * @throws {RequiredError}
 | 
				
			||||||
         */
 | 
					         */
 | 
				
			||||||
        async getAllAssets(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<AssetResponseDto>>> {
 | 
					        async getAllAssets(ifNoneMatch?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<AssetResponseDto>>> {
 | 
				
			||||||
            const localVarAxiosArgs = await localVarAxiosParamCreator.getAllAssets(options);
 | 
					            const localVarAxiosArgs = await localVarAxiosParamCreator.getAllAssets(ifNoneMatch, options);
 | 
				
			||||||
            return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
 | 
					            return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
@ -3590,11 +3596,12 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
 | 
				
			|||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * Get all AssetEntity belong to the user
 | 
					         * Get all AssetEntity belong to the user
 | 
				
			||||||
         * @summary 
 | 
					         * @summary 
 | 
				
			||||||
 | 
					         * @param {string} [ifNoneMatch] ETag of data already cached on the client
 | 
				
			||||||
         * @param {*} [options] Override http request option.
 | 
					         * @param {*} [options] Override http request option.
 | 
				
			||||||
         * @throws {RequiredError}
 | 
					         * @throws {RequiredError}
 | 
				
			||||||
         */
 | 
					         */
 | 
				
			||||||
        getAllAssets(options?: any): AxiosPromise<Array<AssetResponseDto>> {
 | 
					        getAllAssets(ifNoneMatch?: string, options?: any): AxiosPromise<Array<AssetResponseDto>> {
 | 
				
			||||||
            return localVarFp.getAllAssets(options).then((request) => request(axios, basePath));
 | 
					            return localVarFp.getAllAssets(ifNoneMatch, options).then((request) => request(axios, basePath));
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * Get a single asset\'s information
 | 
					         * Get a single asset\'s information
 | 
				
			||||||
@ -3788,12 +3795,13 @@ export class AssetApi extends BaseAPI {
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Get all AssetEntity belong to the user
 | 
					     * Get all AssetEntity belong to the user
 | 
				
			||||||
     * @summary 
 | 
					     * @summary 
 | 
				
			||||||
 | 
					     * @param {string} [ifNoneMatch] ETag of data already cached on the client
 | 
				
			||||||
     * @param {*} [options] Override http request option.
 | 
					     * @param {*} [options] Override http request option.
 | 
				
			||||||
     * @throws {RequiredError}
 | 
					     * @throws {RequiredError}
 | 
				
			||||||
     * @memberof AssetApi
 | 
					     * @memberof AssetApi
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public getAllAssets(options?: AxiosRequestConfig) {
 | 
					    public getAllAssets(ifNoneMatch?: string, options?: AxiosRequestConfig) {
 | 
				
			||||||
        return AssetApiFp(this.configuration).getAllAssets(options).then((request) => request(this.axios, this.basePath));
 | 
					        return AssetApiFp(this.configuration).getAllAssets(ifNoneMatch, options).then((request) => request(this.axios, this.basePath));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user