mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-03 19:29:32 -05:00 
			
		
		
		
	refactor(server): asset stats (#3253)
* refactor(server): asset stats * chore: open api
This commit is contained in:
		
							parent
							
								
									05e1a6d949
								
							
						
					
					
						commit
						f952bc0b64
					
				
							
								
								
									
										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(() => {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user