mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	refactor(server): asset stats (#3253)
* refactor(server): asset stats * chore: open api
This commit is contained in:
		
							
								
								
									
										194
									
								
								cli/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										194
									
								
								cli/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							| @@ -486,43 +486,6 @@ export interface AssetCountByTimeBucketResponseDto { | ||||
|      */ | ||||
|     'buckets': Array<AssetCountByTimeBucket>; | ||||
| } | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
|  * @interface AssetCountByUserIdResponseDto | ||||
|  */ | ||||
| export interface AssetCountByUserIdResponseDto { | ||||
|     /** | ||||
|      *  | ||||
|      * @type {number} | ||||
|      * @memberof AssetCountByUserIdResponseDto | ||||
|      */ | ||||
|     'audio': number; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {number} | ||||
|      * @memberof AssetCountByUserIdResponseDto | ||||
|      */ | ||||
|     'photos': number; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {number} | ||||
|      * @memberof AssetCountByUserIdResponseDto | ||||
|      */ | ||||
|     'videos': number; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {number} | ||||
|      * @memberof AssetCountByUserIdResponseDto | ||||
|      */ | ||||
|     'other': number; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {number} | ||||
|      * @memberof AssetCountByUserIdResponseDto | ||||
|      */ | ||||
|     'total': number; | ||||
| } | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
| @@ -724,6 +687,31 @@ export interface AssetResponseDto { | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
|  * @interface AssetStatsResponseDto | ||||
|  */ | ||||
| export interface AssetStatsResponseDto { | ||||
|     /** | ||||
|      *  | ||||
|      * @type {number} | ||||
|      * @memberof AssetStatsResponseDto | ||||
|      */ | ||||
|     'images': number; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {number} | ||||
|      * @memberof AssetStatsResponseDto | ||||
|      */ | ||||
|     'videos': number; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {number} | ||||
|      * @memberof AssetStatsResponseDto | ||||
|      */ | ||||
|     'total': number; | ||||
| } | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
| @@ -4892,44 +4880,6 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | ||||
| 
 | ||||
| 
 | ||||
|      | ||||
|             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||
|             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||
|             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||
| 
 | ||||
|             return { | ||||
|                 url: toPathString(localVarUrlObj), | ||||
|                 options: localVarRequestOptions, | ||||
|             }; | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         getArchivedAssetCountByUserId: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||
|             const localVarPath = `/asset/stat/archive`; | ||||
|             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||
|             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||
|             let baseOptions; | ||||
|             if (configuration) { | ||||
|                 baseOptions = configuration.baseOptions; | ||||
|             } | ||||
| 
 | ||||
|             const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; | ||||
|             const localVarHeaderParameter = {} as any; | ||||
|             const localVarQueryParameter = {} as any; | ||||
| 
 | ||||
|             // authentication cookie required
 | ||||
| 
 | ||||
|             // authentication api_key required
 | ||||
|             await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) | ||||
| 
 | ||||
|             // authentication bearer required
 | ||||
|             // http bearer authentication required
 | ||||
|             await setBearerAuthToObject(localVarHeaderParameter, configuration) | ||||
| 
 | ||||
| 
 | ||||
|      | ||||
|             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||
|             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||
|             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||
| @@ -5079,8 +5029,8 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         getAssetCountByUserId: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||
|             const localVarPath = `/asset/count-by-user-id`; | ||||
|         getAssetSearchTerms: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||
|             const localVarPath = `/asset/search-terms`; | ||||
|             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||
|             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||
|             let baseOptions; | ||||
| @@ -5114,11 +5064,13 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {boolean} [isArchived]  | ||||
|          * @param {boolean} [isFavorite]  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         getAssetSearchTerms: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||
|             const localVarPath = `/asset/search-terms`; | ||||
|         getAssetStats: async (isArchived?: boolean, isFavorite?: boolean, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||
|             const localVarPath = `/asset/statistics`; | ||||
|             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||
|             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||
|             let baseOptions; | ||||
| @@ -5139,6 +5091,14 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | ||||
|             // http bearer authentication required
 | ||||
|             await setBearerAuthToObject(localVarHeaderParameter, configuration) | ||||
| 
 | ||||
|             if (isArchived !== undefined) { | ||||
|                 localVarQueryParameter['isArchived'] = isArchived; | ||||
|             } | ||||
| 
 | ||||
|             if (isFavorite !== undefined) { | ||||
|                 localVarQueryParameter['isFavorite'] = isFavorite; | ||||
|             } | ||||
| 
 | ||||
| 
 | ||||
|      | ||||
|             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||
| @@ -5887,15 +5847,6 @@ export const AssetApiFp = function(configuration?: Configuration) { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getAllAssets(userId, isFavorite, isArchived, withoutThumbs, skip, ifNoneMatch, options); | ||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         async getArchivedAssetCountByUserId(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AssetCountByUserIdResponseDto>> { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getArchivedAssetCountByUserId(options); | ||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||
|         }, | ||||
|         /** | ||||
|          * Get a single asset\'s information | ||||
|          * @param {string} id  | ||||
| @@ -5932,17 +5883,19 @@ export const AssetApiFp = function(configuration?: Configuration) { | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         async getAssetCountByUserId(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AssetCountByUserIdResponseDto>> { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetCountByUserId(options); | ||||
|         async getAssetSearchTerms(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<string>>> { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetSearchTerms(options); | ||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {boolean} [isArchived]  | ||||
|          * @param {boolean} [isFavorite]  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         async getAssetSearchTerms(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<string>>> { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetSearchTerms(options); | ||||
|         async getAssetStats(isArchived?: boolean, isFavorite?: boolean, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AssetStatsResponseDto>> { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetStats(isArchived, isFavorite, options); | ||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||
|         }, | ||||
|         /** | ||||
| @@ -6160,14 +6113,6 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath | ||||
|         getAllAssets(requestParameters: AssetApiGetAllAssetsRequest = {}, options?: AxiosRequestConfig): AxiosPromise<Array<AssetResponseDto>> { | ||||
|             return localVarFp.getAllAssets(requestParameters.userId, requestParameters.isFavorite, requestParameters.isArchived, requestParameters.withoutThumbs, requestParameters.skip, requestParameters.ifNoneMatch, options).then((request) => request(axios, basePath)); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         getArchivedAssetCountByUserId(options?: AxiosRequestConfig): AxiosPromise<AssetCountByUserIdResponseDto> { | ||||
|             return localVarFp.getArchivedAssetCountByUserId(options).then((request) => request(axios, basePath)); | ||||
|         }, | ||||
|         /** | ||||
|          * Get a single asset\'s information | ||||
|          * @param {AssetApiGetAssetByIdRequest} requestParameters Request parameters. | ||||
| @@ -6200,16 +6145,17 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         getAssetCountByUserId(options?: AxiosRequestConfig): AxiosPromise<AssetCountByUserIdResponseDto> { | ||||
|             return localVarFp.getAssetCountByUserId(options).then((request) => request(axios, basePath)); | ||||
|         getAssetSearchTerms(options?: AxiosRequestConfig): AxiosPromise<Array<string>> { | ||||
|             return localVarFp.getAssetSearchTerms(options).then((request) => request(axios, basePath)); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {AssetApiGetAssetStatsRequest} requestParameters Request parameters. | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         getAssetSearchTerms(options?: AxiosRequestConfig): AxiosPromise<Array<string>> { | ||||
|             return localVarFp.getAssetSearchTerms(options).then((request) => request(axios, basePath)); | ||||
|         getAssetStats(requestParameters: AssetApiGetAssetStatsRequest = {}, options?: AxiosRequestConfig): AxiosPromise<AssetStatsResponseDto> { | ||||
|             return localVarFp.getAssetStats(requestParameters.isArchived, requestParameters.isFavorite, options).then((request) => request(axios, basePath)); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
| @@ -6523,6 +6469,27 @@ export interface AssetApiGetAssetCountByTimeBucketRequest { | ||||
|     readonly getAssetCountByTimeBucketDto: GetAssetCountByTimeBucketDto | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Request parameters for getAssetStats operation in AssetApi. | ||||
|  * @export | ||||
|  * @interface AssetApiGetAssetStatsRequest | ||||
|  */ | ||||
| export interface AssetApiGetAssetStatsRequest { | ||||
|     /** | ||||
|      *  | ||||
|      * @type {boolean} | ||||
|      * @memberof AssetApiGetAssetStats | ||||
|      */ | ||||
|     readonly isArchived?: boolean | ||||
| 
 | ||||
|     /** | ||||
|      *  | ||||
|      * @type {boolean} | ||||
|      * @memberof AssetApiGetAssetStats | ||||
|      */ | ||||
|     readonly isFavorite?: boolean | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Request parameters for getAssetThumbnail operation in AssetApi. | ||||
|  * @export | ||||
| @@ -6915,16 +6882,6 @@ export class AssetApi extends BaseAPI { | ||||
|         return AssetApiFp(this.configuration).getAllAssets(requestParameters.userId, requestParameters.isFavorite, requestParameters.isArchived, requestParameters.withoutThumbs, requestParameters.skip, requestParameters.ifNoneMatch, options).then((request) => request(this.axios, this.basePath)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      *  | ||||
|      * @param {*} [options] Override http request option. | ||||
|      * @throws {RequiredError} | ||||
|      * @memberof AssetApi | ||||
|      */ | ||||
|     public getArchivedAssetCountByUserId(options?: AxiosRequestConfig) { | ||||
|         return AssetApiFp(this.configuration).getArchivedAssetCountByUserId(options).then((request) => request(this.axios, this.basePath)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get a single asset\'s information | ||||
|      * @param {AssetApiGetAssetByIdRequest} requestParameters Request parameters. | ||||
| @@ -6964,18 +6921,19 @@ export class AssetApi extends BaseAPI { | ||||
|      * @throws {RequiredError} | ||||
|      * @memberof AssetApi | ||||
|      */ | ||||
|     public getAssetCountByUserId(options?: AxiosRequestConfig) { | ||||
|         return AssetApiFp(this.configuration).getAssetCountByUserId(options).then((request) => request(this.axios, this.basePath)); | ||||
|     public getAssetSearchTerms(options?: AxiosRequestConfig) { | ||||
|         return AssetApiFp(this.configuration).getAssetSearchTerms(options).then((request) => request(this.axios, this.basePath)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      *  | ||||
|      * @param {AssetApiGetAssetStatsRequest} requestParameters Request parameters. | ||||
|      * @param {*} [options] Override http request option. | ||||
|      * @throws {RequiredError} | ||||
|      * @memberof AssetApi | ||||
|      */ | ||||
|     public getAssetSearchTerms(options?: AxiosRequestConfig) { | ||||
|         return AssetApiFp(this.configuration).getAssetSearchTerms(options).then((request) => request(this.axios, this.basePath)); | ||||
|     public getAssetStats(requestParameters: AssetApiGetAssetStatsRequest = {}, options?: AxiosRequestConfig) { | ||||
|         return AssetApiFp(this.configuration).getAssetStats(requestParameters.isArchived, requestParameters.isFavorite, options).then((request) => request(this.axios, this.basePath)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|   | ||||
							
								
								
									
										6
									
								
								mobile/openapi/.openapi-generator/FILES
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								mobile/openapi/.openapi-generator/FILES
									
									
									
										generated
									
									
									
								
							| @@ -23,11 +23,11 @@ doc/AssetBulkUploadCheckResponseDto.md | ||||
| doc/AssetBulkUploadCheckResult.md | ||||
| doc/AssetCountByTimeBucket.md | ||||
| doc/AssetCountByTimeBucketResponseDto.md | ||||
| doc/AssetCountByUserIdResponseDto.md | ||||
| doc/AssetFileUploadResponseDto.md | ||||
| doc/AssetIdsDto.md | ||||
| doc/AssetIdsResponseDto.md | ||||
| doc/AssetResponseDto.md | ||||
| doc/AssetStatsResponseDto.md | ||||
| doc/AssetTypeEnum.md | ||||
| doc/AudioCodec.md | ||||
| doc/AuthDeviceResponseDto.md | ||||
| @@ -163,11 +163,11 @@ lib/model/asset_bulk_upload_check_response_dto.dart | ||||
| lib/model/asset_bulk_upload_check_result.dart | ||||
| lib/model/asset_count_by_time_bucket.dart | ||||
| lib/model/asset_count_by_time_bucket_response_dto.dart | ||||
| lib/model/asset_count_by_user_id_response_dto.dart | ||||
| lib/model/asset_file_upload_response_dto.dart | ||||
| lib/model/asset_ids_dto.dart | ||||
| lib/model/asset_ids_response_dto.dart | ||||
| lib/model/asset_response_dto.dart | ||||
| lib/model/asset_stats_response_dto.dart | ||||
| lib/model/asset_type_enum.dart | ||||
| lib/model/audio_codec.dart | ||||
| lib/model/auth_device_response_dto.dart | ||||
| @@ -272,11 +272,11 @@ test/asset_bulk_upload_check_response_dto_test.dart | ||||
| test/asset_bulk_upload_check_result_test.dart | ||||
| test/asset_count_by_time_bucket_response_dto_test.dart | ||||
| test/asset_count_by_time_bucket_test.dart | ||||
| test/asset_count_by_user_id_response_dto_test.dart | ||||
| test/asset_file_upload_response_dto_test.dart | ||||
| test/asset_ids_dto_test.dart | ||||
| test/asset_ids_response_dto_test.dart | ||||
| test/asset_response_dto_test.dart | ||||
| test/asset_stats_response_dto_test.dart | ||||
| test/asset_type_enum_test.dart | ||||
| test/audio_codec_test.dart | ||||
| test/auth_device_response_dto_test.dart | ||||
|   | ||||
							
								
								
									
										5
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							| @@ -94,12 +94,11 @@ Class | Method | HTTP request | Description | ||||
| *AssetApi* | [**downloadArchive**](doc//AssetApi.md#downloadarchive) | **POST** /asset/download |  | ||||
| *AssetApi* | [**downloadFile**](doc//AssetApi.md#downloadfile) | **POST** /asset/download/{id} |  | ||||
| *AssetApi* | [**getAllAssets**](doc//AssetApi.md#getallassets) | **GET** /asset |  | ||||
| *AssetApi* | [**getArchivedAssetCountByUserId**](doc//AssetApi.md#getarchivedassetcountbyuserid) | **GET** /asset/stat/archive |  | ||||
| *AssetApi* | [**getAssetById**](doc//AssetApi.md#getassetbyid) | **GET** /asset/assetById/{id} |  | ||||
| *AssetApi* | [**getAssetByTimeBucket**](doc//AssetApi.md#getassetbytimebucket) | **POST** /asset/time-bucket |  | ||||
| *AssetApi* | [**getAssetCountByTimeBucket**](doc//AssetApi.md#getassetcountbytimebucket) | **POST** /asset/count-by-time-bucket |  | ||||
| *AssetApi* | [**getAssetCountByUserId**](doc//AssetApi.md#getassetcountbyuserid) | **GET** /asset/count-by-user-id |  | ||||
| *AssetApi* | [**getAssetSearchTerms**](doc//AssetApi.md#getassetsearchterms) | **GET** /asset/search-terms |  | ||||
| *AssetApi* | [**getAssetStats**](doc//AssetApi.md#getassetstats) | **GET** /asset/statistics |  | ||||
| *AssetApi* | [**getAssetThumbnail**](doc//AssetApi.md#getassetthumbnail) | **GET** /asset/thumbnail/{id} |  | ||||
| *AssetApi* | [**getCuratedLocations**](doc//AssetApi.md#getcuratedlocations) | **GET** /asset/curated-locations |  | ||||
| *AssetApi* | [**getCuratedObjects**](doc//AssetApi.md#getcuratedobjects) | **GET** /asset/curated-objects |  | ||||
| @@ -194,11 +193,11 @@ Class | Method | HTTP request | Description | ||||
|  - [AssetBulkUploadCheckResult](doc//AssetBulkUploadCheckResult.md) | ||||
|  - [AssetCountByTimeBucket](doc//AssetCountByTimeBucket.md) | ||||
|  - [AssetCountByTimeBucketResponseDto](doc//AssetCountByTimeBucketResponseDto.md) | ||||
|  - [AssetCountByUserIdResponseDto](doc//AssetCountByUserIdResponseDto.md) | ||||
|  - [AssetFileUploadResponseDto](doc//AssetFileUploadResponseDto.md) | ||||
|  - [AssetIdsDto](doc//AssetIdsDto.md) | ||||
|  - [AssetIdsResponseDto](doc//AssetIdsResponseDto.md) | ||||
|  - [AssetResponseDto](doc//AssetResponseDto.md) | ||||
|  - [AssetStatsResponseDto](doc//AssetStatsResponseDto.md) | ||||
|  - [AssetTypeEnum](doc//AssetTypeEnum.md) | ||||
|  - [AudioCodec](doc//AudioCodec.md) | ||||
|  - [AuthDeviceResponseDto](doc//AuthDeviceResponseDto.md) | ||||
|   | ||||
							
								
								
									
										162
									
								
								mobile/openapi/doc/AssetApi.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										162
									
								
								mobile/openapi/doc/AssetApi.md
									
									
									
										generated
									
									
									
								
							| @@ -16,12 +16,11 @@ Method | HTTP request | Description | ||||
| [**downloadArchive**](AssetApi.md#downloadarchive) | **POST** /asset/download |  | ||||
| [**downloadFile**](AssetApi.md#downloadfile) | **POST** /asset/download/{id} |  | ||||
| [**getAllAssets**](AssetApi.md#getallassets) | **GET** /asset |  | ||||
| [**getArchivedAssetCountByUserId**](AssetApi.md#getarchivedassetcountbyuserid) | **GET** /asset/stat/archive |  | ||||
| [**getAssetById**](AssetApi.md#getassetbyid) | **GET** /asset/assetById/{id} |  | ||||
| [**getAssetByTimeBucket**](AssetApi.md#getassetbytimebucket) | **POST** /asset/time-bucket |  | ||||
| [**getAssetCountByTimeBucket**](AssetApi.md#getassetcountbytimebucket) | **POST** /asset/count-by-time-bucket |  | ||||
| [**getAssetCountByUserId**](AssetApi.md#getassetcountbyuserid) | **GET** /asset/count-by-user-id |  | ||||
| [**getAssetSearchTerms**](AssetApi.md#getassetsearchterms) | **GET** /asset/search-terms |  | ||||
| [**getAssetStats**](AssetApi.md#getassetstats) | **GET** /asset/statistics |  | ||||
| [**getAssetThumbnail**](AssetApi.md#getassetthumbnail) | **GET** /asset/thumbnail/{id} |  | ||||
| [**getCuratedLocations**](AssetApi.md#getcuratedlocations) | **GET** /asset/curated-locations |  | ||||
| [**getCuratedObjects**](AssetApi.md#getcuratedobjects) | **GET** /asset/curated-objects |  | ||||
| @@ -445,57 +444,6 @@ 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) | ||||
| 
 | ||||
| # **getArchivedAssetCountByUserId** | ||||
| > AssetCountByUserIdResponseDto getArchivedAssetCountByUserId() | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ### Example | ||||
| ```dart | ||||
| import 'package:openapi/api.dart'; | ||||
| // TODO Configure API key authorization: cookie | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY'; | ||||
| // uncomment below to setup prefix (e.g. Bearer) for API key, if needed | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer'; | ||||
| // TODO Configure API key authorization: api_key | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY'; | ||||
| // uncomment below to setup prefix (e.g. Bearer) for API key, if needed | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer'; | ||||
| // TODO Configure HTTP Bearer authorization: bearer | ||||
| // Case 1. Use String Token | ||||
| //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN'); | ||||
| // Case 2. Use Function which generate token. | ||||
| // String yourTokenGeneratorFunction() { ... } | ||||
| //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); | ||||
| 
 | ||||
| final api_instance = AssetApi(); | ||||
| 
 | ||||
| try { | ||||
|     final result = api_instance.getArchivedAssetCountByUserId(); | ||||
|     print(result); | ||||
| } catch (e) { | ||||
|     print('Exception when calling AssetApi->getArchivedAssetCountByUserId: $e\n'); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### Parameters | ||||
| This endpoint does not need any parameter. | ||||
| 
 | ||||
| ### Return type | ||||
| 
 | ||||
| [**AssetCountByUserIdResponseDto**](AssetCountByUserIdResponseDto.md) | ||||
| 
 | ||||
| ### Authorization | ||||
| 
 | ||||
| [cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer) | ||||
| 
 | ||||
| ### HTTP request headers | ||||
| 
 | ||||
|  - **Content-Type**: Not defined | ||||
|  - **Accept**: application/json | ||||
| 
 | ||||
| [[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) | ||||
| 
 | ||||
| # **getAssetById** | ||||
| > AssetResponseDto getAssetById(id, key) | ||||
| 
 | ||||
| @@ -665,57 +613,6 @@ 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) | ||||
| 
 | ||||
| # **getAssetCountByUserId** | ||||
| > AssetCountByUserIdResponseDto getAssetCountByUserId() | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ### Example | ||||
| ```dart | ||||
| import 'package:openapi/api.dart'; | ||||
| // TODO Configure API key authorization: cookie | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY'; | ||||
| // uncomment below to setup prefix (e.g. Bearer) for API key, if needed | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer'; | ||||
| // TODO Configure API key authorization: api_key | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY'; | ||||
| // uncomment below to setup prefix (e.g. Bearer) for API key, if needed | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer'; | ||||
| // TODO Configure HTTP Bearer authorization: bearer | ||||
| // Case 1. Use String Token | ||||
| //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN'); | ||||
| // Case 2. Use Function which generate token. | ||||
| // String yourTokenGeneratorFunction() { ... } | ||||
| //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); | ||||
| 
 | ||||
| final api_instance = AssetApi(); | ||||
| 
 | ||||
| try { | ||||
|     final result = api_instance.getAssetCountByUserId(); | ||||
|     print(result); | ||||
| } catch (e) { | ||||
|     print('Exception when calling AssetApi->getAssetCountByUserId: $e\n'); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### Parameters | ||||
| This endpoint does not need any parameter. | ||||
| 
 | ||||
| ### Return type | ||||
| 
 | ||||
| [**AssetCountByUserIdResponseDto**](AssetCountByUserIdResponseDto.md) | ||||
| 
 | ||||
| ### Authorization | ||||
| 
 | ||||
| [cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer) | ||||
| 
 | ||||
| ### HTTP request headers | ||||
| 
 | ||||
|  - **Content-Type**: Not defined | ||||
|  - **Accept**: application/json | ||||
| 
 | ||||
| [[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) | ||||
| 
 | ||||
| # **getAssetSearchTerms** | ||||
| > List<String> getAssetSearchTerms() | ||||
| 
 | ||||
| @@ -767,6 +664,63 @@ This endpoint does not need any parameter. | ||||
| 
 | ||||
| [[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) | ||||
| 
 | ||||
| # **getAssetStats** | ||||
| > AssetStatsResponseDto getAssetStats(isArchived, isFavorite) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ### Example | ||||
| ```dart | ||||
| import 'package:openapi/api.dart'; | ||||
| // TODO Configure API key authorization: cookie | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY'; | ||||
| // uncomment below to setup prefix (e.g. Bearer) for API key, if needed | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer'; | ||||
| // TODO Configure API key authorization: api_key | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY'; | ||||
| // uncomment below to setup prefix (e.g. Bearer) for API key, if needed | ||||
| //defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer'; | ||||
| // TODO Configure HTTP Bearer authorization: bearer | ||||
| // Case 1. Use String Token | ||||
| //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN'); | ||||
| // Case 2. Use Function which generate token. | ||||
| // String yourTokenGeneratorFunction() { ... } | ||||
| //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); | ||||
| 
 | ||||
| final api_instance = AssetApi(); | ||||
| final isArchived = true; // bool |  | ||||
| final isFavorite = true; // bool |  | ||||
| 
 | ||||
| try { | ||||
|     final result = api_instance.getAssetStats(isArchived, isFavorite); | ||||
|     print(result); | ||||
| } catch (e) { | ||||
|     print('Exception when calling AssetApi->getAssetStats: $e\n'); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### Parameters | ||||
| 
 | ||||
| Name | Type | Description  | Notes | ||||
| ------------- | ------------- | ------------- | ------------- | ||||
|  **isArchived** | **bool**|  | [optional]  | ||||
|  **isFavorite** | **bool**|  | [optional]  | ||||
| 
 | ||||
| ### Return type | ||||
| 
 | ||||
| [**AssetStatsResponseDto**](AssetStatsResponseDto.md) | ||||
| 
 | ||||
| ### Authorization | ||||
| 
 | ||||
| [cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer) | ||||
| 
 | ||||
| ### HTTP request headers | ||||
| 
 | ||||
|  - **Content-Type**: Not defined | ||||
|  - **Accept**: application/json | ||||
| 
 | ||||
| [[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) | ||||
| 
 | ||||
| # **getAssetThumbnail** | ||||
| > MultipartFile getAssetThumbnail(id, format, key) | ||||
| 
 | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| # openapi.model.AssetCountByUserIdResponseDto | ||||
| # openapi.model.AssetStatsResponseDto | ||||
| 
 | ||||
| ## Load the model package | ||||
| ```dart | ||||
| @@ -8,11 +8,9 @@ import 'package:openapi/api.dart'; | ||||
| ## Properties | ||||
| Name | Type | Description | Notes | ||||
| ------------ | ------------- | ------------- | ------------- | ||||
| **audio** | **int** |  | [default to 0] | ||||
| **photos** | **int** |  | [default to 0] | ||||
| **videos** | **int** |  | [default to 0] | ||||
| **other** | **int** |  | [default to 0] | ||||
| **total** | **int** |  | [default to 0] | ||||
| **images** | **int** |  |  | ||||
| **videos** | **int** |  |  | ||||
| **total** | **int** |  |  | ||||
| 
 | ||||
| [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | ||||
| 
 | ||||
							
								
								
									
										2
									
								
								mobile/openapi/lib/api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								mobile/openapi/lib/api.dart
									
									
									
										generated
									
									
									
								
							| @@ -60,11 +60,11 @@ part 'model/asset_bulk_upload_check_response_dto.dart'; | ||||
| part 'model/asset_bulk_upload_check_result.dart'; | ||||
| part 'model/asset_count_by_time_bucket.dart'; | ||||
| part 'model/asset_count_by_time_bucket_response_dto.dart'; | ||||
| part 'model/asset_count_by_user_id_response_dto.dart'; | ||||
| part 'model/asset_file_upload_response_dto.dart'; | ||||
| part 'model/asset_ids_dto.dart'; | ||||
| part 'model/asset_ids_response_dto.dart'; | ||||
| part 'model/asset_response_dto.dart'; | ||||
| part 'model/asset_stats_response_dto.dart'; | ||||
| part 'model/asset_type_enum.dart'; | ||||
| part 'model/audio_codec.dart'; | ||||
| part 'model/auth_device_response_dto.dart'; | ||||
|   | ||||
							
								
								
									
										140
									
								
								mobile/openapi/lib/api/asset_api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										140
									
								
								mobile/openapi/lib/api/asset_api.dart
									
									
									
										generated
									
									
									
								
							| @@ -440,47 +440,6 @@ class AssetApi { | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   /// Performs an HTTP 'GET /asset/stat/archive' operation and returns the [Response]. | ||||
|   Future<Response> getArchivedAssetCountByUserIdWithHttpInfo() async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/asset/stat/archive'; | ||||
| 
 | ||||
|     // ignore: prefer_final_locals | ||||
|     Object? postBody; | ||||
| 
 | ||||
|     final queryParams = <QueryParam>[]; | ||||
|     final headerParams = <String, String>{}; | ||||
|     final formParams = <String, String>{}; | ||||
| 
 | ||||
|     const contentTypes = <String>[]; | ||||
| 
 | ||||
| 
 | ||||
|     return apiClient.invokeAPI( | ||||
|       path, | ||||
|       'GET', | ||||
|       queryParams, | ||||
|       postBody, | ||||
|       headerParams, | ||||
|       formParams, | ||||
|       contentTypes.isEmpty ? null : contentTypes.first, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   Future<AssetCountByUserIdResponseDto?> getArchivedAssetCountByUserId() async { | ||||
|     final response = await getArchivedAssetCountByUserIdWithHttpInfo(); | ||||
|     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) { | ||||
|       return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'AssetCountByUserIdResponseDto',) as AssetCountByUserIdResponseDto; | ||||
|      | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   /// Get a single asset's information | ||||
|   /// | ||||
|   /// Note: This method returns the HTTP [Response]. | ||||
| @@ -639,47 +598,6 @@ class AssetApi { | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   /// Performs an HTTP 'GET /asset/count-by-user-id' operation and returns the [Response]. | ||||
|   Future<Response> getAssetCountByUserIdWithHttpInfo() async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/asset/count-by-user-id'; | ||||
| 
 | ||||
|     // ignore: prefer_final_locals | ||||
|     Object? postBody; | ||||
| 
 | ||||
|     final queryParams = <QueryParam>[]; | ||||
|     final headerParams = <String, String>{}; | ||||
|     final formParams = <String, String>{}; | ||||
| 
 | ||||
|     const contentTypes = <String>[]; | ||||
| 
 | ||||
| 
 | ||||
|     return apiClient.invokeAPI( | ||||
|       path, | ||||
|       'GET', | ||||
|       queryParams, | ||||
|       postBody, | ||||
|       headerParams, | ||||
|       formParams, | ||||
|       contentTypes.isEmpty ? null : contentTypes.first, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   Future<AssetCountByUserIdResponseDto?> getAssetCountByUserId() async { | ||||
|     final response = await getAssetCountByUserIdWithHttpInfo(); | ||||
|     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) { | ||||
|       return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'AssetCountByUserIdResponseDto',) as AssetCountByUserIdResponseDto; | ||||
|      | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   /// Performs an HTTP 'GET /asset/search-terms' operation and returns the [Response]. | ||||
|   Future<Response> getAssetSearchTermsWithHttpInfo() async { | ||||
|     // ignore: prefer_const_declarations | ||||
| @@ -724,6 +642,64 @@ class AssetApi { | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   /// Performs an HTTP 'GET /asset/statistics' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [bool] isArchived: | ||||
|   /// | ||||
|   /// * [bool] isFavorite: | ||||
|   Future<Response> getAssetStatsWithHttpInfo({ bool? isArchived, bool? isFavorite, }) async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/asset/statistics'; | ||||
| 
 | ||||
|     // ignore: prefer_final_locals | ||||
|     Object? postBody; | ||||
| 
 | ||||
|     final queryParams = <QueryParam>[]; | ||||
|     final headerParams = <String, String>{}; | ||||
|     final formParams = <String, String>{}; | ||||
| 
 | ||||
|     if (isArchived != null) { | ||||
|       queryParams.addAll(_queryParams('', 'isArchived', isArchived)); | ||||
|     } | ||||
|     if (isFavorite != null) { | ||||
|       queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); | ||||
|     } | ||||
| 
 | ||||
|     const contentTypes = <String>[]; | ||||
| 
 | ||||
| 
 | ||||
|     return apiClient.invokeAPI( | ||||
|       path, | ||||
|       'GET', | ||||
|       queryParams, | ||||
|       postBody, | ||||
|       headerParams, | ||||
|       formParams, | ||||
|       contentTypes.isEmpty ? null : contentTypes.first, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [bool] isArchived: | ||||
|   /// | ||||
|   /// * [bool] isFavorite: | ||||
|   Future<AssetStatsResponseDto?> getAssetStats({ bool? isArchived, bool? isFavorite, }) async { | ||||
|     final response = await getAssetStatsWithHttpInfo( isArchived: isArchived, isFavorite: isFavorite, ); | ||||
|     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) { | ||||
|       return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'AssetStatsResponseDto',) as AssetStatsResponseDto; | ||||
|      | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   /// Performs an HTTP 'GET /asset/thumbnail/{id}' operation and returns the [Response]. | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   | ||||
							
								
								
									
										4
									
								
								mobile/openapi/lib/api_client.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								mobile/openapi/lib/api_client.dart
									
									
									
										generated
									
									
									
								
							| @@ -215,8 +215,6 @@ class ApiClient { | ||||
|           return AssetCountByTimeBucket.fromJson(value); | ||||
|         case 'AssetCountByTimeBucketResponseDto': | ||||
|           return AssetCountByTimeBucketResponseDto.fromJson(value); | ||||
|         case 'AssetCountByUserIdResponseDto': | ||||
|           return AssetCountByUserIdResponseDto.fromJson(value); | ||||
|         case 'AssetFileUploadResponseDto': | ||||
|           return AssetFileUploadResponseDto.fromJson(value); | ||||
|         case 'AssetIdsDto': | ||||
| @@ -225,6 +223,8 @@ class ApiClient { | ||||
|           return AssetIdsResponseDto.fromJson(value); | ||||
|         case 'AssetResponseDto': | ||||
|           return AssetResponseDto.fromJson(value); | ||||
|         case 'AssetStatsResponseDto': | ||||
|           return AssetStatsResponseDto.fromJson(value); | ||||
|         case 'AssetTypeEnum': | ||||
|           return AssetTypeEnumTypeTransformer().decode(value); | ||||
|         case 'AudioCodec': | ||||
|   | ||||
| @@ -1,130 +0,0 @@ | ||||
| // | ||||
| // AUTO-GENERATED FILE, DO NOT MODIFY! | ||||
| // | ||||
| // @dart=2.12 | ||||
| 
 | ||||
| // ignore_for_file: unused_element, unused_import | ||||
| // ignore_for_file: always_put_required_named_parameters_first | ||||
| // ignore_for_file: constant_identifier_names | ||||
| // ignore_for_file: lines_longer_than_80_chars | ||||
| 
 | ||||
| part of openapi.api; | ||||
| 
 | ||||
| class AssetCountByUserIdResponseDto { | ||||
|   /// Returns a new [AssetCountByUserIdResponseDto] instance. | ||||
|   AssetCountByUserIdResponseDto({ | ||||
|     this.audio = 0, | ||||
|     this.photos = 0, | ||||
|     this.videos = 0, | ||||
|     this.other = 0, | ||||
|     this.total = 0, | ||||
|   }); | ||||
| 
 | ||||
|   int audio; | ||||
| 
 | ||||
|   int photos; | ||||
| 
 | ||||
|   int videos; | ||||
| 
 | ||||
|   int other; | ||||
| 
 | ||||
|   int total; | ||||
| 
 | ||||
|   @override | ||||
|   bool operator ==(Object other) => identical(this, other) || other is AssetCountByUserIdResponseDto && | ||||
|      other.audio == audio && | ||||
|      other.photos == photos && | ||||
|      other.videos == videos && | ||||
|      other.other == other && | ||||
|      other.total == total; | ||||
| 
 | ||||
|   @override | ||||
|   int get hashCode => | ||||
|     // ignore: unnecessary_parenthesis | ||||
|     (audio.hashCode) + | ||||
|     (photos.hashCode) + | ||||
|     (videos.hashCode) + | ||||
|     (other.hashCode) + | ||||
|     (total.hashCode); | ||||
| 
 | ||||
|   @override | ||||
|   String toString() => 'AssetCountByUserIdResponseDto[audio=$audio, photos=$photos, videos=$videos, other=$other, total=$total]'; | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final json = <String, dynamic>{}; | ||||
|       json[r'audio'] = this.audio; | ||||
|       json[r'photos'] = this.photos; | ||||
|       json[r'videos'] = this.videos; | ||||
|       json[r'other'] = this.other; | ||||
|       json[r'total'] = this.total; | ||||
|     return json; | ||||
|   } | ||||
| 
 | ||||
|   /// Returns a new [AssetCountByUserIdResponseDto] instance and imports its values from | ||||
|   /// [value] if it's a [Map], null otherwise. | ||||
|   // ignore: prefer_constructors_over_static_methods | ||||
|   static AssetCountByUserIdResponseDto? fromJson(dynamic value) { | ||||
|     if (value is Map) { | ||||
|       final json = value.cast<String, dynamic>(); | ||||
| 
 | ||||
|       return AssetCountByUserIdResponseDto( | ||||
|         audio: mapValueOfType<int>(json, r'audio')!, | ||||
|         photos: mapValueOfType<int>(json, r'photos')!, | ||||
|         videos: mapValueOfType<int>(json, r'videos')!, | ||||
|         other: mapValueOfType<int>(json, r'other')!, | ||||
|         total: mapValueOfType<int>(json, r'total')!, | ||||
|       ); | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   static List<AssetCountByUserIdResponseDto> listFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final result = <AssetCountByUserIdResponseDto>[]; | ||||
|     if (json is List && json.isNotEmpty) { | ||||
|       for (final row in json) { | ||||
|         final value = AssetCountByUserIdResponseDto.fromJson(row); | ||||
|         if (value != null) { | ||||
|           result.add(value); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return result.toList(growable: growable); | ||||
|   } | ||||
| 
 | ||||
|   static Map<String, AssetCountByUserIdResponseDto> mapFromJson(dynamic json) { | ||||
|     final map = <String, AssetCountByUserIdResponseDto>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||
|       for (final entry in json.entries) { | ||||
|         final value = AssetCountByUserIdResponseDto.fromJson(entry.value); | ||||
|         if (value != null) { | ||||
|           map[entry.key] = value; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return map; | ||||
|   } | ||||
| 
 | ||||
|   // maps a json object with a list of AssetCountByUserIdResponseDto-objects as value to a dart map | ||||
|   static Map<String, List<AssetCountByUserIdResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final map = <String, List<AssetCountByUserIdResponseDto>>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       // ignore: parameter_assignments | ||||
|       json = json.cast<String, dynamic>(); | ||||
|       for (final entry in json.entries) { | ||||
|         map[entry.key] = AssetCountByUserIdResponseDto.listFromJson(entry.value, growable: growable,); | ||||
|       } | ||||
|     } | ||||
|     return map; | ||||
|   } | ||||
| 
 | ||||
|   /// The list of required keys that must be present in a JSON. | ||||
|   static const requiredKeys = <String>{ | ||||
|     'audio', | ||||
|     'photos', | ||||
|     'videos', | ||||
|     'other', | ||||
|     'total', | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										114
									
								
								mobile/openapi/lib/model/asset_stats_response_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								mobile/openapi/lib/model/asset_stats_response_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,114 @@ | ||||
| // | ||||
| // AUTO-GENERATED FILE, DO NOT MODIFY! | ||||
| // | ||||
| // @dart=2.12 | ||||
| 
 | ||||
| // ignore_for_file: unused_element, unused_import | ||||
| // ignore_for_file: always_put_required_named_parameters_first | ||||
| // ignore_for_file: constant_identifier_names | ||||
| // ignore_for_file: lines_longer_than_80_chars | ||||
| 
 | ||||
| part of openapi.api; | ||||
| 
 | ||||
| class AssetStatsResponseDto { | ||||
|   /// Returns a new [AssetStatsResponseDto] instance. | ||||
|   AssetStatsResponseDto({ | ||||
|     required this.images, | ||||
|     required this.videos, | ||||
|     required this.total, | ||||
|   }); | ||||
| 
 | ||||
|   int images; | ||||
| 
 | ||||
|   int videos; | ||||
| 
 | ||||
|   int total; | ||||
| 
 | ||||
|   @override | ||||
|   bool operator ==(Object other) => identical(this, other) || other is AssetStatsResponseDto && | ||||
|      other.images == images && | ||||
|      other.videos == videos && | ||||
|      other.total == total; | ||||
| 
 | ||||
|   @override | ||||
|   int get hashCode => | ||||
|     // ignore: unnecessary_parenthesis | ||||
|     (images.hashCode) + | ||||
|     (videos.hashCode) + | ||||
|     (total.hashCode); | ||||
| 
 | ||||
|   @override | ||||
|   String toString() => 'AssetStatsResponseDto[images=$images, videos=$videos, total=$total]'; | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final json = <String, dynamic>{}; | ||||
|       json[r'images'] = this.images; | ||||
|       json[r'videos'] = this.videos; | ||||
|       json[r'total'] = this.total; | ||||
|     return json; | ||||
|   } | ||||
| 
 | ||||
|   /// Returns a new [AssetStatsResponseDto] instance and imports its values from | ||||
|   /// [value] if it's a [Map], null otherwise. | ||||
|   // ignore: prefer_constructors_over_static_methods | ||||
|   static AssetStatsResponseDto? fromJson(dynamic value) { | ||||
|     if (value is Map) { | ||||
|       final json = value.cast<String, dynamic>(); | ||||
| 
 | ||||
|       return AssetStatsResponseDto( | ||||
|         images: mapValueOfType<int>(json, r'images')!, | ||||
|         videos: mapValueOfType<int>(json, r'videos')!, | ||||
|         total: mapValueOfType<int>(json, r'total')!, | ||||
|       ); | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   static List<AssetStatsResponseDto> listFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final result = <AssetStatsResponseDto>[]; | ||||
|     if (json is List && json.isNotEmpty) { | ||||
|       for (final row in json) { | ||||
|         final value = AssetStatsResponseDto.fromJson(row); | ||||
|         if (value != null) { | ||||
|           result.add(value); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return result.toList(growable: growable); | ||||
|   } | ||||
| 
 | ||||
|   static Map<String, AssetStatsResponseDto> mapFromJson(dynamic json) { | ||||
|     final map = <String, AssetStatsResponseDto>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||
|       for (final entry in json.entries) { | ||||
|         final value = AssetStatsResponseDto.fromJson(entry.value); | ||||
|         if (value != null) { | ||||
|           map[entry.key] = value; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return map; | ||||
|   } | ||||
| 
 | ||||
|   // maps a json object with a list of AssetStatsResponseDto-objects as value to a dart map | ||||
|   static Map<String, List<AssetStatsResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final map = <String, List<AssetStatsResponseDto>>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       // ignore: parameter_assignments | ||||
|       json = json.cast<String, dynamic>(); | ||||
|       for (final entry in json.entries) { | ||||
|         map[entry.key] = AssetStatsResponseDto.listFromJson(entry.value, growable: growable,); | ||||
|       } | ||||
|     } | ||||
|     return map; | ||||
|   } | ||||
| 
 | ||||
|   /// The list of required keys that must be present in a JSON. | ||||
|   static const requiredKeys = <String>{ | ||||
|     'images', | ||||
|     'videos', | ||||
|     'total', | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										13
									
								
								mobile/openapi/test/asset_api_test.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										13
									
								
								mobile/openapi/test/asset_api_test.dart
									
									
									
										generated
									
									
									
								
							| @@ -60,11 +60,6 @@ void main() { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     //Future<AssetCountByUserIdResponseDto> getArchivedAssetCountByUserId() async | ||||
|     test('test getArchivedAssetCountByUserId', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // Get a single asset's information | ||||
|     // | ||||
|     //Future<AssetResponseDto> getAssetById(String id, { String key }) async | ||||
| @@ -82,13 +77,13 @@ void main() { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     //Future<AssetCountByUserIdResponseDto> getAssetCountByUserId() async | ||||
|     test('test getAssetCountByUserId', () async { | ||||
|     //Future<List<String>> getAssetSearchTerms() async | ||||
|     test('test getAssetSearchTerms', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     //Future<List<String>> getAssetSearchTerms() async | ||||
|     test('test getAssetSearchTerms', () async { | ||||
|     //Future<AssetStatsResponseDto> getAssetStats({ bool isArchived, bool isFavorite }) async | ||||
|     test('test getAssetStats', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|   | ||||
| @@ -11,32 +11,22 @@ | ||||
| import 'package:openapi/api.dart'; | ||||
| import 'package:test/test.dart'; | ||||
| 
 | ||||
| // tests for AssetCountByUserIdResponseDto | ||||
| // tests for AssetStatsResponseDto | ||||
| void main() { | ||||
|   // final instance = AssetCountByUserIdResponseDto(); | ||||
|   // final instance = AssetStatsResponseDto(); | ||||
| 
 | ||||
|   group('test AssetCountByUserIdResponseDto', () { | ||||
|     // int audio (default value: 0) | ||||
|     test('to test the property `audio`', () async { | ||||
|   group('test AssetStatsResponseDto', () { | ||||
|     // int images | ||||
|     test('to test the property `images`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // int photos (default value: 0) | ||||
|     test('to test the property `photos`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // int videos (default value: 0) | ||||
|     // int videos | ||||
|     test('to test the property `videos`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // int other (default value: 0) | ||||
|     test('to test the property `other`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // int total (default value: 0) | ||||
|     // int total | ||||
|     test('to test the property `total`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| @@ -984,38 +984,6 @@ | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     "/asset/count-by-user-id": { | ||||
|       "get": { | ||||
|         "operationId": "getAssetCountByUserId", | ||||
|         "parameters": [], | ||||
|         "responses": { | ||||
|           "200": { | ||||
|             "description": "", | ||||
|             "content": { | ||||
|               "application/json": { | ||||
|                 "schema": { | ||||
|                   "$ref": "#/components/schemas/AssetCountByUserIdResponseDto" | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "tags": [ | ||||
|           "Asset" | ||||
|         ], | ||||
|         "security": [ | ||||
|           { | ||||
|             "bearer": [] | ||||
|           }, | ||||
|           { | ||||
|             "cookie": [] | ||||
|           }, | ||||
|           { | ||||
|             "api_key": [] | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     "/asset/curated-locations": { | ||||
|       "get": { | ||||
|         "operationId": "getCuratedLocations", | ||||
| @@ -1608,17 +1576,34 @@ | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     "/asset/stat/archive": { | ||||
|     "/asset/statistics": { | ||||
|       "get": { | ||||
|         "operationId": "getArchivedAssetCountByUserId", | ||||
|         "parameters": [], | ||||
|         "operationId": "getAssetStats", | ||||
|         "parameters": [ | ||||
|           { | ||||
|             "name": "isArchived", | ||||
|             "required": false, | ||||
|             "in": "query", | ||||
|             "schema": { | ||||
|               "type": "boolean" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "name": "isFavorite", | ||||
|             "required": false, | ||||
|             "in": "query", | ||||
|             "schema": { | ||||
|               "type": "boolean" | ||||
|             } | ||||
|           } | ||||
|         ], | ||||
|         "responses": { | ||||
|           "200": { | ||||
|             "description": "", | ||||
|             "content": { | ||||
|               "application/json": { | ||||
|                 "schema": { | ||||
|                   "$ref": "#/components/schemas/AssetCountByUserIdResponseDto" | ||||
|                   "$ref": "#/components/schemas/AssetStatsResponseDto" | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
| @@ -4786,38 +4771,6 @@ | ||||
|           "buckets" | ||||
|         ] | ||||
|       }, | ||||
|       "AssetCountByUserIdResponseDto": { | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
|           "audio": { | ||||
|             "type": "integer", | ||||
|             "default": 0 | ||||
|           }, | ||||
|           "photos": { | ||||
|             "type": "integer", | ||||
|             "default": 0 | ||||
|           }, | ||||
|           "videos": { | ||||
|             "type": "integer", | ||||
|             "default": 0 | ||||
|           }, | ||||
|           "other": { | ||||
|             "type": "integer", | ||||
|             "default": 0 | ||||
|           }, | ||||
|           "total": { | ||||
|             "type": "integer", | ||||
|             "default": 0 | ||||
|           } | ||||
|         }, | ||||
|         "required": [ | ||||
|           "audio", | ||||
|           "photos", | ||||
|           "videos", | ||||
|           "other", | ||||
|           "total" | ||||
|         ] | ||||
|       }, | ||||
|       "AssetFileUploadResponseDto": { | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
| @@ -4970,6 +4923,25 @@ | ||||
|           "checksum" | ||||
|         ] | ||||
|       }, | ||||
|       "AssetStatsResponseDto": { | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
|           "images": { | ||||
|             "type": "integer" | ||||
|           }, | ||||
|           "videos": { | ||||
|             "type": "integer" | ||||
|           }, | ||||
|           "total": { | ||||
|             "type": "integer" | ||||
|           } | ||||
|         }, | ||||
|         "required": [ | ||||
|           "images", | ||||
|           "videos", | ||||
|           "total" | ||||
|         ] | ||||
|       }, | ||||
|       "AssetTypeEnum": { | ||||
|         "type": "string", | ||||
|         "enum": [ | ||||
|   | ||||
| @@ -1,6 +1,13 @@ | ||||
| import { AssetEntity, AssetType } from '@app/infra/entities'; | ||||
| import { Paginated, PaginationOptions } from '../domain.util'; | ||||
|  | ||||
| export type AssetStats = Record<AssetType, number>; | ||||
|  | ||||
| export interface AssetStatsOptions { | ||||
|   isFavorite?: boolean; | ||||
|   isArchived?: boolean; | ||||
| } | ||||
|  | ||||
| export interface AssetSearchOptions { | ||||
|   isVisible?: boolean; | ||||
|   type?: AssetType; | ||||
| @@ -55,4 +62,5 @@ export interface IAssetRepository { | ||||
|   save(asset: Partial<AssetEntity>): Promise<AssetEntity>; | ||||
|   findLivePhotoMatch(options: LivePhotoSearchOptions): Promise<AssetEntity | null>; | ||||
|   getMapMarkers(ownerId: string, options?: MapMarkerSearchOptions): Promise<MapMarker[]>; | ||||
|   getStatistics(ownerId: string, options: AssetStatsOptions): Promise<AssetStats>; | ||||
| } | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| import { AssetType } from '@app/infra/entities'; | ||||
| import { BadRequestException } from '@nestjs/common'; | ||||
| import { | ||||
|   assetEntityStub, | ||||
| @@ -10,9 +11,9 @@ import { | ||||
| import { when } from 'jest-when'; | ||||
| import { Readable } from 'stream'; | ||||
| import { IStorageRepository } from '../storage'; | ||||
| import { IAssetRepository } from './asset.repository'; | ||||
| import { AssetStats, IAssetRepository } from './asset.repository'; | ||||
| import { AssetService } from './asset.service'; | ||||
| import { DownloadResponseDto } from './index'; | ||||
| import { AssetStatsResponseDto, DownloadResponseDto } from './dto'; | ||||
| import { mapAsset } from './response-dto'; | ||||
|  | ||||
| const downloadResponse: DownloadResponseDto = { | ||||
| @@ -25,6 +26,19 @@ const downloadResponse: DownloadResponseDto = { | ||||
|   ], | ||||
| }; | ||||
|  | ||||
| const stats: AssetStats = { | ||||
|   [AssetType.IMAGE]: 10, | ||||
|   [AssetType.VIDEO]: 23, | ||||
|   [AssetType.AUDIO]: 0, | ||||
|   [AssetType.OTHER]: 0, | ||||
| }; | ||||
|  | ||||
| const statResponse: AssetStatsResponseDto = { | ||||
|   images: 10, | ||||
|   videos: 23, | ||||
|   total: 33, | ||||
| }; | ||||
|  | ||||
| describe(AssetService.name, () => { | ||||
|   let sut: AssetService; | ||||
|   let accessMock: IAccessRepositoryMock; | ||||
| @@ -287,4 +301,30 @@ describe(AssetService.name, () => { | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('getStatistics', () => { | ||||
|     it('should get the statistics for a user, excluding archived assets', async () => { | ||||
|       assetMock.getStatistics.mockResolvedValue(stats); | ||||
|       await expect(sut.getStatistics(authStub.admin, { isArchived: false })).resolves.toEqual(statResponse); | ||||
|       expect(assetMock.getStatistics).toHaveBeenCalledWith(authStub.admin.id, { isArchived: false }); | ||||
|     }); | ||||
|  | ||||
|     it('should get the statistics for a user for archived assets', async () => { | ||||
|       assetMock.getStatistics.mockResolvedValue(stats); | ||||
|       await expect(sut.getStatistics(authStub.admin, { isArchived: true })).resolves.toEqual(statResponse); | ||||
|       expect(assetMock.getStatistics).toHaveBeenCalledWith(authStub.admin.id, { isArchived: true }); | ||||
|     }); | ||||
|  | ||||
|     it('should get the statistics for a user for favorite assets', async () => { | ||||
|       assetMock.getStatistics.mockResolvedValue(stats); | ||||
|       await expect(sut.getStatistics(authStub.admin, { isFavorite: true })).resolves.toEqual(statResponse); | ||||
|       expect(assetMock.getStatistics).toHaveBeenCalledWith(authStub.admin.id, { isFavorite: true }); | ||||
|     }); | ||||
|  | ||||
|     it('should get the statistics for a user for all assets', async () => { | ||||
|       assetMock.getStatistics.mockResolvedValue(stats); | ||||
|       await expect(sut.getStatistics(authStub.admin, {})).resolves.toEqual(statResponse); | ||||
|       expect(assetMock.getStatistics).toHaveBeenCalledWith(authStub.admin.id, {}); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|   | ||||
| @@ -9,6 +9,7 @@ import { HumanReadableSize, usePagination } from '../domain.util'; | ||||
| import { ImmichReadStream, IStorageRepository } from '../storage'; | ||||
| import { IAssetRepository } from './asset.repository'; | ||||
| import { AssetIdsDto, DownloadArchiveInfo, DownloadDto, DownloadResponseDto, MemoryLaneDto } from './dto'; | ||||
| import { AssetStatsDto, mapStats } from './dto/asset-statistics.dto'; | ||||
| import { MapMarkerDto } from './dto/map-marker.dto'; | ||||
| import { mapAsset, MapMarkerResponseDto } from './response-dto'; | ||||
| import { MemoryLaneResponseDto } from './response-dto/memory-lane-response.dto'; | ||||
| @@ -155,4 +156,9 @@ export class AssetService { | ||||
|  | ||||
|     throw new BadRequestException('assetIds, albumId, or userId is required'); | ||||
|   } | ||||
|  | ||||
|   async getStatistics(authUser: AuthUserDto, dto: AssetStatsDto) { | ||||
|     const stats = await this.assetRepository.getStatistics(authUser.id, dto); | ||||
|     return mapStats(stats); | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										37
									
								
								server/src/domain/asset/dto/asset-statistics.dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								server/src/domain/asset/dto/asset-statistics.dto.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| import { AssetType } from '@app/infra/entities'; | ||||
| import { ApiProperty } from '@nestjs/swagger'; | ||||
| import { Transform } from 'class-transformer'; | ||||
| import { IsBoolean, IsOptional } from 'class-validator'; | ||||
| import { toBoolean } from '../../domain.util'; | ||||
| import { AssetStats } from '../asset.repository'; | ||||
|  | ||||
| export class AssetStatsDto { | ||||
|   @IsBoolean() | ||||
|   @Transform(toBoolean) | ||||
|   @IsOptional() | ||||
|   isArchived?: boolean; | ||||
|  | ||||
|   @IsBoolean() | ||||
|   @Transform(toBoolean) | ||||
|   @IsOptional() | ||||
|   isFavorite?: boolean; | ||||
| } | ||||
|  | ||||
| export class AssetStatsResponseDto { | ||||
|   @ApiProperty({ type: 'integer' }) | ||||
|   images!: number; | ||||
|  | ||||
|   @ApiProperty({ type: 'integer' }) | ||||
|   videos!: number; | ||||
|  | ||||
|   @ApiProperty({ type: 'integer' }) | ||||
|   total!: number; | ||||
| } | ||||
|  | ||||
| export const mapStats = (stats: AssetStats): AssetStatsResponseDto => { | ||||
|   return { | ||||
|     images: stats[AssetType.IMAGE], | ||||
|     videos: stats[AssetType.VIDEO], | ||||
|     total: Object.values(stats).reduce((total, value) => total + value, 0), | ||||
|   }; | ||||
| }; | ||||
| @@ -1,4 +1,5 @@ | ||||
| export * from './asset-ids.dto'; | ||||
| export * from './asset-statistics.dto'; | ||||
| export * from './download.dto'; | ||||
| export * from './map-marker.dto'; | ||||
| export * from './memory-lane.dto'; | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { AssetEntity, AssetType, ExifEntity } from '@app/infra/entities'; | ||||
| import { AssetEntity, ExifEntity } from '@app/infra/entities'; | ||||
| import { Injectable } from '@nestjs/common'; | ||||
| import { InjectRepository } from '@nestjs/typeorm'; | ||||
| import { IsNull, Not } from 'typeorm'; | ||||
| @@ -11,7 +11,6 @@ import { GetAssetCountByTimeBucketDto, TimeGroupEnum } from './dto/get-asset-cou | ||||
| import { SearchPropertiesDto } from './dto/search-properties.dto'; | ||||
| import { UpdateAssetDto } from './dto/update-asset.dto'; | ||||
| import { AssetCountByTimeBucket } from './response-dto/asset-count-by-time-group-response.dto'; | ||||
| import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-user-id-response.dto'; | ||||
| import { CuratedLocationsResponseDto } from './response-dto/curated-locations-response.dto'; | ||||
| import { CuratedObjectsResponseDto } from './response-dto/curated-objects-response.dto'; | ||||
|  | ||||
| @@ -38,8 +37,6 @@ export interface IAssetRepository { | ||||
|   getDetectedObjectsByUserId(userId: string): Promise<CuratedObjectsResponseDto[]>; | ||||
|   getSearchPropertiesByUserId(userId: string): Promise<SearchPropertiesDto[]>; | ||||
|   getAssetCountByTimeBucket(userId: string, dto: GetAssetCountByTimeBucketDto): Promise<AssetCountByTimeBucket[]>; | ||||
|   getAssetCountByUserId(userId: string): Promise<AssetCountByUserIdResponseDto>; | ||||
|   getArchivedAssetCountByUserId(userId: string): Promise<AssetCountByUserIdResponseDto>; | ||||
|   getAssetByTimeBucket(userId: string, getAssetByTimeBucketDto: GetAssetByTimeBucketDto): Promise<AssetEntity[]>; | ||||
|   getAssetsByChecksums(userId: string, checksums: Buffer[]): Promise<AssetCheck[]>; | ||||
|   getExistingAssets(userId: string, checkDuplicateAssetDto: CheckExistingAssetsDto): Promise<string[]>; | ||||
| @@ -55,35 +52,6 @@ export class AssetRepository implements IAssetRepository { | ||||
|     @InjectRepository(ExifEntity) private exifRepository: Repository<ExifEntity>, | ||||
|   ) {} | ||||
|  | ||||
|   async getAssetCountByUserId(ownerId: string): Promise<AssetCountByUserIdResponseDto> { | ||||
|     // Get asset count by AssetType | ||||
|     const items = await this.assetRepository | ||||
|       .createQueryBuilder('asset') | ||||
|       .select(`COUNT(asset.id)`, 'count') | ||||
|       .addSelect(`asset.type`, 'type') | ||||
|       .where('"ownerId" = :ownerId', { ownerId: ownerId }) | ||||
|       .andWhere('asset.isVisible = true') | ||||
|       .groupBy('asset.type') | ||||
|       .getRawMany(); | ||||
|  | ||||
|     return this.getAssetCount(items); | ||||
|   } | ||||
|  | ||||
|   async getArchivedAssetCountByUserId(ownerId: string): Promise<AssetCountByUserIdResponseDto> { | ||||
|     // Get archived asset count by AssetType | ||||
|     const items = await this.assetRepository | ||||
|       .createQueryBuilder('asset') | ||||
|       .select(`COUNT(asset.id)`, 'count') | ||||
|       .addSelect(`asset.type`, 'type') | ||||
|       .where('"ownerId" = :ownerId', { ownerId: ownerId }) | ||||
|       .andWhere('asset.isVisible = true') | ||||
|       .andWhere('asset.isArchived = true') | ||||
|       .groupBy('asset.type') | ||||
|       .getRawMany(); | ||||
|  | ||||
|     return this.getAssetCount(items); | ||||
|   } | ||||
|  | ||||
|   async getAssetByTimeBucket(userId: string, dto: GetAssetByTimeBucketDto): Promise<AssetEntity[]> { | ||||
|     // Get asset entity from a list of time buckets | ||||
|     let builder = this.assetRepository | ||||
| @@ -337,29 +305,6 @@ export class AssetRepository implements IAssetRepository { | ||||
|     return assets.map((asset) => asset.deviceAssetId); | ||||
|   } | ||||
|  | ||||
|   private getAssetCount(items: any): AssetCountByUserIdResponseDto { | ||||
|     const assetCountByUserId = new AssetCountByUserIdResponseDto(); | ||||
|  | ||||
|     // asset type to dto property mapping | ||||
|     const map: Record<AssetType, keyof AssetCountByUserIdResponseDto> = { | ||||
|       [AssetType.AUDIO]: 'audio', | ||||
|       [AssetType.IMAGE]: 'photos', | ||||
|       [AssetType.VIDEO]: 'videos', | ||||
|       [AssetType.OTHER]: 'other', | ||||
|     }; | ||||
|  | ||||
|     for (const item of items) { | ||||
|       const count = Number(item.count) || 0; | ||||
|       const assetType = item.type as AssetType; | ||||
|       const type = map[assetType]; | ||||
|  | ||||
|       assetCountByUserId[type] = count; | ||||
|       assetCountByUserId.total += count; | ||||
|     } | ||||
|  | ||||
|     return assetCountByUserId; | ||||
|   } | ||||
|  | ||||
|   getByOriginalPath(originalPath: string): Promise<AssetOwnerCheck | null> { | ||||
|     return this.assetRepository.findOne({ | ||||
|       select: { | ||||
|   | ||||
| @@ -38,7 +38,6 @@ import { ServeFileDto } from './dto/serve-file.dto'; | ||||
| import { UpdateAssetDto } from './dto/update-asset.dto'; | ||||
| import { AssetBulkUploadCheckResponseDto } from './response-dto/asset-check-response.dto'; | ||||
| import { AssetCountByTimeBucketResponseDto } from './response-dto/asset-count-by-time-group-response.dto'; | ||||
| import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-user-id-response.dto'; | ||||
| import { AssetFileUploadResponseDto } from './response-dto/asset-file-upload-response.dto'; | ||||
| import { CheckDuplicateAssetResponseDto } from './response-dto/check-duplicate-asset-response.dto'; | ||||
| import { CheckExistingAssetsResponseDto } from './response-dto/check-existing-assets-response.dto'; | ||||
| @@ -173,15 +172,6 @@ export class AssetController { | ||||
|     return this.assetService.getAssetCountByTimeBucket(authUser, dto); | ||||
|   } | ||||
|  | ||||
|   @Get('/count-by-user-id') | ||||
|   getAssetCountByUserId(@AuthUser() authUser: AuthUserDto): Promise<AssetCountByUserIdResponseDto> { | ||||
|     return this.assetService.getAssetCountByUserId(authUser); | ||||
|   } | ||||
|  | ||||
|   @Get('/stat/archive') | ||||
|   getArchivedAssetCountByUserId(@AuthUser() authUser: AuthUserDto): Promise<AssetCountByUserIdResponseDto> { | ||||
|     return this.assetService.getArchivedAssetCountByUserId(authUser); | ||||
|   } | ||||
|   /** | ||||
|    * Get all AssetEntity belong to the user | ||||
|    */ | ||||
|   | ||||
| @@ -26,7 +26,6 @@ import { CreateAssetDto } from './dto/create-asset.dto'; | ||||
| import { TimeGroupEnum } from './dto/get-asset-count-by-time-bucket.dto'; | ||||
| import { AssetRejectReason, AssetUploadAction } from './response-dto/asset-check-response.dto'; | ||||
| import { AssetCountByTimeBucket } from './response-dto/asset-count-by-time-group-response.dto'; | ||||
| import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-user-id-response.dto'; | ||||
|  | ||||
| const _getCreateAssetDto = (): CreateAssetDto => { | ||||
|   const createAssetDto = new CreateAssetDto(); | ||||
| @@ -103,24 +102,6 @@ const _getAssetCountByTimeBucket = (): AssetCountByTimeBucket[] => { | ||||
|   return [result1, result2]; | ||||
| }; | ||||
|  | ||||
| const _getAssetCountByUserId = (): AssetCountByUserIdResponseDto => { | ||||
|   const result = new AssetCountByUserIdResponseDto(); | ||||
|  | ||||
|   result.videos = 2; | ||||
|   result.photos = 2; | ||||
|  | ||||
|   return result; | ||||
| }; | ||||
|  | ||||
| const _getArchivedAssetsCountByUserId = (): AssetCountByUserIdResponseDto => { | ||||
|   const result = new AssetCountByUserIdResponseDto(); | ||||
|  | ||||
|   result.videos = 1; | ||||
|   result.photos = 2; | ||||
|  | ||||
|   return result; | ||||
| }; | ||||
|  | ||||
| const uploadFile = { | ||||
|   nullAuth: { | ||||
|     authUser: null, | ||||
| @@ -197,8 +178,6 @@ describe('AssetService', () => { | ||||
|       getSearchPropertiesByUserId: jest.fn(), | ||||
|       getAssetByTimeBucket: jest.fn(), | ||||
|       getAssetsByChecksums: jest.fn(), | ||||
|       getAssetCountByUserId: jest.fn(), | ||||
|       getArchivedAssetCountByUserId: jest.fn(), | ||||
|       getExistingAssets: jest.fn(), | ||||
|       getByOriginalPath: jest.fn(), | ||||
|     }; | ||||
| @@ -467,20 +446,6 @@ describe('AssetService', () => { | ||||
|     expect(result.buckets.length).toEqual(2); | ||||
|   }); | ||||
|  | ||||
|   it('get asset count by user id', async () => { | ||||
|     const assetCount = _getAssetCountByUserId(); | ||||
|     assetRepositoryMock.getAssetCountByUserId.mockResolvedValue(assetCount); | ||||
|  | ||||
|     await expect(sut.getAssetCountByUserId(authStub.user1)).resolves.toEqual(assetCount); | ||||
|   }); | ||||
|  | ||||
|   it('get archived asset count by user id', async () => { | ||||
|     const assetCount = _getArchivedAssetsCountByUserId(); | ||||
|     assetRepositoryMock.getArchivedAssetCountByUserId.mockResolvedValue(assetCount); | ||||
|  | ||||
|     await expect(sut.getArchivedAssetCountByUserId(authStub.user1)).resolves.toEqual(assetCount); | ||||
|   }); | ||||
|  | ||||
|   describe('deleteAll', () => { | ||||
|     it('should return failed status when an asset is missing', async () => { | ||||
|       assetRepositoryMock.get.mockResolvedValue(null); | ||||
|   | ||||
| @@ -58,7 +58,6 @@ import { | ||||
|   AssetCountByTimeBucketResponseDto, | ||||
|   mapAssetCountByTimeBucket, | ||||
| } from './response-dto/asset-count-by-time-group-response.dto'; | ||||
| import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-user-id-response.dto'; | ||||
| import { AssetFileUploadResponseDto } from './response-dto/asset-file-upload-response.dto'; | ||||
| import { CheckDuplicateAssetResponseDto } from './response-dto/check-duplicate-asset-response.dto'; | ||||
| import { CheckExistingAssetsResponseDto } from './response-dto/check-existing-assets-response.dto'; | ||||
| @@ -536,14 +535,6 @@ export class AssetService { | ||||
|     return mapAssetCountByTimeBucket(result); | ||||
|   } | ||||
|  | ||||
|   getAssetCountByUserId(authUser: AuthUserDto): Promise<AssetCountByUserIdResponseDto> { | ||||
|     return this._assetRepository.getAssetCountByUserId(authUser.id); | ||||
|   } | ||||
|  | ||||
|   getArchivedAssetCountByUserId(authUser: AuthUserDto): Promise<AssetCountByUserIdResponseDto> { | ||||
|     return this._assetRepository.getArchivedAssetCountByUserId(authUser.id); | ||||
|   } | ||||
|  | ||||
|   getExifPermission(authUser: AuthUserDto) { | ||||
|     return !authUser.isPublicUser || authUser.isShowExif; | ||||
|   } | ||||
|   | ||||
| @@ -1,18 +0,0 @@ | ||||
| import { ApiProperty } from '@nestjs/swagger'; | ||||
|  | ||||
| export class AssetCountByUserIdResponseDto { | ||||
|   @ApiProperty({ type: 'integer' }) | ||||
|   audio = 0; | ||||
|  | ||||
|   @ApiProperty({ type: 'integer' }) | ||||
|   photos = 0; | ||||
|  | ||||
|   @ApiProperty({ type: 'integer' }) | ||||
|   videos = 0; | ||||
|  | ||||
|   @ApiProperty({ type: 'integer' }) | ||||
|   other = 0; | ||||
|  | ||||
|   @ApiProperty({ type: 'integer' }) | ||||
|   total = 0; | ||||
| } | ||||
| @@ -1,6 +1,8 @@ | ||||
| import { | ||||
|   AssetIdsDto, | ||||
|   AssetService, | ||||
|   AssetStatsDto, | ||||
|   AssetStatsResponseDto, | ||||
|   AuthUserDto, | ||||
|   DownloadDto, | ||||
|   DownloadResponseDto, | ||||
| @@ -53,4 +55,9 @@ export class AssetController { | ||||
|   downloadFile(@AuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto) { | ||||
|     return this.service.downloadFile(authUser, id).then(asStreamableFile); | ||||
|   } | ||||
|  | ||||
|   @Get('statistics') | ||||
|   getAssetStats(@AuthUser() authUser: AuthUserDto, @Query() dto: AssetStatsDto): Promise<AssetStatsResponseDto> { | ||||
|     return this.service.getStatistics(authUser, dto); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| import { | ||||
|   AssetSearchOptions, | ||||
|   AssetStats, | ||||
|   AssetStatsOptions, | ||||
|   IAssetRepository, | ||||
|   LivePhotoSearchOptions, | ||||
|   MapMarker, | ||||
| @@ -321,4 +323,38 @@ export class AssetRepository implements IAssetRepository { | ||||
|       lon: asset.exifInfo!.longitude!, | ||||
|     })); | ||||
|   } | ||||
|  | ||||
|   async getStatistics(ownerId: string, options: AssetStatsOptions): Promise<AssetStats> { | ||||
|     let builder = await this.repository | ||||
|       .createQueryBuilder('asset') | ||||
|       .select(`COUNT(asset.id)`, 'count') | ||||
|       .addSelect(`asset.type`, 'type') | ||||
|       .where('"ownerId" = :ownerId', { ownerId }) | ||||
|       .andWhere('asset.isVisible = true') | ||||
|       .groupBy('asset.type'); | ||||
|  | ||||
|     const { isArchived, isFavorite } = options; | ||||
|     if (isArchived !== undefined) { | ||||
|       builder = builder.andWhere(`asset.isArchived = :isArchived`, { isArchived }); | ||||
|     } | ||||
|  | ||||
|     if (isFavorite !== undefined) { | ||||
|       builder = builder.andWhere(`asset.isFavorite = :isFavorite`, { isFavorite }); | ||||
|     } | ||||
|  | ||||
|     const items = await builder.getRawMany(); | ||||
|  | ||||
|     const result: AssetStats = { | ||||
|       [AssetType.AUDIO]: 0, | ||||
|       [AssetType.IMAGE]: 0, | ||||
|       [AssetType.VIDEO]: 0, | ||||
|       [AssetType.OTHER]: 0, | ||||
|     }; | ||||
|  | ||||
|     for (const item of items) { | ||||
|       result[item.type as AssetType] = Number(item.count) || 0; | ||||
|     } | ||||
|  | ||||
|     return result; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -18,5 +18,6 @@ export const newAssetRepositoryMock = (): jest.Mocked<IAssetRepository> => { | ||||
|     save: jest.fn(), | ||||
|     findLivePhotoMatch: jest.fn(), | ||||
|     getMapMarkers: jest.fn(), | ||||
|     getStatistics: jest.fn(), | ||||
|   }; | ||||
| }; | ||||
|   | ||||
							
								
								
									
										195
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										195
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							| @@ -486,43 +486,6 @@ export interface AssetCountByTimeBucketResponseDto { | ||||
|      */ | ||||
|     'buckets': Array<AssetCountByTimeBucket>; | ||||
| } | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
|  * @interface AssetCountByUserIdResponseDto | ||||
|  */ | ||||
| export interface AssetCountByUserIdResponseDto { | ||||
|     /** | ||||
|      *  | ||||
|      * @type {number} | ||||
|      * @memberof AssetCountByUserIdResponseDto | ||||
|      */ | ||||
|     'audio': number; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {number} | ||||
|      * @memberof AssetCountByUserIdResponseDto | ||||
|      */ | ||||
|     'photos': number; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {number} | ||||
|      * @memberof AssetCountByUserIdResponseDto | ||||
|      */ | ||||
|     'videos': number; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {number} | ||||
|      * @memberof AssetCountByUserIdResponseDto | ||||
|      */ | ||||
|     'other': number; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {number} | ||||
|      * @memberof AssetCountByUserIdResponseDto | ||||
|      */ | ||||
|     'total': number; | ||||
| } | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
| @@ -724,6 +687,31 @@ export interface AssetResponseDto { | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
|  * @interface AssetStatsResponseDto | ||||
|  */ | ||||
| export interface AssetStatsResponseDto { | ||||
|     /** | ||||
|      *  | ||||
|      * @type {number} | ||||
|      * @memberof AssetStatsResponseDto | ||||
|      */ | ||||
|     'images': number; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {number} | ||||
|      * @memberof AssetStatsResponseDto | ||||
|      */ | ||||
|     'videos': number; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {number} | ||||
|      * @memberof AssetStatsResponseDto | ||||
|      */ | ||||
|     'total': number; | ||||
| } | ||||
| /** | ||||
|  *  | ||||
|  * @export | ||||
| @@ -4901,44 +4889,6 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | ||||
| 
 | ||||
| 
 | ||||
|      | ||||
|             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||
|             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||
|             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||
| 
 | ||||
|             return { | ||||
|                 url: toPathString(localVarUrlObj), | ||||
|                 options: localVarRequestOptions, | ||||
|             }; | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         getArchivedAssetCountByUserId: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||
|             const localVarPath = `/asset/stat/archive`; | ||||
|             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||
|             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||
|             let baseOptions; | ||||
|             if (configuration) { | ||||
|                 baseOptions = configuration.baseOptions; | ||||
|             } | ||||
| 
 | ||||
|             const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; | ||||
|             const localVarHeaderParameter = {} as any; | ||||
|             const localVarQueryParameter = {} as any; | ||||
| 
 | ||||
|             // authentication cookie required
 | ||||
| 
 | ||||
|             // authentication api_key required
 | ||||
|             await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) | ||||
| 
 | ||||
|             // authentication bearer required
 | ||||
|             // http bearer authentication required
 | ||||
|             await setBearerAuthToObject(localVarHeaderParameter, configuration) | ||||
| 
 | ||||
| 
 | ||||
|      | ||||
|             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||
|             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||
|             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||
| @@ -5088,8 +5038,8 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         getAssetCountByUserId: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||
|             const localVarPath = `/asset/count-by-user-id`; | ||||
|         getAssetSearchTerms: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||
|             const localVarPath = `/asset/search-terms`; | ||||
|             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||
|             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||
|             let baseOptions; | ||||
| @@ -5123,11 +5073,13 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {boolean} [isArchived]  | ||||
|          * @param {boolean} [isFavorite]  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         getAssetSearchTerms: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||
|             const localVarPath = `/asset/search-terms`; | ||||
|         getAssetStats: async (isArchived?: boolean, isFavorite?: boolean, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||
|             const localVarPath = `/asset/statistics`; | ||||
|             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||
|             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||
|             let baseOptions; | ||||
| @@ -5148,6 +5100,14 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | ||||
|             // http bearer authentication required
 | ||||
|             await setBearerAuthToObject(localVarHeaderParameter, configuration) | ||||
| 
 | ||||
|             if (isArchived !== undefined) { | ||||
|                 localVarQueryParameter['isArchived'] = isArchived; | ||||
|             } | ||||
| 
 | ||||
|             if (isFavorite !== undefined) { | ||||
|                 localVarQueryParameter['isFavorite'] = isFavorite; | ||||
|             } | ||||
| 
 | ||||
| 
 | ||||
|      | ||||
|             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||
| @@ -5896,15 +5856,6 @@ export const AssetApiFp = function(configuration?: Configuration) { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getAllAssets(userId, isFavorite, isArchived, withoutThumbs, skip, ifNoneMatch, options); | ||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         async getArchivedAssetCountByUserId(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AssetCountByUserIdResponseDto>> { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getArchivedAssetCountByUserId(options); | ||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||
|         }, | ||||
|         /** | ||||
|          * Get a single asset\'s information | ||||
|          * @param {string} id  | ||||
| @@ -5941,17 +5892,19 @@ export const AssetApiFp = function(configuration?: Configuration) { | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         async getAssetCountByUserId(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AssetCountByUserIdResponseDto>> { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetCountByUserId(options); | ||||
|         async getAssetSearchTerms(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<string>>> { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetSearchTerms(options); | ||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {boolean} [isArchived]  | ||||
|          * @param {boolean} [isFavorite]  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         async getAssetSearchTerms(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<string>>> { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetSearchTerms(options); | ||||
|         async getAssetStats(isArchived?: boolean, isFavorite?: boolean, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AssetStatsResponseDto>> { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetStats(isArchived, isFavorite, options); | ||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||
|         }, | ||||
|         /** | ||||
| @@ -6177,14 +6130,6 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath | ||||
|         getAllAssets(userId?: string, isFavorite?: boolean, isArchived?: boolean, withoutThumbs?: boolean, skip?: number, ifNoneMatch?: string, options?: any): AxiosPromise<Array<AssetResponseDto>> { | ||||
|             return localVarFp.getAllAssets(userId, isFavorite, isArchived, withoutThumbs, skip, ifNoneMatch, options).then((request) => request(axios, basePath)); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         getArchivedAssetCountByUserId(options?: any): AxiosPromise<AssetCountByUserIdResponseDto> { | ||||
|             return localVarFp.getArchivedAssetCountByUserId(options).then((request) => request(axios, basePath)); | ||||
|         }, | ||||
|         /** | ||||
|          * Get a single asset\'s information | ||||
|          * @param {string} id  | ||||
| @@ -6218,16 +6163,18 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         getAssetCountByUserId(options?: any): AxiosPromise<AssetCountByUserIdResponseDto> { | ||||
|             return localVarFp.getAssetCountByUserId(options).then((request) => request(axios, basePath)); | ||||
|         getAssetSearchTerms(options?: any): AxiosPromise<Array<string>> { | ||||
|             return localVarFp.getAssetSearchTerms(options).then((request) => request(axios, basePath)); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
|          * @param {boolean} [isArchived]  | ||||
|          * @param {boolean} [isFavorite]  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         getAssetSearchTerms(options?: any): AxiosPromise<Array<string>> { | ||||
|             return localVarFp.getAssetSearchTerms(options).then((request) => request(axios, basePath)); | ||||
|         getAssetStats(isArchived?: boolean, isFavorite?: boolean, options?: any): AxiosPromise<AssetStatsResponseDto> { | ||||
|             return localVarFp.getAssetStats(isArchived, isFavorite, options).then((request) => request(axios, basePath)); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
| @@ -6565,6 +6512,27 @@ export interface AssetApiGetAssetCountByTimeBucketRequest { | ||||
|     readonly getAssetCountByTimeBucketDto: GetAssetCountByTimeBucketDto | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Request parameters for getAssetStats operation in AssetApi. | ||||
|  * @export | ||||
|  * @interface AssetApiGetAssetStatsRequest | ||||
|  */ | ||||
| export interface AssetApiGetAssetStatsRequest { | ||||
|     /** | ||||
|      *  | ||||
|      * @type {boolean} | ||||
|      * @memberof AssetApiGetAssetStats | ||||
|      */ | ||||
|     readonly isArchived?: boolean | ||||
| 
 | ||||
|     /** | ||||
|      *  | ||||
|      * @type {boolean} | ||||
|      * @memberof AssetApiGetAssetStats | ||||
|      */ | ||||
|     readonly isFavorite?: boolean | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Request parameters for getAssetThumbnail operation in AssetApi. | ||||
|  * @export | ||||
| @@ -6957,16 +6925,6 @@ export class AssetApi extends BaseAPI { | ||||
|         return AssetApiFp(this.configuration).getAllAssets(requestParameters.userId, requestParameters.isFavorite, requestParameters.isArchived, requestParameters.withoutThumbs, requestParameters.skip, requestParameters.ifNoneMatch, options).then((request) => request(this.axios, this.basePath)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      *  | ||||
|      * @param {*} [options] Override http request option. | ||||
|      * @throws {RequiredError} | ||||
|      * @memberof AssetApi | ||||
|      */ | ||||
|     public getArchivedAssetCountByUserId(options?: AxiosRequestConfig) { | ||||
|         return AssetApiFp(this.configuration).getArchivedAssetCountByUserId(options).then((request) => request(this.axios, this.basePath)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get a single asset\'s information | ||||
|      * @param {AssetApiGetAssetByIdRequest} requestParameters Request parameters. | ||||
| @@ -7006,18 +6964,19 @@ export class AssetApi extends BaseAPI { | ||||
|      * @throws {RequiredError} | ||||
|      * @memberof AssetApi | ||||
|      */ | ||||
|     public getAssetCountByUserId(options?: AxiosRequestConfig) { | ||||
|         return AssetApiFp(this.configuration).getAssetCountByUserId(options).then((request) => request(this.axios, this.basePath)); | ||||
|     public getAssetSearchTerms(options?: AxiosRequestConfig) { | ||||
|         return AssetApiFp(this.configuration).getAssetSearchTerms(options).then((request) => request(this.axios, this.basePath)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      *  | ||||
|      * @param {AssetApiGetAssetStatsRequest} requestParameters Request parameters. | ||||
|      * @param {*} [options] Override http request option. | ||||
|      * @throws {RequiredError} | ||||
|      * @memberof AssetApi | ||||
|      */ | ||||
|     public getAssetSearchTerms(options?: AxiosRequestConfig) { | ||||
|         return AssetApiFp(this.configuration).getAssetSearchTerms(options).then((request) => request(this.axios, this.basePath)); | ||||
|     public getAssetStats(requestParameters: AssetApiGetAssetStatsRequest = {}, options?: AxiosRequestConfig) { | ||||
|         return AssetApiFp(this.configuration).getAssetStats(requestParameters.isArchived, requestParameters.isFavorite, options).then((request) => request(this.axios, this.basePath)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| <script lang="ts"> | ||||
|   import { page } from '$app/stores'; | ||||
|   import { api } from '@api'; | ||||
|   import { AssetApiGetAssetStatsRequest, api } from '@api'; | ||||
|   import AccountMultipleOutline from 'svelte-material-icons/AccountMultipleOutline.svelte'; | ||||
|   import AccountMultiple from 'svelte-material-icons/AccountMultiple.svelte'; | ||||
|   import ImageAlbum from 'svelte-material-icons/ImageAlbum.svelte'; | ||||
| @@ -18,31 +18,9 @@ | ||||
|   import { locale } from '$lib/stores/preferences.store'; | ||||
|   import SideBarSection from './side-bar-section.svelte'; | ||||
|  | ||||
|   const getAssetCount = async () => { | ||||
|     const { data: allAssetCount } = await api.assetApi.getAssetCountByUserId(); | ||||
|     const { data: archivedCount } = await api.assetApi.getArchivedAssetCountByUserId(); | ||||
|  | ||||
|     return { | ||||
|       videos: allAssetCount.videos - archivedCount.videos, | ||||
|       photos: allAssetCount.photos - archivedCount.photos, | ||||
|     }; | ||||
|   }; | ||||
|  | ||||
|   const getFavoriteCount = async () => { | ||||
|     try { | ||||
|       const { data: assets } = await api.assetApi.getAllAssets({ | ||||
|         isFavorite: true, | ||||
|         withoutThumbs: true, | ||||
|       }); | ||||
|  | ||||
|       return { | ||||
|         favorites: assets.length, | ||||
|       }; | ||||
|     } catch { | ||||
|       return { | ||||
|         favorites: 0, | ||||
|       }; | ||||
|     } | ||||
|   const getStats = async (dto: AssetApiGetAssetStatsRequest) => { | ||||
|     const { data: stats } = await api.assetApi.getAssetStats(dto); | ||||
|     return stats; | ||||
|   }; | ||||
|  | ||||
|   const getAlbumCount = async () => { | ||||
| @@ -54,22 +32,6 @@ | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const getArchivedAssetsCount = async () => { | ||||
|     try { | ||||
|       const { data: assetCount } = await api.assetApi.getArchivedAssetCountByUserId(); | ||||
|  | ||||
|       return { | ||||
|         videos: assetCount.videos, | ||||
|         photos: assetCount.photos, | ||||
|       }; | ||||
|     } catch { | ||||
|       return { | ||||
|         videos: 0, | ||||
|         photos: 0, | ||||
|       }; | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const isFavoritesSelected = $page.route.id === '/(user)/favorites'; | ||||
|   const isPhotosSelected = $page.route.id === '/(user)/photos'; | ||||
|   const isSharingSelected = $page.route.id === '/(user)/sharing'; | ||||
| @@ -83,12 +45,12 @@ | ||||
|       isSelected={isPhotosSelected} | ||||
|     > | ||||
|       <svelte:fragment slot="moreInformation"> | ||||
|         {#await getAssetCount()} | ||||
|         {#await getStats({ isArchived: false })} | ||||
|           <LoadingSpinner /> | ||||
|         {:then data} | ||||
|           <div> | ||||
|             <p>{data.videos.toLocaleString($locale)} Videos</p> | ||||
|             <p>{data.photos.toLocaleString($locale)} Photos</p> | ||||
|             <p>{data.images.toLocaleString($locale)} Photos</p> | ||||
|           </div> | ||||
|         {/await} | ||||
|       </svelte:fragment> | ||||
| @@ -129,11 +91,12 @@ | ||||
|       isSelected={isFavoritesSelected} | ||||
|     > | ||||
|       <svelte:fragment slot="moreInformation"> | ||||
|         {#await getFavoriteCount()} | ||||
|         {#await getStats({ isFavorite: true })} | ||||
|           <LoadingSpinner /> | ||||
|         {:then data} | ||||
|           <div> | ||||
|             <p>{data.favorites} Favorites</p> | ||||
|             <p>{data.videos.toLocaleString($locale)} Videos</p> | ||||
|             <p>{data.images.toLocaleString($locale)} Photos</p> | ||||
|           </div> | ||||
|         {/await} | ||||
|       </svelte:fragment> | ||||
| @@ -155,12 +118,12 @@ | ||||
|   <a data-sveltekit-preload-data="hover" href={AppRoute.ARCHIVE} draggable="false"> | ||||
|     <SideBarButton title="Archive" logo={ArchiveArrowDownOutline} isSelected={$page.route.id === '/(user)/archive'}> | ||||
|       <svelte:fragment slot="moreInformation"> | ||||
|         {#await getArchivedAssetsCount()} | ||||
|         {#await getStats({ isArchived: true })} | ||||
|           <LoadingSpinner /> | ||||
|         {:then data} | ||||
|           <div> | ||||
|             <p>{data.videos.toLocaleString($locale)} Videos</p> | ||||
|             <p>{data.photos.toLocaleString($locale)} Photos</p> | ||||
|             <p>{data.images.toLocaleString($locale)} Photos</p> | ||||
|           </div> | ||||
|         {/await} | ||||
|       </svelte:fragment> | ||||
|   | ||||
| @@ -10,22 +10,22 @@ | ||||
|   import AssetGrid from '$lib/components/photos-page/asset-grid.svelte'; | ||||
|   import AssetSelectContextMenu from '$lib/components/photos-page/asset-select-context-menu.svelte'; | ||||
|   import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte'; | ||||
|   import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte'; | ||||
|   import { assetInteractionStore, isMultiSelectStoreState, selectedAssets } from '$lib/stores/asset-interaction.store'; | ||||
|   import { assetStore } from '$lib/stores/assets.store'; | ||||
|   import { openFileUploadDialog } from '$lib/utils/file-uploader'; | ||||
|   import { api } from '@api'; | ||||
|   import { onDestroy, onMount } from 'svelte'; | ||||
|   import DotsVertical from 'svelte-material-icons/DotsVertical.svelte'; | ||||
|   import Plus from 'svelte-material-icons/Plus.svelte'; | ||||
|   import type { PageData } from './$types'; | ||||
|   import { api } from '@api'; | ||||
|   import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte'; | ||||
|   import { openFileUploadDialog } from '$lib/utils/file-uploader'; | ||||
|  | ||||
|   export let data: PageData; | ||||
|   let assetCount = 1; | ||||
|  | ||||
|   onMount(async () => { | ||||
|     const { data: allAssetCount } = await api.assetApi.getAssetCountByUserId(); | ||||
|     assetCount = allAssetCount.total; | ||||
|     const { data: stats } = await api.assetApi.getAssetStats(); | ||||
|     assetCount = stats.total; | ||||
|   }); | ||||
|  | ||||
|   onDestroy(() => { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user