Просмотр исходного кода

fix: show/set activity like per user (#4775)

* fix: like per user

* chore: open api

* chore: e2e test for userId filtering
Jason Rasmussen 1 год назад
Родитель
Сommit
0130591a0f

+ 18 - 5
cli/src/api/open-api/api.ts

@@ -5076,10 +5076,11 @@ export const ActivityApiAxiosParamCreator = function (configuration?: Configurat
          * @param {string} albumId 
          * @param {string} [assetId] 
          * @param {ReactionType} [type] 
+         * @param {string} [userId] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        getActivities: async (albumId: string, assetId?: string, type?: ReactionType, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+        getActivities: async (albumId: string, assetId?: string, type?: ReactionType, userId?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
             // verify required parameter 'albumId' is not null or undefined
             assertParamExists('getActivities', 'albumId', albumId)
             const localVarPath = `/activity`;
@@ -5115,6 +5116,10 @@ export const ActivityApiAxiosParamCreator = function (configuration?: Configurat
                 localVarQueryParameter['type'] = type;
             }
 
+            if (userId !== undefined) {
+                localVarQueryParameter['userId'] = userId;
+            }
+
 
     
             setSearchParams(localVarUrlObj, localVarQueryParameter);
@@ -5211,11 +5216,12 @@ export const ActivityApiFp = function(configuration?: Configuration) {
          * @param {string} albumId 
          * @param {string} [assetId] 
          * @param {ReactionType} [type] 
+         * @param {string} [userId] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async getActivities(albumId: string, assetId?: string, type?: ReactionType, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<ActivityResponseDto>>> {
-            const localVarAxiosArgs = await localVarAxiosParamCreator.getActivities(albumId, assetId, type, options);
+        async getActivities(albumId: string, assetId?: string, type?: ReactionType, userId?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<ActivityResponseDto>>> {
+            const localVarAxiosArgs = await localVarAxiosParamCreator.getActivities(albumId, assetId, type, userId, options);
             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
         },
         /**
@@ -5264,7 +5270,7 @@ export const ActivityApiFactory = function (configuration?: Configuration, baseP
          * @throws {RequiredError}
          */
         getActivities(requestParameters: ActivityApiGetActivitiesRequest, options?: AxiosRequestConfig): AxiosPromise<Array<ActivityResponseDto>> {
-            return localVarFp.getActivities(requestParameters.albumId, requestParameters.assetId, requestParameters.type, options).then((request) => request(axios, basePath));
+            return localVarFp.getActivities(requestParameters.albumId, requestParameters.assetId, requestParameters.type, requestParameters.userId, options).then((request) => request(axios, basePath));
         },
         /**
          * 
@@ -5332,6 +5338,13 @@ export interface ActivityApiGetActivitiesRequest {
      * @memberof ActivityApiGetActivities
      */
     readonly type?: ReactionType
+
+    /**
+     * 
+     * @type {string}
+     * @memberof ActivityApiGetActivities
+     */
+    readonly userId?: string
 }
 
 /**
@@ -5392,7 +5405,7 @@ export class ActivityApi extends BaseAPI {
      * @memberof ActivityApi
      */
     public getActivities(requestParameters: ActivityApiGetActivitiesRequest, options?: AxiosRequestConfig) {
-        return ActivityApiFp(this.configuration).getActivities(requestParameters.albumId, requestParameters.assetId, requestParameters.type, options).then((request) => request(this.axios, this.basePath));
+        return ActivityApiFp(this.configuration).getActivities(requestParameters.albumId, requestParameters.assetId, requestParameters.type, requestParameters.userId, options).then((request) => request(this.axios, this.basePath));
     }
 
     /**

+ 4 - 2
mobile/openapi/doc/ActivityApi.md

@@ -125,7 +125,7 @@ void (empty response body)
 [[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)
 
 # **getActivities**
-> List<ActivityResponseDto> getActivities(albumId, assetId, type)
+> List<ActivityResponseDto> getActivities(albumId, assetId, type, userId)
 
 
 
@@ -151,9 +151,10 @@ final api_instance = ActivityApi();
 final albumId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | 
 final assetId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | 
 final type = ; // ReactionType | 
+final userId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | 
 
 try {
-    final result = api_instance.getActivities(albumId, assetId, type);
+    final result = api_instance.getActivities(albumId, assetId, type, userId);
     print(result);
 } catch (e) {
     print('Exception when calling ActivityApi->getActivities: $e\n');
@@ -167,6 +168,7 @@ Name | Type | Description  | Notes
  **albumId** | **String**|  | 
  **assetId** | **String**|  | [optional] 
  **type** | [**ReactionType**](.md)|  | [optional] 
+ **userId** | **String**|  | [optional] 
 
 ### Return type
 

+ 10 - 3
mobile/openapi/lib/api/activity_api.dart

@@ -111,7 +111,9 @@ class ActivityApi {
   /// * [String] assetId:
   ///
   /// * [ReactionType] type:
-  Future<Response> getActivitiesWithHttpInfo(String albumId, { String? assetId, ReactionType? type, }) async {
+  ///
+  /// * [String] userId:
+  Future<Response> getActivitiesWithHttpInfo(String albumId, { String? assetId, ReactionType? type, String? userId, }) async {
     // ignore: prefer_const_declarations
     final path = r'/activity';
 
@@ -129,6 +131,9 @@ class ActivityApi {
     if (type != null) {
       queryParams.addAll(_queryParams('', 'type', type));
     }
+    if (userId != null) {
+      queryParams.addAll(_queryParams('', 'userId', userId));
+    }
 
     const contentTypes = <String>[];
 
@@ -151,8 +156,10 @@ class ActivityApi {
   /// * [String] assetId:
   ///
   /// * [ReactionType] type:
-  Future<List<ActivityResponseDto>?> getActivities(String albumId, { String? assetId, ReactionType? type, }) async {
-    final response = await getActivitiesWithHttpInfo(albumId,  assetId: assetId, type: type, );
+  ///
+  /// * [String] userId:
+  Future<List<ActivityResponseDto>?> getActivities(String albumId, { String? assetId, ReactionType? type, String? userId, }) async {
+    final response = await getActivitiesWithHttpInfo(albumId,  assetId: assetId, type: type, userId: userId, );
     if (response.statusCode >= HttpStatus.badRequest) {
       throw ApiException(response.statusCode, await _decodeBodyBytes(response));
     }

+ 1 - 1
mobile/openapi/test/activity_api_test.dart

@@ -27,7 +27,7 @@ void main() {
       // TODO
     });
 
-    //Future<List<ActivityResponseDto>> getActivities(String albumId, { String assetId, ReactionType type }) async
+    //Future<List<ActivityResponseDto>> getActivities(String albumId, { String assetId, ReactionType type, String userId }) async
     test('test getActivities', () async {
       // TODO
     });

+ 9 - 0
server/immich-openapi-specs.json

@@ -30,6 +30,15 @@
             "schema": {
               "$ref": "#/components/schemas/ReactionType"
             }
+          },
+          {
+            "name": "userId",
+            "required": false,
+            "in": "query",
+            "schema": {
+              "format": "uuid",
+              "type": "string"
+            }
           }
         ],
         "responses": {

+ 3 - 0
server/src/domain/activity/activity.dto.ts

@@ -38,6 +38,9 @@ export class ActivitySearchDto extends ActivityDto {
   @Optional()
   @ApiProperty({ enumName: 'ReactionType', enum: ReactionType })
   type?: ReactionType;
+
+  @ValidateUUID({ optional: true })
+  userId?: string;
 }
 
 const isComment = (dto: ActivityCreateDto) => dto.type === 'comment';

+ 1 - 0
server/src/domain/activity/activity.service.ts

@@ -28,6 +28,7 @@ export class ActivityService {
   async getAll(authUser: AuthUserDto, dto: ActivitySearchDto): Promise<ActivityResponseDto[]> {
     await this.access.requirePermission(authUser, Permission.ALBUM_READ, dto.albumId);
     const activities = await this.repository.search({
+      userId: dto.userId,
       albumId: dto.albumId,
       assetId: dto.assetId,
       isLiked: dto.type && dto.type === ReactionType.LIKE,

+ 23 - 0
server/test/e2e/activity.e2e-spec.ts

@@ -134,6 +134,29 @@ describe(`${ActivityController.name} (e2e)`, () => {
       expect(body[0]).toEqual(reaction);
     });
 
+    it('should filter by userId', async () => {
+      const [reaction] = await Promise.all([
+        api.activityApi.create(server, admin.accessToken, { albumId: album.id, type: ReactionType.LIKE }),
+      ]);
+
+      const response1 = await request(server)
+        .get('/activity')
+        .query({ albumId: album.id, userId: uuidStub.notFound })
+        .set('Authorization', `Bearer ${admin.accessToken}`);
+
+      expect(response1.status).toEqual(200);
+      expect(response1.body.length).toBe(0);
+
+      const response2 = await request(server)
+        .get('/activity')
+        .query({ albumId: album.id, userId: admin.userId })
+        .set('Authorization', `Bearer ${admin.accessToken}`);
+
+      expect(response2.status).toEqual(200);
+      expect(response2.body.length).toBe(1);
+      expect(response2.body[0]).toEqual(reaction);
+    });
+
     it('should filter by assetId', async () => {
       const [reaction] = await Promise.all([
         api.activityApi.create(server, admin.accessToken, {

+ 18 - 5
web/src/api/open-api/api.ts

@@ -5076,10 +5076,11 @@ export const ActivityApiAxiosParamCreator = function (configuration?: Configurat
          * @param {string} albumId 
          * @param {string} [assetId] 
          * @param {ReactionType} [type] 
+         * @param {string} [userId] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        getActivities: async (albumId: string, assetId?: string, type?: ReactionType, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+        getActivities: async (albumId: string, assetId?: string, type?: ReactionType, userId?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
             // verify required parameter 'albumId' is not null or undefined
             assertParamExists('getActivities', 'albumId', albumId)
             const localVarPath = `/activity`;
@@ -5115,6 +5116,10 @@ export const ActivityApiAxiosParamCreator = function (configuration?: Configurat
                 localVarQueryParameter['type'] = type;
             }
 
+            if (userId !== undefined) {
+                localVarQueryParameter['userId'] = userId;
+            }
+
 
     
             setSearchParams(localVarUrlObj, localVarQueryParameter);
@@ -5211,11 +5216,12 @@ export const ActivityApiFp = function(configuration?: Configuration) {
          * @param {string} albumId 
          * @param {string} [assetId] 
          * @param {ReactionType} [type] 
+         * @param {string} [userId] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async getActivities(albumId: string, assetId?: string, type?: ReactionType, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<ActivityResponseDto>>> {
-            const localVarAxiosArgs = await localVarAxiosParamCreator.getActivities(albumId, assetId, type, options);
+        async getActivities(albumId: string, assetId?: string, type?: ReactionType, userId?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<ActivityResponseDto>>> {
+            const localVarAxiosArgs = await localVarAxiosParamCreator.getActivities(albumId, assetId, type, userId, options);
             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
         },
         /**
@@ -5264,7 +5270,7 @@ export const ActivityApiFactory = function (configuration?: Configuration, baseP
          * @throws {RequiredError}
          */
         getActivities(requestParameters: ActivityApiGetActivitiesRequest, options?: AxiosRequestConfig): AxiosPromise<Array<ActivityResponseDto>> {
-            return localVarFp.getActivities(requestParameters.albumId, requestParameters.assetId, requestParameters.type, options).then((request) => request(axios, basePath));
+            return localVarFp.getActivities(requestParameters.albumId, requestParameters.assetId, requestParameters.type, requestParameters.userId, options).then((request) => request(axios, basePath));
         },
         /**
          * 
@@ -5332,6 +5338,13 @@ export interface ActivityApiGetActivitiesRequest {
      * @memberof ActivityApiGetActivities
      */
     readonly type?: ReactionType
+
+    /**
+     * 
+     * @type {string}
+     * @memberof ActivityApiGetActivities
+     */
+    readonly userId?: string
 }
 
 /**
@@ -5392,7 +5405,7 @@ export class ActivityApi extends BaseAPI {
      * @memberof ActivityApi
      */
     public getActivities(requestParameters: ActivityApiGetActivitiesRequest, options?: AxiosRequestConfig) {
-        return ActivityApiFp(this.configuration).getActivities(requestParameters.albumId, requestParameters.assetId, requestParameters.type, options).then((request) => request(this.axios, this.basePath));
+        return ActivityApiFp(this.configuration).getActivities(requestParameters.albumId, requestParameters.assetId, requestParameters.type, requestParameters.userId, options).then((request) => request(this.axios, this.basePath));
     }
 
     /**

+ 2 - 1
web/src/lib/components/asset-viewer/asset-viewer.svelte

@@ -123,9 +123,10 @@
   };
 
   const getFavorite = async () => {
-    if (album) {
+    if (album && user) {
       try {
         const { data } = await api.activityApi.getActivities({
+          userId: user.id,
           assetId: asset.id,
           albumId: album.id,
           type: ReactionType.Like,