From 43fd7737f16b26a4b32d43a8b2bb4e69f8656ddb Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 7 Feb 2023 15:11:14 -0600 Subject: [PATCH] chore(server): make owner as required response for AlbumResponseDto (#1579) --- mobile/openapi/doc/AlbumResponseDto.md | 2 +- .../openapi/lib/model/album_response_dto.dart | 19 +++------- .../src/api-v1/album/album-repository.ts | 1 + .../src/api-v1/album/album.service.spec.ts | 36 +++++++++++++------ server/immich-openapi-specs.json | 3 +- .../album/response-dto/album-response.dto.ts | 6 ++-- web/src/api/open-api/api.ts | 2 +- .../shared-components/immich-thumbnail.svelte | 3 +- 8 files changed, 41 insertions(+), 31 deletions(-) diff --git a/mobile/openapi/doc/AlbumResponseDto.md b/mobile/openapi/doc/AlbumResponseDto.md index 33ab5943b..3b65af276 100644 --- a/mobile/openapi/doc/AlbumResponseDto.md +++ b/mobile/openapi/doc/AlbumResponseDto.md @@ -18,7 +18,7 @@ Name | Type | Description | Notes **shared** | **bool** | | **sharedUsers** | [**List**](UserResponseDto.md) | | [default to const []] **assets** | [**List**](AssetResponseDto.md) | | [default to const []] -**owner** | [**UserResponseDto**](UserResponseDto.md) | | [optional] +**owner** | [**UserResponseDto**](UserResponseDto.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) diff --git a/mobile/openapi/lib/model/album_response_dto.dart b/mobile/openapi/lib/model/album_response_dto.dart index 3616c7549..615206a3c 100644 --- a/mobile/openapi/lib/model/album_response_dto.dart +++ b/mobile/openapi/lib/model/album_response_dto.dart @@ -23,7 +23,7 @@ class AlbumResponseDto { required this.shared, this.sharedUsers = const [], this.assets = const [], - this.owner, + required this.owner, }); int assetCount; @@ -46,13 +46,7 @@ class AlbumResponseDto { List assets; - /// - /// 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. - /// - UserResponseDto? owner; + UserResponseDto owner; @override bool operator ==(Object other) => identical(this, other) || other is AlbumResponseDto && @@ -81,7 +75,7 @@ class AlbumResponseDto { (shared.hashCode) + (sharedUsers.hashCode) + (assets.hashCode) + - (owner == null ? 0 : owner!.hashCode); + (owner.hashCode); @override String toString() => 'AlbumResponseDto[assetCount=$assetCount, id=$id, ownerId=$ownerId, albumName=$albumName, createdAt=$createdAt, updatedAt=$updatedAt, albumThumbnailAssetId=$albumThumbnailAssetId, shared=$shared, sharedUsers=$sharedUsers, assets=$assets, owner=$owner]'; @@ -102,11 +96,7 @@ class AlbumResponseDto { json[r'shared'] = this.shared; json[r'sharedUsers'] = this.sharedUsers; json[r'assets'] = this.assets; - if (this.owner != null) { json[r'owner'] = this.owner; - } else { - // json[r'owner'] = null; - } return json; } @@ -139,7 +129,7 @@ class AlbumResponseDto { shared: mapValueOfType(json, r'shared')!, sharedUsers: UserResponseDto.listFromJson(json[r'sharedUsers'])!, assets: AssetResponseDto.listFromJson(json[r'assets'])!, - owner: UserResponseDto.fromJson(json[r'owner']), + owner: UserResponseDto.fromJson(json[r'owner'])!, ); } return null; @@ -199,6 +189,7 @@ class AlbumResponseDto { 'shared', 'sharedUsers', 'assets', + 'owner', }; } diff --git a/server/apps/immich/src/api-v1/album/album-repository.ts b/server/apps/immich/src/api-v1/album/album-repository.ts index 7fa96169c..81f72db18 100644 --- a/server/apps/immich/src/api-v1/album/album-repository.ts +++ b/server/apps/immich/src/api-v1/album/album-repository.ts @@ -49,6 +49,7 @@ export class AlbumRepository implements IAlbumRepository { relations: { sharedLinks: true, assets: true, + owner: true, }, where: { ownerId, diff --git a/server/apps/immich/src/api-v1/album/album.service.spec.ts b/server/apps/immich/src/api-v1/album/album.service.spec.ts index 6733db560..c009e1650 100644 --- a/server/apps/immich/src/api-v1/album/album.service.spec.ts +++ b/server/apps/immich/src/api-v1/album/album.service.spec.ts @@ -1,8 +1,8 @@ import { AlbumService } from './album.service'; import { AuthUserDto } from '../../decorators/auth-user.decorator'; import { BadRequestException, NotFoundException, ForbiddenException } from '@nestjs/common'; -import { AlbumEntity } from '@app/infra'; -import { AlbumResponseDto, ICryptoRepository } from '@app/domain'; +import { AlbumEntity, UserEntity } from '@app/infra'; +import { AlbumResponseDto, ICryptoRepository, mapUser } from '@app/domain'; import { AddAssetsResponseDto } from './response-dto/add-assets-response.dto'; import { IAlbumRepository } from './album-repository'; import { DownloadService } from '../../modules/download/download.service'; @@ -21,6 +21,18 @@ describe('Album service', () => { email: 'auth@test.com', isAdmin: false, }); + + const albumOwner: UserEntity = Object.freeze({ + ...authUser, + firstName: 'auth', + lastName: 'user', + createdAt: 'date', + updatedAt: 'date', + profileImagePath: '', + shouldChangePassword: false, + oauthId: '', + tags: [], + }); const albumId = 'f19ab956-4761-41ea-a5d6-bae948308d58'; const sharedAlbumOwnerId = '2222'; const sharedAlbumSharedAlsoWithId = '3333'; @@ -28,7 +40,8 @@ describe('Album service', () => { const _getOwnedAlbum = () => { const albumEntity = new AlbumEntity(); - albumEntity.ownerId = authUser.id; + albumEntity.ownerId = albumOwner.id; + albumEntity.owner = albumOwner; albumEntity.id = albumId; albumEntity.albumName = 'name'; albumEntity.createdAt = 'date'; @@ -42,7 +55,8 @@ describe('Album service', () => { const _getOwnedSharedAlbum = () => { const albumEntity = new AlbumEntity(); - albumEntity.ownerId = authUser.id; + albumEntity.ownerId = albumOwner.id; + albumEntity.owner = albumOwner; albumEntity.id = albumId; albumEntity.albumName = 'name'; albumEntity.createdAt = 'date'; @@ -68,6 +82,7 @@ describe('Album service', () => { const _getSharedWithAuthUserAlbum = () => { const albumEntity = new AlbumEntity(); albumEntity.ownerId = sharedAlbumOwnerId; + albumEntity.owner = albumOwner; albumEntity.id = albumId; albumEntity.albumName = 'name'; albumEntity.createdAt = 'date'; @@ -174,22 +189,22 @@ describe('Album service', () => { }); it('gets an owned album', async () => { - const ownerId = authUser.id; const albumId = 'f19ab956-4761-41ea-a5d6-bae948308d58'; const albumEntity = _getOwnedAlbum(); albumRepositoryMock.get.mockImplementation(() => Promise.resolve(albumEntity)); const expectedResult: AlbumResponseDto = { + ownerId: albumOwner.id, + owner: mapUser(albumOwner), + id: albumId, albumName: 'name', - albumThumbnailAssetId: null, createdAt: 'date', updatedAt: 'date', - id: 'f19ab956-4761-41ea-a5d6-bae948308d58', - ownerId, - shared: false, - assets: [], sharedUsers: [], + assets: [], + albumThumbnailAssetId: null, + shared: false, assetCount: 0, }; await expect(sut.getAlbumInfo(authUser, albumId)).resolves.toEqual(expectedResult); @@ -473,6 +488,7 @@ describe('Album service', () => { const albumEntity = new AlbumEntity(); albumEntity.ownerId = authUser.id; + albumEntity.owner = albumOwner; albumEntity.id = albumId; albumEntity.albumName = 'name'; albumEntity.createdAt = 'date'; diff --git a/server/immich-openapi-specs.json b/server/immich-openapi-specs.json index 64484ee9f..bd7442c78 100644 --- a/server/immich-openapi-specs.json +++ b/server/immich-openapi-specs.json @@ -3400,7 +3400,8 @@ "albumThumbnailAssetId", "shared", "sharedUsers", - "assets" + "assets", + "owner" ] }, "SharedLinkResponseDto": { diff --git a/server/libs/domain/src/album/response-dto/album-response.dto.ts b/server/libs/domain/src/album/response-dto/album-response.dto.ts index 7e7458727..b7a52af8c 100644 --- a/server/libs/domain/src/album/response-dto/album-response.dto.ts +++ b/server/libs/domain/src/album/response-dto/album-response.dto.ts @@ -13,7 +13,7 @@ export class AlbumResponseDto { shared!: boolean; sharedUsers!: UserResponseDto[]; assets!: AssetResponseDto[]; - owner?: UserResponseDto; + owner!: UserResponseDto; @ApiProperty({ type: 'integer' }) assetCount!: number; } @@ -35,7 +35,7 @@ export function mapAlbum(entity: AlbumEntity): AlbumResponseDto { updatedAt: entity.updatedAt, id: entity.id, ownerId: entity.ownerId, - owner: entity.owner ? mapUser(entity.owner) : undefined, + owner: mapUser(entity.owner), sharedUsers, shared: sharedUsers.length > 0 || entity.sharedLinks?.length > 0, assets: entity.assets?.map((assetAlbum) => mapAsset(assetAlbum.assetInfo)) || [], @@ -60,7 +60,7 @@ export function mapAlbumExcludeAssetInfo(entity: AlbumEntity): AlbumResponseDto updatedAt: entity.updatedAt, id: entity.id, ownerId: entity.ownerId, - owner: entity.owner ? mapUser(entity.owner) : undefined, + owner: mapUser(entity.owner), sharedUsers, shared: sharedUsers.length > 0 || entity.sharedLinks?.length > 0, assets: [], diff --git a/web/src/api/open-api/api.ts b/web/src/api/open-api/api.ts index 078877269..4789ee960 100644 --- a/web/src/api/open-api/api.ts +++ b/web/src/api/open-api/api.ts @@ -281,7 +281,7 @@ export interface AlbumResponseDto { * @type {UserResponseDto} * @memberof AlbumResponseDto */ - 'owner'?: UserResponseDto; + 'owner': UserResponseDto; } /** * diff --git a/web/src/lib/components/shared-components/immich-thumbnail.svelte b/web/src/lib/components/shared-components/immich-thumbnail.svelte index 0f9390cf4..b4b10e9f3 100644 --- a/web/src/lib/components/shared-components/immich-thumbnail.svelte +++ b/web/src/lib/components/shared-components/immich-thumbnail.svelte @@ -34,6 +34,7 @@ let calculateVideoDurationIntervalHandler: NodeJS.Timer; let videoProgress = '00:00'; let videoUrl: string; + $: isPublicShared = publicSharedKey !== ''; const loadVideoData = async (isLivePhoto: boolean) => { isThumbnailVideoPlaying = false; @@ -183,7 +184,7 @@ {/if} - {#if asset.isFavorite} + {#if asset.isFavorite && !isPublicShared}