mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	feat(server): add updatedAt to Asset, Album and User (#1566)
* feat: add updatedAt info to DTO and generate api * chore: remove unsued file * chore: Add update statement to add/remove asset/user to album * fix: test
This commit is contained in:
		
							
								
								
									
										2
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							| @@ -3,7 +3,7 @@ Immich API | ||||
| 
 | ||||
| This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: | ||||
| 
 | ||||
| - API version: 1.43.1 | ||||
| - API version: 1.45.0 | ||||
| - Build package: org.openapitools.codegen.languages.DartClientCodegen | ||||
| 
 | ||||
| ## Requirements | ||||
|   | ||||
							
								
								
									
										1
									
								
								mobile/openapi/doc/AlbumResponseDto.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								mobile/openapi/doc/AlbumResponseDto.md
									
									
									
										generated
									
									
									
								
							| @@ -13,6 +13,7 @@ Name | Type | Description | Notes | ||||
| **ownerId** | **String** |  |  | ||||
| **albumName** | **String** |  |  | ||||
| **createdAt** | **String** |  |  | ||||
| **updatedAt** | **String** |  |  | ||||
| **albumThumbnailAssetId** | **String** |  |  | ||||
| **shared** | **bool** |  |  | ||||
| **sharedUsers** | [**List<UserResponseDto>**](UserResponseDto.md) |  | [default to const []] | ||||
|   | ||||
							
								
								
									
										1
									
								
								mobile/openapi/doc/AssetResponseDto.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								mobile/openapi/doc/AssetResponseDto.md
									
									
									
										generated
									
									
									
								
							| @@ -17,6 +17,7 @@ Name | Type | Description | Notes | ||||
| **resizePath** | **String** |  |  | ||||
| **createdAt** | **String** |  |  | ||||
| **modifiedAt** | **String** |  |  | ||||
| **updatedAt** | **String** |  |  | ||||
| **isFavorite** | **bool** |  |  | ||||
| **mimeType** | **String** |  |  | ||||
| **duration** | **String** |  |  | ||||
|   | ||||
							
								
								
									
										102
									
								
								mobile/openapi/lib/model/album_response_dto.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										102
									
								
								mobile/openapi/lib/model/album_response_dto.dart
									
									
									
										generated
									
									
									
								
							| @@ -18,6 +18,7 @@ class AlbumResponseDto { | ||||
|     required this.ownerId, | ||||
|     required this.albumName, | ||||
|     required this.createdAt, | ||||
|     required this.updatedAt, | ||||
|     required this.albumThumbnailAssetId, | ||||
|     required this.shared, | ||||
|     this.sharedUsers = const [], | ||||
| @@ -34,6 +35,8 @@ class AlbumResponseDto { | ||||
| 
 | ||||
|   String createdAt; | ||||
| 
 | ||||
|   String updatedAt; | ||||
| 
 | ||||
|   String? albumThumbnailAssetId; | ||||
| 
 | ||||
|   bool shared; | ||||
| @@ -43,51 +46,51 @@ class AlbumResponseDto { | ||||
|   List<AssetResponseDto> assets; | ||||
| 
 | ||||
|   @override | ||||
|   bool operator ==(Object other) => | ||||
|       identical(this, other) || | ||||
|       other is AlbumResponseDto && | ||||
|           other.assetCount == assetCount && | ||||
|           other.id == id && | ||||
|           other.ownerId == ownerId && | ||||
|           other.albumName == albumName && | ||||
|           other.createdAt == createdAt && | ||||
|           other.albumThumbnailAssetId == albumThumbnailAssetId && | ||||
|           other.shared == shared && | ||||
|           other.sharedUsers == sharedUsers && | ||||
|           other.assets == assets; | ||||
|   bool operator ==(Object other) => identical(this, other) || other is AlbumResponseDto && | ||||
|      other.assetCount == assetCount && | ||||
|      other.id == id && | ||||
|      other.ownerId == ownerId && | ||||
|      other.albumName == albumName && | ||||
|      other.createdAt == createdAt && | ||||
|      other.updatedAt == updatedAt && | ||||
|      other.albumThumbnailAssetId == albumThumbnailAssetId && | ||||
|      other.shared == shared && | ||||
|      other.sharedUsers == sharedUsers && | ||||
|      other.assets == assets; | ||||
| 
 | ||||
|   @override | ||||
|   int get hashCode => | ||||
|       // ignore: unnecessary_parenthesis | ||||
|       (assetCount.hashCode) + | ||||
|       (id.hashCode) + | ||||
|       (ownerId.hashCode) + | ||||
|       (albumName.hashCode) + | ||||
|       (createdAt.hashCode) + | ||||
|       (albumThumbnailAssetId == null ? 0 : albumThumbnailAssetId!.hashCode) + | ||||
|       (shared.hashCode) + | ||||
|       (sharedUsers.hashCode) + | ||||
|       (assets.hashCode); | ||||
|     // ignore: unnecessary_parenthesis | ||||
|     (assetCount.hashCode) + | ||||
|     (id.hashCode) + | ||||
|     (ownerId.hashCode) + | ||||
|     (albumName.hashCode) + | ||||
|     (createdAt.hashCode) + | ||||
|     (updatedAt.hashCode) + | ||||
|     (albumThumbnailAssetId == null ? 0 : albumThumbnailAssetId!.hashCode) + | ||||
|     (shared.hashCode) + | ||||
|     (sharedUsers.hashCode) + | ||||
|     (assets.hashCode); | ||||
| 
 | ||||
|   @override | ||||
|   String toString() => | ||||
|       'AlbumResponseDto[assetCount=$assetCount, id=$id, ownerId=$ownerId, albumName=$albumName, createdAt=$createdAt, albumThumbnailAssetId=$albumThumbnailAssetId, shared=$shared, sharedUsers=$sharedUsers, assets=$assets]'; | ||||
|   String toString() => 'AlbumResponseDto[assetCount=$assetCount, id=$id, ownerId=$ownerId, albumName=$albumName, createdAt=$createdAt, updatedAt=$updatedAt, albumThumbnailAssetId=$albumThumbnailAssetId, shared=$shared, sharedUsers=$sharedUsers, assets=$assets]'; | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final json = <String, dynamic>{}; | ||||
|     json[r'assetCount'] = this.assetCount; | ||||
|     json[r'id'] = this.id; | ||||
|     json[r'ownerId'] = this.ownerId; | ||||
|     json[r'albumName'] = this.albumName; | ||||
|     json[r'createdAt'] = this.createdAt; | ||||
|       json[r'assetCount'] = this.assetCount; | ||||
|       json[r'id'] = this.id; | ||||
|       json[r'ownerId'] = this.ownerId; | ||||
|       json[r'albumName'] = this.albumName; | ||||
|       json[r'createdAt'] = this.createdAt; | ||||
|       json[r'updatedAt'] = this.updatedAt; | ||||
|     if (this.albumThumbnailAssetId != null) { | ||||
|       json[r'albumThumbnailAssetId'] = this.albumThumbnailAssetId; | ||||
|     } else { | ||||
|       // json[r'albumThumbnailAssetId'] = null; | ||||
|     } | ||||
|     json[r'shared'] = this.shared; | ||||
|     json[r'sharedUsers'] = this.sharedUsers; | ||||
|     json[r'assets'] = this.assets; | ||||
|       json[r'shared'] = this.shared; | ||||
|       json[r'sharedUsers'] = this.sharedUsers; | ||||
|       json[r'assets'] = this.assets; | ||||
|     return json; | ||||
|   } | ||||
| 
 | ||||
| @@ -101,13 +104,13 @@ class AlbumResponseDto { | ||||
|       // 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 "AlbumResponseDto[$key]" is missing from JSON.'); | ||||
|       //     assert(json[key] != null, 'Required key "AlbumResponseDto[$key]" has a null value in JSON.'); | ||||
|       //   }); | ||||
|       //   return true; | ||||
|       // }()); | ||||
|       assert(() { | ||||
|         requiredKeys.forEach((key) { | ||||
|           assert(json.containsKey(key), 'Required key "AlbumResponseDto[$key]" is missing from JSON.'); | ||||
|           assert(json[key] != null, 'Required key "AlbumResponseDto[$key]" has a null value in JSON.'); | ||||
|         }); | ||||
|         return true; | ||||
|       }()); | ||||
| 
 | ||||
|       return AlbumResponseDto( | ||||
|         assetCount: mapValueOfType<int>(json, r'assetCount')!, | ||||
| @@ -115,8 +118,8 @@ class AlbumResponseDto { | ||||
|         ownerId: mapValueOfType<String>(json, r'ownerId')!, | ||||
|         albumName: mapValueOfType<String>(json, r'albumName')!, | ||||
|         createdAt: mapValueOfType<String>(json, r'createdAt')!, | ||||
|         albumThumbnailAssetId: | ||||
|             mapValueOfType<String>(json, r'albumThumbnailAssetId'), | ||||
|         updatedAt: mapValueOfType<String>(json, r'updatedAt')!, | ||||
|         albumThumbnailAssetId: mapValueOfType<String>(json, r'albumThumbnailAssetId'), | ||||
|         shared: mapValueOfType<bool>(json, r'shared')!, | ||||
|         sharedUsers: UserResponseDto.listFromJson(json[r'sharedUsers'])!, | ||||
|         assets: AssetResponseDto.listFromJson(json[r'assets'])!, | ||||
| @@ -125,10 +128,7 @@ class AlbumResponseDto { | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   static List<AlbumResponseDto>? listFromJson( | ||||
|     dynamic json, { | ||||
|     bool growable = false, | ||||
|   }) { | ||||
|   static List<AlbumResponseDto>? listFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final result = <AlbumResponseDto>[]; | ||||
|     if (json is List && json.isNotEmpty) { | ||||
|       for (final row in json) { | ||||
| @@ -156,18 +156,12 @@ class AlbumResponseDto { | ||||
|   } | ||||
| 
 | ||||
|   // maps a json object with a list of AlbumResponseDto-objects as value to a dart map | ||||
|   static Map<String, List<AlbumResponseDto>> mapListFromJson( | ||||
|     dynamic json, { | ||||
|     bool growable = false, | ||||
|   }) { | ||||
|   static Map<String, List<AlbumResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final map = <String, List<AlbumResponseDto>>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||
|       for (final entry in json.entries) { | ||||
|         final value = AlbumResponseDto.listFromJson( | ||||
|           entry.value, | ||||
|           growable: growable, | ||||
|         ); | ||||
|         final value = AlbumResponseDto.listFromJson(entry.value, growable: growable,); | ||||
|         if (value != null) { | ||||
|           map[entry.key] = value; | ||||
|         } | ||||
| @@ -183,9 +177,11 @@ class AlbumResponseDto { | ||||
|     'ownerId', | ||||
|     'albumName', | ||||
|     'createdAt', | ||||
|     'updatedAt', | ||||
|     'albumThumbnailAssetId', | ||||
|     'shared', | ||||
|     'sharedUsers', | ||||
|     'assets', | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
|   | ||||
							
								
								
									
										10
									
								
								mobile/openapi/lib/model/asset_response_dto.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								mobile/openapi/lib/model/asset_response_dto.dart
									
									
									
										generated
									
									
									
								
							| @@ -22,6 +22,7 @@ class AssetResponseDto { | ||||
|     required this.resizePath, | ||||
|     required this.createdAt, | ||||
|     required this.modifiedAt, | ||||
|     required this.updatedAt, | ||||
|     required this.isFavorite, | ||||
|     required this.mimeType, | ||||
|     required this.duration, | ||||
| @@ -51,6 +52,8 @@ class AssetResponseDto { | ||||
| 
 | ||||
|   String modifiedAt; | ||||
| 
 | ||||
|   String updatedAt; | ||||
| 
 | ||||
|   bool isFavorite; | ||||
| 
 | ||||
|   String? mimeType; | ||||
| @@ -92,6 +95,7 @@ class AssetResponseDto { | ||||
|      other.resizePath == resizePath && | ||||
|      other.createdAt == createdAt && | ||||
|      other.modifiedAt == modifiedAt && | ||||
|      other.updatedAt == updatedAt && | ||||
|      other.isFavorite == isFavorite && | ||||
|      other.mimeType == mimeType && | ||||
|      other.duration == duration && | ||||
| @@ -114,6 +118,7 @@ class AssetResponseDto { | ||||
|     (resizePath == null ? 0 : resizePath!.hashCode) + | ||||
|     (createdAt.hashCode) + | ||||
|     (modifiedAt.hashCode) + | ||||
|     (updatedAt.hashCode) + | ||||
|     (isFavorite.hashCode) + | ||||
|     (mimeType == null ? 0 : mimeType!.hashCode) + | ||||
|     (duration.hashCode) + | ||||
| @@ -125,7 +130,7 @@ class AssetResponseDto { | ||||
|     (tags.hashCode); | ||||
| 
 | ||||
|   @override | ||||
|   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, tags=$tags]'; | ||||
|   String toString() => 'AssetResponseDto[type=$type, id=$id, deviceAssetId=$deviceAssetId, ownerId=$ownerId, deviceId=$deviceId, originalPath=$originalPath, resizePath=$resizePath, createdAt=$createdAt, modifiedAt=$modifiedAt, updatedAt=$updatedAt, isFavorite=$isFavorite, mimeType=$mimeType, duration=$duration, webpPath=$webpPath, encodedVideoPath=$encodedVideoPath, exifInfo=$exifInfo, smartInfo=$smartInfo, livePhotoVideoId=$livePhotoVideoId, tags=$tags]'; | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final json = <String, dynamic>{}; | ||||
| @@ -142,6 +147,7 @@ class AssetResponseDto { | ||||
|     } | ||||
|       json[r'createdAt'] = this.createdAt; | ||||
|       json[r'modifiedAt'] = this.modifiedAt; | ||||
|       json[r'updatedAt'] = this.updatedAt; | ||||
|       json[r'isFavorite'] = this.isFavorite; | ||||
|     if (this.mimeType != null) { | ||||
|       json[r'mimeType'] = this.mimeType; | ||||
| @@ -206,6 +212,7 @@ class AssetResponseDto { | ||||
|         resizePath: mapValueOfType<String>(json, r'resizePath'), | ||||
|         createdAt: mapValueOfType<String>(json, r'createdAt')!, | ||||
|         modifiedAt: mapValueOfType<String>(json, r'modifiedAt')!, | ||||
|         updatedAt: mapValueOfType<String>(json, r'updatedAt')!, | ||||
|         isFavorite: mapValueOfType<bool>(json, r'isFavorite')!, | ||||
|         mimeType: mapValueOfType<String>(json, r'mimeType'), | ||||
|         duration: mapValueOfType<String>(json, r'duration')!, | ||||
| @@ -273,6 +280,7 @@ class AssetResponseDto { | ||||
|     'resizePath', | ||||
|     'createdAt', | ||||
|     'modifiedAt', | ||||
|     'updatedAt', | ||||
|     'isFavorite', | ||||
|     'mimeType', | ||||
|     'duration', | ||||
|   | ||||
							
								
								
									
										5
									
								
								mobile/openapi/test/album_response_dto_test.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								mobile/openapi/test/album_response_dto_test.dart
									
									
									
										generated
									
									
									
								
							| @@ -41,6 +41,11 @@ void main() { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // String updatedAt | ||||
|     test('to test the property `updatedAt`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // String albumThumbnailAssetId | ||||
|     test('to test the property `albumThumbnailAssetId`', () async { | ||||
|       // TODO | ||||
|   | ||||
							
								
								
									
										5
									
								
								mobile/openapi/test/asset_response_dto_test.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								mobile/openapi/test/asset_response_dto_test.dart
									
									
									
										generated
									
									
									
								
							| @@ -61,6 +61,11 @@ void main() { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // String updatedAt | ||||
|     test('to test the property `updatedAt`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // bool isFavorite | ||||
|     test('to test the property `isFavorite`', () async { | ||||
|       // TODO | ||||
|   | ||||
| @@ -213,18 +213,27 @@ export class AlbumRepository implements IAlbumRepository { | ||||
|   } | ||||
|  | ||||
|   async get(albumId: string): Promise<AlbumEntity | undefined> { | ||||
|     const query = this.albumRepository.createQueryBuilder('album'); | ||||
|  | ||||
|     const album = await query | ||||
|       .where('album.id = :albumId', { albumId }) | ||||
|       .leftJoinAndSelect('album.sharedUsers', 'sharedUser') | ||||
|       .leftJoinAndSelect('sharedUser.userInfo', 'userInfo') | ||||
|       .leftJoinAndSelect('album.assets', 'assets') | ||||
|       .leftJoinAndSelect('assets.assetInfo', 'assetInfo') | ||||
|       .leftJoinAndSelect('assetInfo.exifInfo', 'exifInfo') | ||||
|       .leftJoinAndSelect('album.sharedLinks', 'sharedLinks') | ||||
|       .orderBy('"assetInfo"."createdAt"::timestamptz', 'ASC') | ||||
|       .getOne(); | ||||
|     const album = await this.albumRepository.findOne({ | ||||
|       where: { id: albumId }, | ||||
|       relations: { | ||||
|         sharedUsers: { | ||||
|           userInfo: true, | ||||
|         }, | ||||
|         assets: { | ||||
|           assetInfo: { | ||||
|             exifInfo: true, | ||||
|           }, | ||||
|         }, | ||||
|         sharedLinks: true, | ||||
|       }, | ||||
|       order: { | ||||
|         assets: { | ||||
|           assetInfo: { | ||||
|             createdAt: 'ASC', | ||||
|           }, | ||||
|         }, | ||||
|       }, | ||||
|     }); | ||||
|  | ||||
|     if (!album) { | ||||
|       return; | ||||
| @@ -249,11 +258,14 @@ export class AlbumRepository implements IAlbumRepository { | ||||
|     } | ||||
|  | ||||
|     await this.userAlbumRepository.save([...newRecords]); | ||||
|     await this.albumRepository.update({ id: album.id }, { updatedAt: new Date().toISOString() }); | ||||
|  | ||||
|     return this.get(album.id) as Promise<AlbumEntity>; // There is an album for sure | ||||
|   } | ||||
|  | ||||
|   async removeUser(album: AlbumEntity, userId: string): Promise<void> { | ||||
|     await this.userAlbumRepository.delete({ albumId: album.id, sharedUserId: userId }); | ||||
|     await this.albumRepository.update({ id: album.id }, { updatedAt: new Date().toISOString() }); | ||||
|   } | ||||
|  | ||||
|   async removeAssets(album: AlbumEntity, removeAssetsDto: RemoveAssetsDto): Promise<number> { | ||||
| @@ -262,6 +274,8 @@ export class AlbumRepository implements IAlbumRepository { | ||||
|       assetId: In(removeAssetsDto.assetIds), | ||||
|     }); | ||||
|  | ||||
|     await this.albumRepository.update({ id: album.id }, { updatedAt: new Date().toISOString() }); | ||||
|  | ||||
|     return res.affected || 0; | ||||
|   } | ||||
|  | ||||
| @@ -290,6 +304,8 @@ export class AlbumRepository implements IAlbumRepository { | ||||
|  | ||||
|     await this.assetAlbumRepository.save([...newRecords]); | ||||
|  | ||||
|     await this.albumRepository.update({ id: album.id }, { updatedAt: new Date().toISOString() }); | ||||
|  | ||||
|     return { | ||||
|       successfullyAdded: newRecords.length, | ||||
|       alreadyInAlbum: alreadyExisting, | ||||
|   | ||||
| @@ -32,6 +32,7 @@ describe('Album service', () => { | ||||
|     albumEntity.id = albumId; | ||||
|     albumEntity.albumName = 'name'; | ||||
|     albumEntity.createdAt = 'date'; | ||||
|     albumEntity.updatedAt = 'date'; | ||||
|     albumEntity.sharedUsers = []; | ||||
|     albumEntity.assets = []; | ||||
|     albumEntity.albumThumbnailAssetId = null; | ||||
| @@ -183,6 +184,7 @@ describe('Album service', () => { | ||||
|       albumName: 'name', | ||||
|       albumThumbnailAssetId: null, | ||||
|       createdAt: 'date', | ||||
|       updatedAt: 'date', | ||||
|       id: 'f19ab956-4761-41ea-a5d6-bae948308d58', | ||||
|       ownerId, | ||||
|       shared: false, | ||||
|   | ||||
| @@ -27,6 +27,7 @@ export class AssetCore { | ||||
|  | ||||
|       createdAt: timeUtils.checkValidTimestamp(dto.createdAt) ? dto.createdAt : new Date().toISOString(), | ||||
|       modifiedAt: timeUtils.checkValidTimestamp(dto.modifiedAt) ? dto.modifiedAt : new Date().toISOString(), | ||||
|       updatedAt: new Date().toISOString(), | ||||
|  | ||||
|       deviceAssetId: dto.deviceAssetId, | ||||
|       deviceId: dto.deviceId, | ||||
|   | ||||
| @@ -23,6 +23,7 @@ describe('TagService', () => { | ||||
|     shouldChangePassword: true, | ||||
|     createdAt: '2022-12-02T19:29:23.603Z', | ||||
|     deletedAt: undefined, | ||||
|     updatedAt: '2022-12-02T19:29:23.603Z', | ||||
|     tags: [], | ||||
|     oauthId: 'oauth-id-1', | ||||
|   }); | ||||
|   | ||||
| @@ -3291,6 +3291,9 @@ | ||||
|           "modifiedAt": { | ||||
|             "type": "string" | ||||
|           }, | ||||
|           "updatedAt": { | ||||
|             "type": "string" | ||||
|           }, | ||||
|           "isFavorite": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
| @@ -3336,6 +3339,7 @@ | ||||
|           "resizePath", | ||||
|           "createdAt", | ||||
|           "modifiedAt", | ||||
|           "updatedAt", | ||||
|           "isFavorite", | ||||
|           "mimeType", | ||||
|           "duration", | ||||
| @@ -3361,6 +3365,9 @@ | ||||
|           "createdAt": { | ||||
|             "type": "string" | ||||
|           }, | ||||
|           "updatedAt": { | ||||
|             "type": "string" | ||||
|           }, | ||||
|           "albumThumbnailAssetId": { | ||||
|             "type": "string", | ||||
|             "nullable": true | ||||
| @@ -3387,6 +3394,7 @@ | ||||
|           "ownerId", | ||||
|           "albumName", | ||||
|           "createdAt", | ||||
|           "updatedAt", | ||||
|           "albumThumbnailAssetId", | ||||
|           "shared", | ||||
|           "sharedUsers", | ||||
|   | ||||
| @@ -8,6 +8,7 @@ export class AlbumResponseDto { | ||||
|   ownerId!: string; | ||||
|   albumName!: string; | ||||
|   createdAt!: string; | ||||
|   updatedAt!: string; | ||||
|   albumThumbnailAssetId!: string | null; | ||||
|   shared!: boolean; | ||||
|   sharedUsers!: UserResponseDto[]; | ||||
| @@ -30,6 +31,7 @@ export function mapAlbum(entity: AlbumEntity): AlbumResponseDto { | ||||
|     albumName: entity.albumName, | ||||
|     albumThumbnailAssetId: entity.albumThumbnailAssetId, | ||||
|     createdAt: entity.createdAt, | ||||
|     updatedAt: entity.updatedAt, | ||||
|     id: entity.id, | ||||
|     ownerId: entity.ownerId, | ||||
|     sharedUsers, | ||||
| @@ -52,6 +54,7 @@ export function mapAlbumExcludeAssetInfo(entity: AlbumEntity): AlbumResponseDto | ||||
|     albumName: entity.albumName, | ||||
|     albumThumbnailAssetId: entity.albumThumbnailAssetId, | ||||
|     createdAt: entity.createdAt, | ||||
|     updatedAt: entity.updatedAt, | ||||
|     id: entity.id, | ||||
|     ownerId: entity.ownerId, | ||||
|     sharedUsers, | ||||
|   | ||||
| @@ -16,6 +16,7 @@ export class AssetResponseDto { | ||||
|   resizePath!: string | null; | ||||
|   createdAt!: string; | ||||
|   modifiedAt!: string; | ||||
|   updatedAt!: string; | ||||
|   isFavorite!: boolean; | ||||
|   mimeType!: string | null; | ||||
|   duration!: string; | ||||
| @@ -38,6 +39,7 @@ export function mapAsset(entity: AssetEntity): AssetResponseDto { | ||||
|     resizePath: entity.resizePath, | ||||
|     createdAt: entity.createdAt, | ||||
|     modifiedAt: entity.modifiedAt, | ||||
|     updatedAt: entity.updatedAt, | ||||
|     isFavorite: entity.isFavorite, | ||||
|     mimeType: entity.mimeType, | ||||
|     webpPath: entity.webpPath, | ||||
| @@ -61,6 +63,7 @@ export function mapAssetWithoutExif(entity: AssetEntity): AssetResponseDto { | ||||
|     resizePath: entity.resizePath, | ||||
|     createdAt: entity.createdAt, | ||||
|     modifiedAt: entity.modifiedAt, | ||||
|     updatedAt: entity.updatedAt, | ||||
|     isFavorite: entity.isFavorite, | ||||
|     mimeType: entity.mimeType, | ||||
|     webpPath: entity.webpPath, | ||||
|   | ||||
| @@ -31,6 +31,7 @@ const adminUser: UserEntity = Object.freeze({ | ||||
|   shouldChangePassword: false, | ||||
|   profileImagePath: '', | ||||
|   createdAt: '2021-01-01', | ||||
|   updatedAt: '2021-01-01', | ||||
|   tags: [], | ||||
| }); | ||||
|  | ||||
| @@ -45,6 +46,7 @@ const immichUser: UserEntity = Object.freeze({ | ||||
|   shouldChangePassword: false, | ||||
|   profileImagePath: '', | ||||
|   createdAt: '2021-01-01', | ||||
|   updatedAt: '2021-01-01', | ||||
|   tags: [], | ||||
| }); | ||||
|  | ||||
| @@ -59,6 +61,7 @@ const updatedImmichUser: UserEntity = Object.freeze({ | ||||
|   shouldChangePassword: true, | ||||
|   profileImagePath: '', | ||||
|   createdAt: '2021-01-01', | ||||
|   updatedAt: '2021-01-01', | ||||
|   tags: [], | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -48,6 +48,7 @@ const assetResponse: AssetResponseDto = { | ||||
|   resizePath: '', | ||||
|   createdAt: today.toISOString(), | ||||
|   modifiedAt: today.toISOString(), | ||||
|   updatedAt: today.toISOString(), | ||||
|   isFavorite: false, | ||||
|   mimeType: 'image/jpeg', | ||||
|   smartInfo: { | ||||
| @@ -67,6 +68,7 @@ const albumResponse: AlbumResponseDto = { | ||||
|   albumName: 'Test Album', | ||||
|   albumThumbnailAssetId: null, | ||||
|   createdAt: today.toISOString(), | ||||
|   updatedAt: today.toISOString(), | ||||
|   id: 'album-123', | ||||
|   ownerId: 'admin_id', | ||||
|   sharedUsers: [], | ||||
| @@ -126,6 +128,7 @@ export const userEntityStub = { | ||||
|     shouldChangePassword: false, | ||||
|     profileImagePath: '', | ||||
|     createdAt: '2021-01-01', | ||||
|     updatedAt: '2021-01-01', | ||||
|     tags: [], | ||||
|   }), | ||||
|   user1: Object.freeze<UserEntity>({ | ||||
| @@ -137,6 +140,7 @@ export const userEntityStub = { | ||||
|     shouldChangePassword: false, | ||||
|     profileImagePath: '', | ||||
|     createdAt: '2021-01-01', | ||||
|     updatedAt: '2021-01-01', | ||||
|     tags: [], | ||||
|   }), | ||||
| }; | ||||
| @@ -329,6 +333,7 @@ export const sharedLinkStub = { | ||||
|       ownerId: authStub.admin.id, | ||||
|       albumName: 'Test Album', | ||||
|       createdAt: today.toISOString(), | ||||
|       updatedAt: today.toISOString(), | ||||
|       albumThumbnailAssetId: null, | ||||
|       sharedUsers: [], | ||||
|       sharedLinks: [], | ||||
| @@ -348,6 +353,7 @@ export const sharedLinkStub = { | ||||
|             resizePath: '', | ||||
|             createdAt: today.toISOString(), | ||||
|             modifiedAt: today.toISOString(), | ||||
|             updatedAt: today.toISOString(), | ||||
|             isFavorite: false, | ||||
|             mimeType: 'image/jpeg', | ||||
|             smartInfo: { | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { Column, CreateDateColumn, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm'; | ||||
| import { Column, CreateDateColumn, Entity, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'; | ||||
| import { AssetAlbumEntity } from './asset-album.entity'; | ||||
| import { SharedLinkEntity } from './shared-link.entity'; | ||||
| import { UserAlbumEntity } from './user-album.entity'; | ||||
| @@ -17,6 +17,9 @@ export class AlbumEntity { | ||||
|   @CreateDateColumn({ type: 'timestamptz' }) | ||||
|   createdAt!: string; | ||||
|  | ||||
|   @UpdateDateColumn({ type: 'timestamptz' }) | ||||
|   updatedAt!: string; | ||||
|  | ||||
|   @Column({ comment: 'Asset ID to be used as thumbnail', type: 'varchar', nullable: true }) | ||||
|   albumThumbnailAssetId!: string | null; | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,14 @@ | ||||
| import { Column, Entity, Index, JoinTable, ManyToMany, OneToOne, PrimaryGeneratedColumn, Unique } from 'typeorm'; | ||||
| import { | ||||
|   Column, | ||||
|   Entity, | ||||
|   Index, | ||||
|   JoinTable, | ||||
|   ManyToMany, | ||||
|   OneToOne, | ||||
|   PrimaryGeneratedColumn, | ||||
|   Unique, | ||||
|   UpdateDateColumn, | ||||
| } from 'typeorm'; | ||||
| import { ExifEntity } from './exif.entity'; | ||||
| import { SharedLinkEntity } from './shared-link.entity'; | ||||
| import { SmartInfoEntity } from './smart-info.entity'; | ||||
| @@ -40,6 +50,9 @@ export class AssetEntity { | ||||
|   @Column({ type: 'timestamptz' }) | ||||
|   modifiedAt!: string; | ||||
|  | ||||
|   @UpdateDateColumn({ type: 'timestamptz' }) | ||||
|   updatedAt!: string; | ||||
|  | ||||
|   @Column({ type: 'boolean', default: false }) | ||||
|   isFavorite!: boolean; | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,12 @@ | ||||
| import { Column, CreateDateColumn, DeleteDateColumn, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm'; | ||||
| import { | ||||
|   Column, | ||||
|   CreateDateColumn, | ||||
|   DeleteDateColumn, | ||||
|   Entity, | ||||
|   OneToMany, | ||||
|   PrimaryGeneratedColumn, | ||||
|   UpdateDateColumn, | ||||
| } from 'typeorm'; | ||||
| import { TagEntity } from './tag.entity'; | ||||
|  | ||||
| @Entity('users') | ||||
| @@ -36,6 +44,9 @@ export class UserEntity { | ||||
|   @DeleteDateColumn() | ||||
|   deletedAt?: Date; | ||||
|  | ||||
|   @UpdateDateColumn({ type: 'timestamptz' }) | ||||
|   updatedAt!: string; | ||||
|  | ||||
|   @OneToMany(() => TagEntity, (tag) => tag.user) | ||||
|   tags!: TagEntity[]; | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,17 @@ | ||||
| import { MigrationInterface, QueryRunner } from 'typeorm'; | ||||
|  | ||||
| export class AddUpdatedAtColumnToAlbumsUsersAssets1675667878312 implements MigrationInterface { | ||||
|   name = 'AddUpdatedAtColumnToAlbumsUsersAssets1675667878312'; | ||||
|  | ||||
|   public async up(queryRunner: QueryRunner): Promise<void> { | ||||
|     await queryRunner.query(`ALTER TABLE "albums" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); | ||||
|     await queryRunner.query(`ALTER TABLE "users" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); | ||||
|     await queryRunner.query(`ALTER TABLE "assets" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); | ||||
|   } | ||||
|  | ||||
|   public async down(queryRunner: QueryRunner): Promise<void> { | ||||
|     await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "updatedAt"`); | ||||
|     await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "updatedAt"`); | ||||
|     await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "updatedAt"`); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										14
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										14
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							| @@ -4,7 +4,7 @@ | ||||
|  * Immich | ||||
|  * Immich API | ||||
|  * | ||||
|  * The version of the OpenAPI document: 1.43.1 | ||||
|  * The version of the OpenAPI document: 1.45.0 | ||||
|  *  | ||||
|  * | ||||
|  * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 | ||||
| @@ -246,6 +246,12 @@ export interface AlbumResponseDto { | ||||
|      * @memberof AlbumResponseDto | ||||
|      */ | ||||
|     'createdAt': string; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {string} | ||||
|      * @memberof AlbumResponseDto | ||||
|      */ | ||||
|     'updatedAt': string; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {string} | ||||
| @@ -462,6 +468,12 @@ export interface AssetResponseDto { | ||||
|      * @memberof AssetResponseDto | ||||
|      */ | ||||
|     'modifiedAt': string; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {string} | ||||
|      * @memberof AssetResponseDto | ||||
|      */ | ||||
|     'updatedAt': string; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {boolean} | ||||
|   | ||||
							
								
								
									
										2
									
								
								web/src/api/open-api/base.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								web/src/api/open-api/base.ts
									
									
									
										generated
									
									
									
								
							| @@ -4,7 +4,7 @@ | ||||
|  * Immich | ||||
|  * Immich API | ||||
|  * | ||||
|  * The version of the OpenAPI document: 1.43.1 | ||||
|  * The version of the OpenAPI document: 1.45.0 | ||||
|  *  | ||||
|  * | ||||
|  * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 | ||||
|   | ||||
							
								
								
									
										2
									
								
								web/src/api/open-api/common.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								web/src/api/open-api/common.ts
									
									
									
										generated
									
									
									
								
							| @@ -4,7 +4,7 @@ | ||||
|  * Immich | ||||
|  * Immich API | ||||
|  * | ||||
|  * The version of the OpenAPI document: 1.43.1 | ||||
|  * The version of the OpenAPI document: 1.45.0 | ||||
|  *  | ||||
|  * | ||||
|  * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 | ||||
|   | ||||
							
								
								
									
										2
									
								
								web/src/api/open-api/configuration.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								web/src/api/open-api/configuration.ts
									
									
									
										generated
									
									
									
								
							| @@ -4,7 +4,7 @@ | ||||
|  * Immich | ||||
|  * Immich API | ||||
|  * | ||||
|  * The version of the OpenAPI document: 1.43.1 | ||||
|  * The version of the OpenAPI document: 1.45.0 | ||||
|  *  | ||||
|  * | ||||
|  * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 | ||||
|   | ||||
							
								
								
									
										2
									
								
								web/src/api/open-api/index.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								web/src/api/open-api/index.ts
									
									
									
										generated
									
									
									
								
							| @@ -4,7 +4,7 @@ | ||||
|  * Immich | ||||
|  * Immich API | ||||
|  * | ||||
|  * The version of the OpenAPI document: 1.43.1 | ||||
|  * The version of the OpenAPI document: 1.45.0 | ||||
|  *  | ||||
|  * | ||||
|  * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user