diff --git a/cli/src/api/open-api/api.ts b/cli/src/api/open-api/api.ts index ecb0fb39e..3828babd7 100644 --- a/cli/src/api/open-api/api.ts +++ b/cli/src/api/open-api/api.ts @@ -670,6 +670,20 @@ export interface AssetJobsDto { } +/** + * + * @export + * @enum {string} + */ + +export const AssetOrder = { + Asc: 'asc', + Desc: 'desc' +} as const; + +export type AssetOrder = typeof AssetOrder[keyof typeof AssetOrder]; + + /** * * @export @@ -7822,6 +7836,260 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration options: localVarRequestOptions, }; }, + /** + * + * @param {string} [id] + * @param {string} [libraryId] + * @param {AssetTypeEnum} [type] + * @param {AssetOrder} [order] + * @param {string} [deviceAssetId] + * @param {string} [deviceId] + * @param {string} [checksum] + * @param {boolean} [isArchived] + * @param {boolean} [isEncoded] + * @param {boolean} [isExternal] + * @param {boolean} [isFavorite] + * @param {boolean} [isMotion] + * @param {boolean} [isOffline] + * @param {boolean} [isReadOnly] + * @param {boolean} [isVisible] + * @param {boolean} [withDeleted] + * @param {boolean} [withStacked] + * @param {boolean} [withExif] + * @param {boolean} [withPeople] + * @param {string} [createdBefore] + * @param {string} [createdAfter] + * @param {string} [updatedBefore] + * @param {string} [updatedAfter] + * @param {string} [trashedBefore] + * @param {string} [trashedAfter] + * @param {string} [takenBefore] + * @param {string} [takenAfter] + * @param {string} [originalFileName] + * @param {string} [originalPath] + * @param {string} [resizePath] + * @param {string} [webpPath] + * @param {string} [encodedVideoPath] + * @param {string} [city] + * @param {string} [state] + * @param {string} [country] + * @param {string} [make] + * @param {string} [model] + * @param {string} [lensModel] + * @param {number} [page] + * @param {number} [size] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + searchAssets: async (id?: string, libraryId?: string, type?: AssetTypeEnum, order?: AssetOrder, deviceAssetId?: string, deviceId?: string, checksum?: string, isArchived?: boolean, isEncoded?: boolean, isExternal?: boolean, isFavorite?: boolean, isMotion?: boolean, isOffline?: boolean, isReadOnly?: boolean, isVisible?: boolean, withDeleted?: boolean, withStacked?: boolean, withExif?: boolean, withPeople?: boolean, createdBefore?: string, createdAfter?: string, updatedBefore?: string, updatedAfter?: string, trashedBefore?: string, trashedAfter?: string, takenBefore?: string, takenAfter?: string, originalFileName?: string, originalPath?: string, resizePath?: string, webpPath?: string, encodedVideoPath?: string, city?: string, state?: string, country?: string, make?: string, model?: string, lensModel?: string, page?: number, size?: number, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/assets`; + // 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) + + if (id !== undefined) { + localVarQueryParameter['id'] = id; + } + + if (libraryId !== undefined) { + localVarQueryParameter['libraryId'] = libraryId; + } + + if (type !== undefined) { + localVarQueryParameter['type'] = type; + } + + if (order !== undefined) { + localVarQueryParameter['order'] = order; + } + + if (deviceAssetId !== undefined) { + localVarQueryParameter['deviceAssetId'] = deviceAssetId; + } + + if (deviceId !== undefined) { + localVarQueryParameter['deviceId'] = deviceId; + } + + if (checksum !== undefined) { + localVarQueryParameter['checksum'] = checksum; + } + + if (isArchived !== undefined) { + localVarQueryParameter['isArchived'] = isArchived; + } + + if (isEncoded !== undefined) { + localVarQueryParameter['isEncoded'] = isEncoded; + } + + if (isExternal !== undefined) { + localVarQueryParameter['isExternal'] = isExternal; + } + + if (isFavorite !== undefined) { + localVarQueryParameter['isFavorite'] = isFavorite; + } + + if (isMotion !== undefined) { + localVarQueryParameter['isMotion'] = isMotion; + } + + if (isOffline !== undefined) { + localVarQueryParameter['isOffline'] = isOffline; + } + + if (isReadOnly !== undefined) { + localVarQueryParameter['isReadOnly'] = isReadOnly; + } + + if (isVisible !== undefined) { + localVarQueryParameter['isVisible'] = isVisible; + } + + if (withDeleted !== undefined) { + localVarQueryParameter['withDeleted'] = withDeleted; + } + + if (withStacked !== undefined) { + localVarQueryParameter['withStacked'] = withStacked; + } + + if (withExif !== undefined) { + localVarQueryParameter['withExif'] = withExif; + } + + if (withPeople !== undefined) { + localVarQueryParameter['withPeople'] = withPeople; + } + + if (createdBefore !== undefined) { + localVarQueryParameter['createdBefore'] = (createdBefore as any instanceof Date) ? + (createdBefore as any).toISOString() : + createdBefore; + } + + if (createdAfter !== undefined) { + localVarQueryParameter['createdAfter'] = (createdAfter as any instanceof Date) ? + (createdAfter as any).toISOString() : + createdAfter; + } + + if (updatedBefore !== undefined) { + localVarQueryParameter['updatedBefore'] = (updatedBefore as any instanceof Date) ? + (updatedBefore as any).toISOString() : + updatedBefore; + } + + if (updatedAfter !== undefined) { + localVarQueryParameter['updatedAfter'] = (updatedAfter as any instanceof Date) ? + (updatedAfter as any).toISOString() : + updatedAfter; + } + + if (trashedBefore !== undefined) { + localVarQueryParameter['trashedBefore'] = (trashedBefore as any instanceof Date) ? + (trashedBefore as any).toISOString() : + trashedBefore; + } + + if (trashedAfter !== undefined) { + localVarQueryParameter['trashedAfter'] = (trashedAfter as any instanceof Date) ? + (trashedAfter as any).toISOString() : + trashedAfter; + } + + if (takenBefore !== undefined) { + localVarQueryParameter['takenBefore'] = (takenBefore as any instanceof Date) ? + (takenBefore as any).toISOString() : + takenBefore; + } + + if (takenAfter !== undefined) { + localVarQueryParameter['takenAfter'] = (takenAfter as any instanceof Date) ? + (takenAfter as any).toISOString() : + takenAfter; + } + + if (originalFileName !== undefined) { + localVarQueryParameter['originalFileName'] = originalFileName; + } + + if (originalPath !== undefined) { + localVarQueryParameter['originalPath'] = originalPath; + } + + if (resizePath !== undefined) { + localVarQueryParameter['resizePath'] = resizePath; + } + + if (webpPath !== undefined) { + localVarQueryParameter['webpPath'] = webpPath; + } + + if (encodedVideoPath !== undefined) { + localVarQueryParameter['encodedVideoPath'] = encodedVideoPath; + } + + if (city !== undefined) { + localVarQueryParameter['city'] = city; + } + + if (state !== undefined) { + localVarQueryParameter['state'] = state; + } + + if (country !== undefined) { + localVarQueryParameter['country'] = country; + } + + if (make !== undefined) { + localVarQueryParameter['make'] = make; + } + + if (model !== undefined) { + localVarQueryParameter['model'] = model; + } + + if (lensModel !== undefined) { + localVarQueryParameter['lensModel'] = lensModel; + } + + if (page !== undefined) { + localVarQueryParameter['page'] = page; + } + + if (size !== undefined) { + localVarQueryParameter['size'] = size; + } + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, /** * * @param {string} id @@ -8440,6 +8708,55 @@ export const AssetApiFp = function(configuration?: Configuration) { const localVarAxiosArgs = await localVarAxiosParamCreator.searchAsset(searchAssetDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, + /** + * + * @param {string} [id] + * @param {string} [libraryId] + * @param {AssetTypeEnum} [type] + * @param {AssetOrder} [order] + * @param {string} [deviceAssetId] + * @param {string} [deviceId] + * @param {string} [checksum] + * @param {boolean} [isArchived] + * @param {boolean} [isEncoded] + * @param {boolean} [isExternal] + * @param {boolean} [isFavorite] + * @param {boolean} [isMotion] + * @param {boolean} [isOffline] + * @param {boolean} [isReadOnly] + * @param {boolean} [isVisible] + * @param {boolean} [withDeleted] + * @param {boolean} [withStacked] + * @param {boolean} [withExif] + * @param {boolean} [withPeople] + * @param {string} [createdBefore] + * @param {string} [createdAfter] + * @param {string} [updatedBefore] + * @param {string} [updatedAfter] + * @param {string} [trashedBefore] + * @param {string} [trashedAfter] + * @param {string} [takenBefore] + * @param {string} [takenAfter] + * @param {string} [originalFileName] + * @param {string} [originalPath] + * @param {string} [resizePath] + * @param {string} [webpPath] + * @param {string} [encodedVideoPath] + * @param {string} [city] + * @param {string} [state] + * @param {string} [country] + * @param {string} [make] + * @param {string} [model] + * @param {string} [lensModel] + * @param {number} [page] + * @param {number} [size] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async searchAssets(id?: string, libraryId?: string, type?: AssetTypeEnum, order?: AssetOrder, deviceAssetId?: string, deviceId?: string, checksum?: string, isArchived?: boolean, isEncoded?: boolean, isExternal?: boolean, isFavorite?: boolean, isMotion?: boolean, isOffline?: boolean, isReadOnly?: boolean, isVisible?: boolean, withDeleted?: boolean, withStacked?: boolean, withExif?: boolean, withPeople?: boolean, createdBefore?: string, createdAfter?: string, updatedBefore?: string, updatedAfter?: string, trashedBefore?: string, trashedAfter?: string, takenBefore?: string, takenAfter?: string, originalFileName?: string, originalPath?: string, resizePath?: string, webpPath?: string, encodedVideoPath?: string, city?: string, state?: string, country?: string, make?: string, model?: string, lensModel?: string, page?: number, size?: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.searchAssets(id, libraryId, type, order, deviceAssetId, deviceId, checksum, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, withDeleted, withStacked, withExif, withPeople, createdBefore, createdAfter, updatedBefore, updatedAfter, trashedBefore, trashedAfter, takenBefore, takenAfter, originalFileName, originalPath, resizePath, webpPath, encodedVideoPath, city, state, country, make, model, lensModel, page, size, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, /** * * @param {string} id @@ -8739,6 +9056,15 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath searchAsset(requestParameters: AssetApiSearchAssetRequest, options?: AxiosRequestConfig): AxiosPromise> { return localVarFp.searchAsset(requestParameters.searchAssetDto, options).then((request) => request(axios, basePath)); }, + /** + * + * @param {AssetApiSearchAssetsRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + searchAssets(requestParameters: AssetApiSearchAssetsRequest = {}, options?: AxiosRequestConfig): AxiosPromise> { + return localVarFp.searchAssets(requestParameters.id, requestParameters.libraryId, requestParameters.type, requestParameters.order, requestParameters.deviceAssetId, requestParameters.deviceId, requestParameters.checksum, requestParameters.isArchived, requestParameters.isEncoded, requestParameters.isExternal, requestParameters.isFavorite, requestParameters.isMotion, requestParameters.isOffline, requestParameters.isReadOnly, requestParameters.isVisible, requestParameters.withDeleted, requestParameters.withStacked, requestParameters.withExif, requestParameters.withPeople, requestParameters.createdBefore, requestParameters.createdAfter, requestParameters.updatedBefore, requestParameters.updatedAfter, requestParameters.trashedBefore, requestParameters.trashedAfter, requestParameters.takenBefore, requestParameters.takenAfter, requestParameters.originalFileName, requestParameters.originalPath, requestParameters.resizePath, requestParameters.webpPath, requestParameters.encodedVideoPath, requestParameters.city, requestParameters.state, requestParameters.country, requestParameters.make, requestParameters.model, requestParameters.lensModel, requestParameters.page, requestParameters.size, options).then((request) => request(axios, basePath)); + }, /** * * @param {AssetApiServeFileRequest} requestParameters Request parameters. @@ -9333,6 +9659,293 @@ export interface AssetApiSearchAssetRequest { readonly searchAssetDto: SearchAssetDto } +/** + * Request parameters for searchAssets operation in AssetApi. + * @export + * @interface AssetApiSearchAssetsRequest + */ +export interface AssetApiSearchAssetsRequest { + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly id?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly libraryId?: string + + /** + * + * @type {AssetTypeEnum} + * @memberof AssetApiSearchAssets + */ + readonly type?: AssetTypeEnum + + /** + * + * @type {AssetOrder} + * @memberof AssetApiSearchAssets + */ + readonly order?: AssetOrder + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly deviceAssetId?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly deviceId?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly checksum?: string + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly isArchived?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly isEncoded?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly isExternal?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly isFavorite?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly isMotion?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly isOffline?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly isReadOnly?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly isVisible?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly withDeleted?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly withStacked?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly withExif?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly withPeople?: boolean + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly createdBefore?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly createdAfter?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly updatedBefore?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly updatedAfter?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly trashedBefore?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly trashedAfter?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly takenBefore?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly takenAfter?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly originalFileName?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly originalPath?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly resizePath?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly webpPath?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly encodedVideoPath?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly city?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly state?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly country?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly make?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly model?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly lensModel?: string + + /** + * + * @type {number} + * @memberof AssetApiSearchAssets + */ + readonly page?: number + + /** + * + * @type {number} + * @memberof AssetApiSearchAssets + */ + readonly size?: number +} + /** * Request parameters for serveFile operation in AssetApi. * @export @@ -9813,6 +10426,17 @@ export class AssetApi extends BaseAPI { return AssetApiFp(this.configuration).searchAsset(requestParameters.searchAssetDto, options).then((request) => request(this.axios, this.basePath)); } + /** + * + * @param {AssetApiSearchAssetsRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof AssetApi + */ + public searchAssets(requestParameters: AssetApiSearchAssetsRequest = {}, options?: AxiosRequestConfig) { + return AssetApiFp(this.configuration).searchAssets(requestParameters.id, requestParameters.libraryId, requestParameters.type, requestParameters.order, requestParameters.deviceAssetId, requestParameters.deviceId, requestParameters.checksum, requestParameters.isArchived, requestParameters.isEncoded, requestParameters.isExternal, requestParameters.isFavorite, requestParameters.isMotion, requestParameters.isOffline, requestParameters.isReadOnly, requestParameters.isVisible, requestParameters.withDeleted, requestParameters.withStacked, requestParameters.withExif, requestParameters.withPeople, requestParameters.createdBefore, requestParameters.createdAfter, requestParameters.updatedBefore, requestParameters.updatedAfter, requestParameters.trashedBefore, requestParameters.trashedAfter, requestParameters.takenBefore, requestParameters.takenAfter, requestParameters.originalFileName, requestParameters.originalPath, requestParameters.resizePath, requestParameters.webpPath, requestParameters.encodedVideoPath, requestParameters.city, requestParameters.state, requestParameters.country, requestParameters.make, requestParameters.model, requestParameters.lensModel, requestParameters.page, requestParameters.size, options).then((request) => request(this.axios, this.basePath)); + } + /** * * @param {AssetApiServeFileRequest} requestParameters Request parameters. diff --git a/mobile/openapi/.openapi-generator/FILES b/mobile/openapi/.openapi-generator/FILES index 9ca833516..8545c35ad 100644 --- a/mobile/openapi/.openapi-generator/FILES +++ b/mobile/openapi/.openapi-generator/FILES @@ -29,6 +29,7 @@ doc/AssetIdsDto.md doc/AssetIdsResponseDto.md doc/AssetJobName.md doc/AssetJobsDto.md +doc/AssetOrder.md doc/AssetResponseDto.md doc/AssetStatsResponseDto.md doc/AssetTypeEnum.md @@ -220,6 +221,7 @@ lib/model/asset_ids_dto.dart lib/model/asset_ids_response_dto.dart lib/model/asset_job_name.dart lib/model/asset_jobs_dto.dart +lib/model/asset_order.dart lib/model/asset_response_dto.dart lib/model/asset_stats_response_dto.dart lib/model/asset_type_enum.dart @@ -376,6 +378,7 @@ test/asset_ids_dto_test.dart test/asset_ids_response_dto_test.dart test/asset_job_name_test.dart test/asset_jobs_dto_test.dart +test/asset_order_test.dart test/asset_response_dto_test.dart test/asset_stats_response_dto_test.dart test/asset_type_enum_test.dart diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 02dc534b0..272c1742e 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -116,6 +116,7 @@ Class | Method | HTTP request | Description *AssetApi* | [**restoreTrash**](doc//AssetApi.md#restoretrash) | **POST** /asset/trash/restore | *AssetApi* | [**runAssetJobs**](doc//AssetApi.md#runassetjobs) | **POST** /asset/jobs | *AssetApi* | [**searchAsset**](doc//AssetApi.md#searchasset) | **POST** /asset/search | +*AssetApi* | [**searchAssets**](doc//AssetApi.md#searchassets) | **GET** /assets | *AssetApi* | [**serveFile**](doc//AssetApi.md#servefile) | **GET** /asset/file/{id} | *AssetApi* | [**updateAsset**](doc//AssetApi.md#updateasset) | **PUT** /asset/{id} | *AssetApi* | [**updateAssets**](doc//AssetApi.md#updateassets) | **PUT** /asset | @@ -229,6 +230,7 @@ Class | Method | HTTP request | Description - [AssetIdsResponseDto](doc//AssetIdsResponseDto.md) - [AssetJobName](doc//AssetJobName.md) - [AssetJobsDto](doc//AssetJobsDto.md) + - [AssetOrder](doc//AssetOrder.md) - [AssetResponseDto](doc//AssetResponseDto.md) - [AssetStatsResponseDto](doc//AssetStatsResponseDto.md) - [AssetTypeEnum](doc//AssetTypeEnum.md) diff --git a/mobile/openapi/doc/AssetApi.md b/mobile/openapi/doc/AssetApi.md index 2938a075b..8b505994e 100644 --- a/mobile/openapi/doc/AssetApi.md +++ b/mobile/openapi/doc/AssetApi.md @@ -34,6 +34,7 @@ Method | HTTP request | Description [**restoreTrash**](AssetApi.md#restoretrash) | **POST** /asset/trash/restore | [**runAssetJobs**](AssetApi.md#runassetjobs) | **POST** /asset/jobs | [**searchAsset**](AssetApi.md#searchasset) | **POST** /asset/search | +[**searchAssets**](AssetApi.md#searchassets) | **GET** /assets | [**serveFile**](AssetApi.md#servefile) | **GET** /asset/file/{id} | [**updateAsset**](AssetApi.md#updateasset) | **PUT** /asset/{id} | [**updateAssets**](AssetApi.md#updateassets) | **PUT** /asset | @@ -1477,6 +1478,139 @@ 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) +# **searchAssets** +> List searchAssets(id, libraryId, type, order, deviceAssetId, deviceId, checksum, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, withDeleted, withStacked, withExif, withPeople, createdBefore, createdAfter, updatedBefore, updatedAfter, trashedBefore, trashedAfter, takenBefore, takenAfter, originalFileName, originalPath, resizePath, webpPath, encodedVideoPath, city, state, country, make, model, lensModel, page, size) + + + +### Example +```dart +import 'package:openapi/api.dart'; +// TODO Configure API key authorization: cookie +//defaultApiClient.getAuthentication('cookie').apiKey = 'YOUR_API_KEY'; +// uncomment below to setup prefix (e.g. Bearer) for API key, if needed +//defaultApiClient.getAuthentication('cookie').apiKeyPrefix = 'Bearer'; +// TODO Configure API key authorization: api_key +//defaultApiClient.getAuthentication('api_key').apiKey = 'YOUR_API_KEY'; +// uncomment below to setup prefix (e.g. Bearer) for API key, if needed +//defaultApiClient.getAuthentication('api_key').apiKeyPrefix = 'Bearer'; +// TODO Configure HTTP Bearer authorization: bearer +// Case 1. Use String Token +//defaultApiClient.getAuthentication('bearer').setAccessToken('YOUR_ACCESS_TOKEN'); +// Case 2. Use Function which generate token. +// String yourTokenGeneratorFunction() { ... } +//defaultApiClient.getAuthentication('bearer').setAccessToken(yourTokenGeneratorFunction); + +final api_instance = AssetApi(); +final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | +final libraryId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | +final type = ; // AssetTypeEnum | +final order = ; // AssetOrder | +final deviceAssetId = deviceAssetId_example; // String | +final deviceId = deviceId_example; // String | +final checksum = checksum_example; // String | +final isArchived = true; // bool | +final isEncoded = true; // bool | +final isExternal = true; // bool | +final isFavorite = true; // bool | +final isMotion = true; // bool | +final isOffline = true; // bool | +final isReadOnly = true; // bool | +final isVisible = true; // bool | +final withDeleted = true; // bool | +final withStacked = true; // bool | +final withExif = true; // bool | +final withPeople = true; // bool | +final createdBefore = 2013-10-20T19:20:30+01:00; // DateTime | +final createdAfter = 2013-10-20T19:20:30+01:00; // DateTime | +final updatedBefore = 2013-10-20T19:20:30+01:00; // DateTime | +final updatedAfter = 2013-10-20T19:20:30+01:00; // DateTime | +final trashedBefore = 2013-10-20T19:20:30+01:00; // DateTime | +final trashedAfter = 2013-10-20T19:20:30+01:00; // DateTime | +final takenBefore = 2013-10-20T19:20:30+01:00; // DateTime | +final takenAfter = 2013-10-20T19:20:30+01:00; // DateTime | +final originalFileName = originalFileName_example; // String | +final originalPath = originalPath_example; // String | +final resizePath = resizePath_example; // String | +final webpPath = webpPath_example; // String | +final encodedVideoPath = encodedVideoPath_example; // String | +final city = city_example; // String | +final state = state_example; // String | +final country = country_example; // String | +final make = make_example; // String | +final model = model_example; // String | +final lensModel = lensModel_example; // String | +final page = 8.14; // num | +final size = 8.14; // num | + +try { + final result = api_instance.searchAssets(id, libraryId, type, order, deviceAssetId, deviceId, checksum, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, withDeleted, withStacked, withExif, withPeople, createdBefore, createdAfter, updatedBefore, updatedAfter, trashedBefore, trashedAfter, takenBefore, takenAfter, originalFileName, originalPath, resizePath, webpPath, encodedVideoPath, city, state, country, make, model, lensModel, page, size); + print(result); +} catch (e) { + print('Exception when calling AssetApi->searchAssets: $e\n'); +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **id** | **String**| | [optional] + **libraryId** | **String**| | [optional] + **type** | [**AssetTypeEnum**](.md)| | [optional] + **order** | [**AssetOrder**](.md)| | [optional] + **deviceAssetId** | **String**| | [optional] + **deviceId** | **String**| | [optional] + **checksum** | **String**| | [optional] + **isArchived** | **bool**| | [optional] + **isEncoded** | **bool**| | [optional] + **isExternal** | **bool**| | [optional] + **isFavorite** | **bool**| | [optional] + **isMotion** | **bool**| | [optional] + **isOffline** | **bool**| | [optional] + **isReadOnly** | **bool**| | [optional] + **isVisible** | **bool**| | [optional] + **withDeleted** | **bool**| | [optional] + **withStacked** | **bool**| | [optional] + **withExif** | **bool**| | [optional] + **withPeople** | **bool**| | [optional] + **createdBefore** | **DateTime**| | [optional] + **createdAfter** | **DateTime**| | [optional] + **updatedBefore** | **DateTime**| | [optional] + **updatedAfter** | **DateTime**| | [optional] + **trashedBefore** | **DateTime**| | [optional] + **trashedAfter** | **DateTime**| | [optional] + **takenBefore** | **DateTime**| | [optional] + **takenAfter** | **DateTime**| | [optional] + **originalFileName** | **String**| | [optional] + **originalPath** | **String**| | [optional] + **resizePath** | **String**| | [optional] + **webpPath** | **String**| | [optional] + **encodedVideoPath** | **String**| | [optional] + **city** | **String**| | [optional] + **state** | **String**| | [optional] + **country** | **String**| | [optional] + **make** | **String**| | [optional] + **model** | **String**| | [optional] + **lensModel** | **String**| | [optional] + **page** | **num**| | [optional] + **size** | **num**| | [optional] + +### Return type + +[**List**](AssetResponseDto.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) + # **serveFile** > MultipartFile serveFile(id, isThumb, isWeb, key) diff --git a/mobile/openapi/doc/AssetOrder.md b/mobile/openapi/doc/AssetOrder.md new file mode 100644 index 000000000..d1460775e --- /dev/null +++ b/mobile/openapi/doc/AssetOrder.md @@ -0,0 +1,14 @@ +# openapi.model.AssetOrder + +## Load the model package +```dart +import 'package:openapi/api.dart'; +``` + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index 92bfa3f81..73a83263b 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -68,6 +68,7 @@ part 'model/asset_ids_dto.dart'; part 'model/asset_ids_response_dto.dart'; part 'model/asset_job_name.dart'; part 'model/asset_jobs_dto.dart'; +part 'model/asset_order.dart'; part 'model/asset_response_dto.dart'; part 'model/asset_stats_response_dto.dart'; part 'model/asset_type_enum.dart'; diff --git a/mobile/openapi/lib/api/asset_api.dart b/mobile/openapi/lib/api/asset_api.dart index 429a320c2..426c25b46 100644 --- a/mobile/openapi/lib/api/asset_api.dart +++ b/mobile/openapi/lib/api/asset_api.dart @@ -1475,6 +1475,333 @@ class AssetApi { return null; } + /// Performs an HTTP 'GET /assets' operation and returns the [Response]. + /// Parameters: + /// + /// * [String] id: + /// + /// * [String] libraryId: + /// + /// * [AssetTypeEnum] type: + /// + /// * [AssetOrder] order: + /// + /// * [String] deviceAssetId: + /// + /// * [String] deviceId: + /// + /// * [String] checksum: + /// + /// * [bool] isArchived: + /// + /// * [bool] isEncoded: + /// + /// * [bool] isExternal: + /// + /// * [bool] isFavorite: + /// + /// * [bool] isMotion: + /// + /// * [bool] isOffline: + /// + /// * [bool] isReadOnly: + /// + /// * [bool] isVisible: + /// + /// * [bool] withDeleted: + /// + /// * [bool] withStacked: + /// + /// * [bool] withExif: + /// + /// * [bool] withPeople: + /// + /// * [DateTime] createdBefore: + /// + /// * [DateTime] createdAfter: + /// + /// * [DateTime] updatedBefore: + /// + /// * [DateTime] updatedAfter: + /// + /// * [DateTime] trashedBefore: + /// + /// * [DateTime] trashedAfter: + /// + /// * [DateTime] takenBefore: + /// + /// * [DateTime] takenAfter: + /// + /// * [String] originalFileName: + /// + /// * [String] originalPath: + /// + /// * [String] resizePath: + /// + /// * [String] webpPath: + /// + /// * [String] encodedVideoPath: + /// + /// * [String] city: + /// + /// * [String] state: + /// + /// * [String] country: + /// + /// * [String] make: + /// + /// * [String] model: + /// + /// * [String] lensModel: + /// + /// * [num] page: + /// + /// * [num] size: + Future searchAssetsWithHttpInfo({ String? id, String? libraryId, AssetTypeEnum? type, AssetOrder? order, String? deviceAssetId, String? deviceId, String? checksum, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isOffline, bool? isReadOnly, bool? isVisible, bool? withDeleted, bool? withStacked, bool? withExif, bool? withPeople, DateTime? createdBefore, DateTime? createdAfter, DateTime? updatedBefore, DateTime? updatedAfter, DateTime? trashedBefore, DateTime? trashedAfter, DateTime? takenBefore, DateTime? takenAfter, String? originalFileName, String? originalPath, String? resizePath, String? webpPath, String? encodedVideoPath, String? city, String? state, String? country, String? make, String? model, String? lensModel, num? page, num? size, }) async { + // ignore: prefer_const_declarations + final path = r'/assets'; + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + if (id != null) { + queryParams.addAll(_queryParams('', 'id', id)); + } + if (libraryId != null) { + queryParams.addAll(_queryParams('', 'libraryId', libraryId)); + } + if (type != null) { + queryParams.addAll(_queryParams('', 'type', type)); + } + if (order != null) { + queryParams.addAll(_queryParams('', 'order', order)); + } + if (deviceAssetId != null) { + queryParams.addAll(_queryParams('', 'deviceAssetId', deviceAssetId)); + } + if (deviceId != null) { + queryParams.addAll(_queryParams('', 'deviceId', deviceId)); + } + if (checksum != null) { + queryParams.addAll(_queryParams('', 'checksum', checksum)); + } + if (isArchived != null) { + queryParams.addAll(_queryParams('', 'isArchived', isArchived)); + } + if (isEncoded != null) { + queryParams.addAll(_queryParams('', 'isEncoded', isEncoded)); + } + if (isExternal != null) { + queryParams.addAll(_queryParams('', 'isExternal', isExternal)); + } + if (isFavorite != null) { + queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); + } + if (isMotion != null) { + queryParams.addAll(_queryParams('', 'isMotion', isMotion)); + } + if (isOffline != null) { + queryParams.addAll(_queryParams('', 'isOffline', isOffline)); + } + if (isReadOnly != null) { + queryParams.addAll(_queryParams('', 'isReadOnly', isReadOnly)); + } + if (isVisible != null) { + queryParams.addAll(_queryParams('', 'isVisible', isVisible)); + } + if (withDeleted != null) { + queryParams.addAll(_queryParams('', 'withDeleted', withDeleted)); + } + if (withStacked != null) { + queryParams.addAll(_queryParams('', 'withStacked', withStacked)); + } + if (withExif != null) { + queryParams.addAll(_queryParams('', 'withExif', withExif)); + } + if (withPeople != null) { + queryParams.addAll(_queryParams('', 'withPeople', withPeople)); + } + if (createdBefore != null) { + queryParams.addAll(_queryParams('', 'createdBefore', createdBefore)); + } + if (createdAfter != null) { + queryParams.addAll(_queryParams('', 'createdAfter', createdAfter)); + } + if (updatedBefore != null) { + queryParams.addAll(_queryParams('', 'updatedBefore', updatedBefore)); + } + if (updatedAfter != null) { + queryParams.addAll(_queryParams('', 'updatedAfter', updatedAfter)); + } + if (trashedBefore != null) { + queryParams.addAll(_queryParams('', 'trashedBefore', trashedBefore)); + } + if (trashedAfter != null) { + queryParams.addAll(_queryParams('', 'trashedAfter', trashedAfter)); + } + if (takenBefore != null) { + queryParams.addAll(_queryParams('', 'takenBefore', takenBefore)); + } + if (takenAfter != null) { + queryParams.addAll(_queryParams('', 'takenAfter', takenAfter)); + } + if (originalFileName != null) { + queryParams.addAll(_queryParams('', 'originalFileName', originalFileName)); + } + if (originalPath != null) { + queryParams.addAll(_queryParams('', 'originalPath', originalPath)); + } + if (resizePath != null) { + queryParams.addAll(_queryParams('', 'resizePath', resizePath)); + } + if (webpPath != null) { + queryParams.addAll(_queryParams('', 'webpPath', webpPath)); + } + if (encodedVideoPath != null) { + queryParams.addAll(_queryParams('', 'encodedVideoPath', encodedVideoPath)); + } + if (city != null) { + queryParams.addAll(_queryParams('', 'city', city)); + } + if (state != null) { + queryParams.addAll(_queryParams('', 'state', state)); + } + if (country != null) { + queryParams.addAll(_queryParams('', 'country', country)); + } + if (make != null) { + queryParams.addAll(_queryParams('', 'make', make)); + } + if (model != null) { + queryParams.addAll(_queryParams('', 'model', model)); + } + if (lensModel != null) { + queryParams.addAll(_queryParams('', 'lensModel', lensModel)); + } + if (page != null) { + queryParams.addAll(_queryParams('', 'page', page)); + } + if (size != null) { + queryParams.addAll(_queryParams('', 'size', size)); + } + + const contentTypes = []; + + + return apiClient.invokeAPI( + path, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [String] id: + /// + /// * [String] libraryId: + /// + /// * [AssetTypeEnum] type: + /// + /// * [AssetOrder] order: + /// + /// * [String] deviceAssetId: + /// + /// * [String] deviceId: + /// + /// * [String] checksum: + /// + /// * [bool] isArchived: + /// + /// * [bool] isEncoded: + /// + /// * [bool] isExternal: + /// + /// * [bool] isFavorite: + /// + /// * [bool] isMotion: + /// + /// * [bool] isOffline: + /// + /// * [bool] isReadOnly: + /// + /// * [bool] isVisible: + /// + /// * [bool] withDeleted: + /// + /// * [bool] withStacked: + /// + /// * [bool] withExif: + /// + /// * [bool] withPeople: + /// + /// * [DateTime] createdBefore: + /// + /// * [DateTime] createdAfter: + /// + /// * [DateTime] updatedBefore: + /// + /// * [DateTime] updatedAfter: + /// + /// * [DateTime] trashedBefore: + /// + /// * [DateTime] trashedAfter: + /// + /// * [DateTime] takenBefore: + /// + /// * [DateTime] takenAfter: + /// + /// * [String] originalFileName: + /// + /// * [String] originalPath: + /// + /// * [String] resizePath: + /// + /// * [String] webpPath: + /// + /// * [String] encodedVideoPath: + /// + /// * [String] city: + /// + /// * [String] state: + /// + /// * [String] country: + /// + /// * [String] make: + /// + /// * [String] model: + /// + /// * [String] lensModel: + /// + /// * [num] page: + /// + /// * [num] size: + Future?> searchAssets({ String? id, String? libraryId, AssetTypeEnum? type, AssetOrder? order, String? deviceAssetId, String? deviceId, String? checksum, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isOffline, bool? isReadOnly, bool? isVisible, bool? withDeleted, bool? withStacked, bool? withExif, bool? withPeople, DateTime? createdBefore, DateTime? createdAfter, DateTime? updatedBefore, DateTime? updatedAfter, DateTime? trashedBefore, DateTime? trashedAfter, DateTime? takenBefore, DateTime? takenAfter, String? originalFileName, String? originalPath, String? resizePath, String? webpPath, String? encodedVideoPath, String? city, String? state, String? country, String? make, String? model, String? lensModel, num? page, num? size, }) async { + final response = await searchAssetsWithHttpInfo( id: id, libraryId: libraryId, type: type, order: order, deviceAssetId: deviceAssetId, deviceId: deviceId, checksum: checksum, isArchived: isArchived, isEncoded: isEncoded, isExternal: isExternal, isFavorite: isFavorite, isMotion: isMotion, isOffline: isOffline, isReadOnly: isReadOnly, isVisible: isVisible, withDeleted: withDeleted, withStacked: withStacked, withExif: withExif, withPeople: withPeople, createdBefore: createdBefore, createdAfter: createdAfter, updatedBefore: updatedBefore, updatedAfter: updatedAfter, trashedBefore: trashedBefore, trashedAfter: trashedAfter, takenBefore: takenBefore, takenAfter: takenAfter, originalFileName: originalFileName, originalPath: originalPath, resizePath: resizePath, webpPath: webpPath, encodedVideoPath: encodedVideoPath, city: city, state: state, country: country, make: make, model: model, lensModel: lensModel, page: page, size: size, ); + 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) { + final responseBody = await _decodeBodyBytes(response); + return (await apiClient.deserializeAsync(responseBody, 'List') as List) + .cast() + .toList(); + + } + return null; + } + /// Performs an HTTP 'GET /asset/file/{id}' operation and returns the [Response]. /// Parameters: /// diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index 4c24967ec..317292f30 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -225,6 +225,8 @@ class ApiClient { return AssetJobNameTypeTransformer().decode(value); case 'AssetJobsDto': return AssetJobsDto.fromJson(value); + case 'AssetOrder': + return AssetOrderTypeTransformer().decode(value); case 'AssetResponseDto': return AssetResponseDto.fromJson(value); case 'AssetStatsResponseDto': diff --git a/mobile/openapi/lib/api_helper.dart b/mobile/openapi/lib/api_helper.dart index ddb16df0c..d3f7971e3 100644 --- a/mobile/openapi/lib/api_helper.dart +++ b/mobile/openapi/lib/api_helper.dart @@ -58,6 +58,9 @@ String parameterToString(dynamic value) { if (value is AssetJobName) { return AssetJobNameTypeTransformer().encode(value).toString(); } + if (value is AssetOrder) { + return AssetOrderTypeTransformer().encode(value).toString(); + } if (value is AssetTypeEnum) { return AssetTypeEnumTypeTransformer().encode(value).toString(); } diff --git a/mobile/openapi/lib/model/asset_order.dart b/mobile/openapi/lib/model/asset_order.dart new file mode 100644 index 000000000..ddb68d381 --- /dev/null +++ b/mobile/openapi/lib/model/asset_order.dart @@ -0,0 +1,85 @@ +// +// 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 AssetOrder { + /// Instantiate a new enum with the provided [value]. + const AssetOrder._(this.value); + + /// The underlying value of this enum member. + final String value; + + @override + String toString() => value; + + String toJson() => value; + + static const asc = AssetOrder._(r'asc'); + static const desc = AssetOrder._(r'desc'); + + /// List of all possible values in this [enum][AssetOrder]. + static const values = [ + asc, + desc, + ]; + + static AssetOrder? fromJson(dynamic value) => AssetOrderTypeTransformer().decode(value); + + static List? listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = AssetOrder.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } +} + +/// Transformation class that can [encode] an instance of [AssetOrder] to String, +/// and [decode] dynamic data back to [AssetOrder]. +class AssetOrderTypeTransformer { + factory AssetOrderTypeTransformer() => _instance ??= const AssetOrderTypeTransformer._(); + + const AssetOrderTypeTransformer._(); + + String encode(AssetOrder data) => data.value; + + /// Decodes a [dynamic value][data] to a AssetOrder. + /// + /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, + /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] + /// cannot be decoded successfully, then an [UnimplementedError] is thrown. + /// + /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, + /// and users are still using an old app with the old code. + AssetOrder? decode(dynamic data, {bool allowNull = true}) { + if (data != null) { + switch (data) { + case r'asc': return AssetOrder.asc; + case r'desc': return AssetOrder.desc; + default: + if (!allowNull) { + throw ArgumentError('Unknown enum value to decode: $data'); + } + } + } + return null; + } + + /// Singleton [AssetOrderTypeTransformer] instance. + static AssetOrderTypeTransformer? _instance; +} + diff --git a/mobile/openapi/test/asset_api_test.dart b/mobile/openapi/test/asset_api_test.dart index ec7a2182d..5a74bcaa2 100644 --- a/mobile/openapi/test/asset_api_test.dart +++ b/mobile/openapi/test/asset_api_test.dart @@ -152,6 +152,11 @@ void main() { // TODO }); + //Future> searchAssets({ String id, String libraryId, AssetTypeEnum type, AssetOrder order, String deviceAssetId, String deviceId, String checksum, bool isArchived, bool isEncoded, bool isExternal, bool isFavorite, bool isMotion, bool isOffline, bool isReadOnly, bool isVisible, bool withDeleted, bool withStacked, bool withExif, bool withPeople, DateTime createdBefore, DateTime createdAfter, DateTime updatedBefore, DateTime updatedAfter, DateTime trashedBefore, DateTime trashedAfter, DateTime takenBefore, DateTime takenAfter, String originalFileName, String originalPath, String resizePath, String webpPath, String encodedVideoPath, String city, String state, String country, String make, String model, String lensModel, num page, num size }) async + test('test searchAssets', () async { + // TODO + }); + //Future serveFile(String id, { bool isThumb, bool isWeb, String key }) async test('test serveFile', () async { // TODO diff --git a/mobile/openapi/test/asset_order_test.dart b/mobile/openapi/test/asset_order_test.dart new file mode 100644 index 000000000..4a1490810 --- /dev/null +++ b/mobile/openapi/test/asset_order_test.dart @@ -0,0 +1,21 @@ +// +// 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 + +import 'package:openapi/api.dart'; +import 'package:test/test.dart'; + +// tests for AssetOrder +void main() { + + group('test AssetOrder', () { + + }); + +} diff --git a/server/immich-openapi-specs.json b/server/immich-openapi-specs.json index 4dbc2c282..ad53f7e1b 100644 --- a/server/immich-openapi-specs.json +++ b/server/immich-openapi-specs.json @@ -2464,6 +2464,372 @@ ] } }, + "/assets": { + "get": { + "operationId": "searchAssets", + "parameters": [ + { + "name": "id", + "required": false, + "in": "query", + "schema": { + "format": "uuid", + "type": "string" + } + }, + { + "name": "libraryId", + "required": false, + "in": "query", + "schema": { + "format": "uuid", + "type": "string" + } + }, + { + "name": "type", + "required": false, + "in": "query", + "schema": { + "$ref": "#/components/schemas/AssetTypeEnum" + } + }, + { + "name": "order", + "required": false, + "in": "query", + "schema": { + "$ref": "#/components/schemas/AssetOrder" + } + }, + { + "name": "deviceAssetId", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "deviceId", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "checksum", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "isArchived", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isEncoded", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isExternal", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isFavorite", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isMotion", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isOffline", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isReadOnly", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isVisible", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "withDeleted", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "withStacked", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "withExif", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "withPeople", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "createdBefore", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "createdAfter", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "updatedBefore", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "updatedAfter", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "trashedBefore", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "trashedAfter", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "takenBefore", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "takenAfter", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "originalFileName", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "originalPath", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "resizePath", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "webpPath", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "encodedVideoPath", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "city", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "state", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "country", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "make", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "model", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "lensModel", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "page", + "required": false, + "in": "query", + "schema": { + "type": "number" + } + }, + { + "name": "size", + "required": false, + "in": "query", + "schema": { + "type": "number" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/AssetResponseDto" + }, + "type": "array" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Asset" + ] + } + }, "/audit/deletes": { "get": { "operationId": "getAuditDeletes", @@ -6304,6 +6670,13 @@ ], "type": "object" }, + "AssetOrder": { + "enum": [ + "asc", + "desc" + ], + "type": "string" + }, "AssetResponseDto": { "properties": { "checksum": { diff --git a/server/src/domain/asset/asset.service.ts b/server/src/domain/asset/asset.service.ts index 3b9b56412..dbba670e7 100644 --- a/server/src/domain/asset/asset.service.ts +++ b/server/src/domain/asset/asset.service.ts @@ -30,6 +30,8 @@ import { AssetIdsDto, AssetJobName, AssetJobsDto, + AssetOrder, + AssetSearchDto, AssetStatsDto, DownloadArchiveInfo, DownloadInfoDto, @@ -91,6 +93,34 @@ export class AssetService { this.configCore = SystemConfigCore.create(configRepository); } + search(authUser: AuthUserDto, dto: AssetSearchDto) { + let checksum: Buffer | undefined = undefined; + + if (dto.checksum) { + const encoding = dto.checksum.length === 28 ? 'base64' : 'hex'; + checksum = Buffer.from(dto.checksum, encoding); + } + + const enumToOrder = { [AssetOrder.ASC]: 'ASC', [AssetOrder.DESC]: 'DESC' } as const; + const order = dto.order ? enumToOrder[dto.order] : undefined; + + return this.assetRepository + .search({ + ...dto, + order, + checksum, + ownerId: authUser.id, + }) + .then((assets) => + assets.map((asset) => + mapAsset(asset, { + stripMetadata: false, + withStack: true, + }), + ), + ); + } + canUploadFile({ authUser, fieldName, file }: UploadRequest): true { this.access.requireUploadAccess(authUser); diff --git a/server/src/domain/asset/dto/asset.dto.ts b/server/src/domain/asset/dto/asset.dto.ts index 0b3ce68d5..c7c371706 100644 --- a/server/src/domain/asset/dto/asset.dto.ts +++ b/server/src/domain/asset/dto/asset.dto.ts @@ -1,8 +1,161 @@ +import { AssetType } from '@app/infra/entities'; +import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; -import { IsBoolean, IsInt, IsPositive, IsString } from 'class-validator'; -import { Optional, ValidateUUID } from '../../domain.util'; +import { IsBoolean, IsEnum, IsInt, IsPositive, IsString, Min } from 'class-validator'; +import { Optional, QueryBoolean, QueryDate, ValidateUUID } from '../../domain.util'; import { BulkIdsDto } from '../response-dto'; +export enum AssetOrder { + ASC = 'asc', + DESC = 'desc', +} + +export class AssetSearchDto { + @ValidateUUID({ optional: true }) + id?: string; + + @ValidateUUID({ optional: true }) + libraryId?: string; + + @IsString() + @Optional() + deviceAssetId?: string; + + @IsString() + @Optional() + deviceId?: string; + + @IsEnum(AssetType) + @Optional() + @ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType }) + type?: AssetType; + + @IsString() + @Optional() + checksum?: string; + + @QueryBoolean({ optional: true }) + isArchived?: boolean; + + @QueryBoolean({ optional: true }) + isEncoded?: boolean; + + @QueryBoolean({ optional: true }) + isExternal?: boolean; + + @QueryBoolean({ optional: true }) + isFavorite?: boolean; + + @QueryBoolean({ optional: true }) + isMotion?: boolean; + + @QueryBoolean({ optional: true }) + isOffline?: boolean; + + @QueryBoolean({ optional: true }) + isReadOnly?: boolean; + + @QueryBoolean({ optional: true }) + isVisible?: boolean; + + @QueryBoolean({ optional: true }) + withDeleted?: boolean; + + @QueryBoolean({ optional: true }) + withStacked?: boolean; + + @QueryBoolean({ optional: true }) + withExif?: boolean; + + @QueryBoolean({ optional: true }) + withPeople?: boolean; + + @QueryDate({ optional: true }) + createdBefore?: Date; + + @QueryDate({ optional: true }) + createdAfter?: Date; + + @QueryDate({ optional: true }) + updatedBefore?: Date; + + @QueryDate({ optional: true }) + updatedAfter?: Date; + + @QueryDate({ optional: true }) + trashedBefore?: Date; + + @QueryDate({ optional: true }) + trashedAfter?: Date; + + @QueryDate({ optional: true }) + takenBefore?: Date; + + @QueryDate({ optional: true }) + takenAfter?: Date; + + @IsString() + @Optional() + originalFileName?: string; + + @IsString() + @Optional() + originalPath?: string; + + @IsString() + @Optional() + resizePath?: string; + + @IsString() + @Optional() + webpPath?: string; + + @IsString() + @Optional() + encodedVideoPath?: string; + + @IsString() + @Optional() + city?: string; + + @IsString() + @Optional() + state?: string; + + @IsString() + @Optional() + country?: string; + + @IsString() + @Optional() + make?: string; + + @IsString() + @Optional() + model?: string; + + @IsString() + @Optional() + lensModel?: string; + + @IsEnum(AssetOrder) + @Optional() + @ApiProperty({ enumName: 'AssetOrder', enum: AssetOrder }) + order?: AssetOrder; + + @IsInt() + @Min(1) + @Type(() => Number) + @Optional() + page?: number; + + @IsInt() + @Min(1) + @Type(() => Number) + @Optional() + size?: number; +} + export class AssetBulkUpdateDto extends BulkIdsDto { @Optional() @IsBoolean() diff --git a/server/src/domain/domain.util.ts b/server/src/domain/domain.util.ts index 04ec4f430..53e674bf3 100644 --- a/server/src/domain/domain.util.ts +++ b/server/src/domain/domain.util.ts @@ -1,6 +1,17 @@ import { applyDecorators } from '@nestjs/common'; import { ApiProperty } from '@nestjs/swagger'; -import { IsArray, IsNotEmpty, IsOptional, IsString, IsUUID, ValidateIf, ValidationOptions } from 'class-validator'; +import { Transform, Type } from 'class-transformer'; +import { + IsArray, + IsBoolean, + IsDate, + IsNotEmpty, + IsOptional, + IsString, + IsUUID, + ValidateIf, + ValidationOptions, +} from 'class-validator'; import { CronJob } from 'cron'; import { basename, extname } from 'node:path'; import sanitize from 'sanitize-filename'; @@ -33,6 +44,22 @@ interface IValue { value?: string; } +export const QueryBoolean = ({ optional }: { optional?: boolean }) => { + const decorators = [IsBoolean(), Transform(toBoolean)]; + if (optional) { + decorators.push(Optional()); + } + return applyDecorators(...decorators); +}; + +export const QueryDate = ({ optional }: { optional?: boolean }) => { + const decorators = [IsDate(), Type(() => Date)]; + if (optional) { + decorators.push(Optional()); + } + return applyDecorators(...decorators); +}; + export const toBoolean = ({ value }: IValue) => { if (value == 'true') { return true; diff --git a/server/src/domain/repositories/asset.repository.ts b/server/src/domain/repositories/asset.repository.ts index 2ceabad35..12ae49000 100644 --- a/server/src/domain/repositories/asset.repository.ts +++ b/server/src/domain/repositories/asset.repository.ts @@ -11,11 +11,58 @@ export interface AssetStatsOptions { } export interface AssetSearchOptions { - isVisible?: boolean; - trashedBefore?: Date; + id?: string; + libraryId?: string; + deviceAssetId?: string; + deviceId?: string; + ownerId?: string; type?: AssetType; - order?: 'ASC' | 'DESC'; + checksum?: Buffer; + + isArchived?: boolean; + isEncoded?: boolean; + isExternal?: boolean; + isFavorite?: boolean; + isMotion?: boolean; + isOffline?: boolean; + isReadOnly?: boolean; + isVisible?: boolean; + withDeleted?: boolean; + withStacked?: boolean; + withExif?: boolean; + withPeople?: boolean; + + createdBefore?: Date; + createdAfter?: Date; + updatedBefore?: Date; + updatedAfter?: Date; + trashedBefore?: Date; + trashedAfter?: Date; + takenBefore?: Date; + takenAfter?: Date; + + originalFileName?: string; + originalPath?: string; + resizePath?: string; + webpPath?: string; + encodedVideoPath?: string; + + city?: string; + state?: string; + country?: string; + make?: string; + model?: string; + lensModel?: string; + + /** defaults to 'DESC' */ + order?: 'ASC' | 'DESC'; + + /** defaults to 1 */ + page?: number; + + /** defaults to 250 */ + size?: number; } export interface LivePhotoSearchOptions { @@ -127,4 +174,5 @@ export interface IAssetRepository { getTimeBucket(timeBucket: string, options: TimeBucketOptions): Promise; upsertExif(exif: Partial): Promise; upsertJobStatus(jobStatus: Partial): Promise; + search(options: AssetSearchOptions): Promise; } diff --git a/server/src/immich/app.module.ts b/server/src/immich/app.module.ts index cf95eb236..7f9edfd42 100644 --- a/server/src/immich/app.module.ts +++ b/server/src/immich/app.module.ts @@ -16,6 +16,7 @@ import { AlbumController, AppController, AssetController, + AssetsController, AuditController, AuthController, JobController, @@ -41,6 +42,7 @@ import { ErrorInterceptor, FileUploadInterceptor } from './interceptors'; ], controllers: [ ActivityController, + AssetsController, AssetController, AssetControllerV1, AppController, diff --git a/server/src/immich/controllers/asset.controller.ts b/server/src/immich/controllers/asset.controller.ts index d0f294d1e..105760e50 100644 --- a/server/src/immich/controllers/asset.controller.ts +++ b/server/src/immich/controllers/asset.controller.ts @@ -4,6 +4,7 @@ import { AssetIdsDto, AssetJobsDto, AssetResponseDto, + AssetSearchDto, AssetService, AssetStatsDto, AssetStatsResponseDto, @@ -42,6 +43,19 @@ import { UseValidation, asStreamableFile } from '../app.utils'; import { Route } from '../interceptors'; import { UUIDParamDto } from './dto/uuid-param.dto'; +@ApiTags('Asset') +@Controller('assets') +@Authenticated() +@UseValidation() +export class AssetsController { + constructor(private service: AssetService) {} + + @Get() + searchAssets(@AuthUser() authUser: AuthUserDto, @Query() dto: AssetSearchDto): Promise { + return this.service.search(authUser, dto); + } +} + @ApiTags('Asset') @Controller(Route.ASSET) @Authenticated() diff --git a/server/src/infra/repositories/asset.repository.ts b/server/src/infra/repositories/asset.repository.ts index 3f0b439a3..b6fa10c0d 100644 --- a/server/src/infra/repositories/asset.repository.ts +++ b/server/src/infra/repositories/asset.repository.ts @@ -18,12 +18,15 @@ import { } from '@app/domain'; import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; +import _ from 'lodash'; import { DateTime } from 'luxon'; import { And, FindOptionsRelations, FindOptionsWhere, In, IsNull, LessThan, Not, Repository } from 'typeorm'; import { AssetEntity, AssetJobStatusEntity, AssetType, ExifEntity } from '../entities'; import OptionalBetween from '../utils/optional-between.util'; import { paginate } from '../utils/pagination.util'; +const DEFAULT_SEARCH_SIZE = 250; + const truncateMap: Record = { [TimeBucketSize.DAY]: 'day', [TimeBucketSize.MONTH]: 'month', @@ -50,6 +53,134 @@ export class AssetRepository implements IAssetRepository { await this.jobStatusRepository.upsert(jobStatus, { conflictPaths: ['assetId'] }); } + search(options: AssetSearchOptions): Promise { + const { + id, + libraryId, + deviceAssetId, + type, + checksum, + ownerId, + + isVisible, + isFavorite, + isExternal, + isReadOnly, + isOffline, + isArchived, + isMotion, + isEncoded, + + createdBefore, + createdAfter, + updatedBefore, + updatedAfter, + trashedBefore, + trashedAfter, + takenBefore, + takenAfter, + + originalFileName, + originalPath, + resizePath, + webpPath, + encodedVideoPath, + + city, + state, + country, + make, + model, + lensModel, + + withDeleted: _withDeleted, + withExif: _withExif, + withStacked, + withPeople, + + order, + } = options; + + const withDeleted = _withDeleted ?? (trashedAfter !== undefined || trashedBefore !== undefined); + + const page = Math.max(options.page || 1, 1); + const size = Math.min(options.size || DEFAULT_SEARCH_SIZE, DEFAULT_SEARCH_SIZE); + + const exifWhere = _.omitBy( + { + city, + state, + country, + make, + model, + lensModel, + }, + _.isUndefined, + ); + + const withExif = Object.keys(exifWhere).length > 0 || _withExif; + + const where = _.omitBy( + { + ownerId, + id, + libraryId, + deviceAssetId, + type, + checksum, + isVisible, + isFavorite, + isExternal, + isReadOnly, + isOffline, + isArchived, + livePhotoVideoId: isMotion && Not(IsNull()), + originalFileName, + originalPath, + resizePath, + webpPath, + encodedVideoPath: encodedVideoPath ?? (isEncoded && Not(IsNull())), + createdAt: OptionalBetween(createdAfter, createdBefore), + updatedAt: OptionalBetween(updatedAfter, updatedBefore), + deletedAt: OptionalBetween(trashedAfter, trashedBefore), + fileCreatedAt: OptionalBetween(takenAfter, takenBefore), + exifInfo: Object.keys(exifWhere).length > 0 ? exifWhere : undefined, + }, + _.isUndefined, + ); + + const builder = this.repository.createQueryBuilder('asset'); + + if (withExif) { + if (_withExif) { + builder.leftJoinAndSelect('asset.exifInfo', 'exifInfo'); + } else { + builder.leftJoin('asset.exifInfo', 'exifInfo'); + } + } + + if (withPeople) { + builder.leftJoinAndSelect('asset.faces', 'faces'); + builder.leftJoinAndSelect('faces.person', 'person'); + } + + if (withStacked) { + builder.leftJoinAndSelect('asset.stack', 'stack'); + } + + if (withDeleted) { + builder.withDeleted(); + } + + builder + .where(where) + .skip(size * (page - 1)) + .take(size) + .orderBy('asset.fileCreatedAt', order ?? 'DESC'); + + return builder.getMany(); + } + create(asset: AssetCreate): Promise { return this.repository.save(asset); } diff --git a/server/test/e2e/asset.e2e-spec.ts b/server/test/e2e/asset.e2e-spec.ts index e15e27d5a..197f40d54 100644 --- a/server/test/e2e/asset.e2e-spec.ts +++ b/server/test/e2e/asset.e2e-spec.ts @@ -4,19 +4,20 @@ import { IPersonRepository, LibraryResponseDto, LoginResponseDto, - SharedLinkResponseDto, TimeBucketSize, WithoutProperty, + mapAsset, usePagination, } from '@app/domain'; import { AssetController } from '@app/immich'; -import { AssetEntity, AssetType, SharedLinkType } from '@app/infra/entities'; +import { AssetEntity, AssetType, LibraryType, SharedLinkType } from '@app/infra/entities'; import { AssetRepository } from '@app/infra/repositories'; import { INestApplication } from '@nestjs/common'; import { api } from '@test/api'; import { errorStub, uuidStub } from '@test/fixtures'; import { db, testApp } from '@test/test-utils'; import { randomBytes } from 'crypto'; +import { DateTime } from 'luxon'; import request from 'supertest'; const user1Dto = { @@ -31,6 +32,9 @@ const user2Dto = { name: 'User 2', }; +const today = DateTime.fromObject({ year: 2023, month: 11, day: 3 }); +const yesterday = today.minus({ days: 1 }); + const makeUploadDto = (options?: { omit: string }): Record => { const dto: Record = { deviceAssetId: 'example-image', @@ -49,83 +53,498 @@ const makeUploadDto = (options?: { omit: string }): Record => { return dto; }; -let assetCount = 0; -const createAsset = ( - repository: IAssetRepository, - loginResponse: LoginResponseDto, - libraryId: string, - createdAt: Date, -): Promise => { - const id = assetCount++; - return repository.create({ - ownerId: loginResponse.userId, - checksum: randomBytes(20), - originalPath: `/tests/test_${id}`, - deviceAssetId: `test_${id}`, - deviceId: 'e2e-test', - libraryId, - isVisible: true, - fileCreatedAt: createdAt, - fileModifiedAt: new Date(), - localDateTime: createdAt, - type: AssetType.IMAGE, - originalFileName: `test_${id}`, - }); -}; - describe(`${AssetController.name} (e2e)`, () => { let app: INestApplication; let server: any; let assetRepository: IAssetRepository; - let defaultLibrary: LibraryResponseDto; - let sharedLink: SharedLinkResponseDto; let user1: LoginResponseDto; let user2: LoginResponseDto; - let asset1: AssetEntity; - let asset2: AssetEntity; - let asset3: AssetEntity; - let asset4: AssetEntity; + let libraries: LibraryResponseDto[]; + let asset1: AssetResponseDto; + let asset2: AssetResponseDto; + let asset3: AssetResponseDto; + let asset4: AssetResponseDto; + let asset5: AssetResponseDto; + + let assetCount = 0; + const createAsset = async (loginResponse: LoginResponseDto, createdAt: Date, other: Partial = {}) => { + const id = assetCount++; + const asset = await assetRepository.create({ + createdAt: today.toJSDate(), + updatedAt: today.toJSDate(), + ownerId: loginResponse.userId, + checksum: randomBytes(20), + originalPath: `/tests/test_${id}`, + deviceAssetId: `test_${id}`, + deviceId: 'e2e-test', + libraryId: ( + libraries.find( + ({ ownerId, type }) => ownerId === loginResponse.userId && type === LibraryType.UPLOAD, + ) as LibraryResponseDto + ).id, + isVisible: true, + fileCreatedAt: createdAt, + fileModifiedAt: new Date(), + localDateTime: createdAt, + type: AssetType.IMAGE, + originalFileName: `test_${id}`, + ...other, + }); + + return mapAsset(asset); + }; beforeAll(async () => { [server, app] = await testApp.create(); assetRepository = app.get(IAssetRepository); - }); - afterAll(async () => { - await testApp.teardown(); - }); - - beforeEach(async () => { await db.reset(); + await api.authApi.adminSignUp(server); const admin = await api.authApi.adminLogin(server); - const [libraries] = await Promise.all([ - api.libraryApi.getAll(server, admin.accessToken), + await Promise.all([ api.userApi.create(server, admin.accessToken, user1Dto), api.userApi.create(server, admin.accessToken, user2Dto), ]); - defaultLibrary = libraries[0]; - [user1, user2] = await Promise.all([ api.authApi.login(server, { email: user1Dto.email, password: user1Dto.password }), api.authApi.login(server, { email: user2Dto.email, password: user2Dto.password }), ]); - [asset1, asset2, asset3, asset4] = await Promise.all([ - createAsset(assetRepository, user1, defaultLibrary.id, new Date('1970-01-01')), - createAsset(assetRepository, user1, defaultLibrary.id, new Date('1970-01-02')), - createAsset(assetRepository, user1, defaultLibrary.id, new Date('1970-02-01')), - createAsset(assetRepository, user2, defaultLibrary.id, new Date('1970-01-01')), + const [user1Libraries, user2Libraries] = await Promise.all([ + api.libraryApi.getAll(server, user1.accessToken), + api.libraryApi.getAll(server, user2.accessToken), ]); - sharedLink = await api.sharedLinkApi.create(server, user1.accessToken, { - type: SharedLinkType.INDIVIDUAL, - assetIds: [asset1.id, asset2.id], + libraries = [...user1Libraries, ...user2Libraries]; + }); + + beforeEach(async () => { + await db.reset({ entities: [AssetEntity] }); + + [asset1, asset2, asset3, asset4, asset5] = await Promise.all([ + createAsset(user1, new Date('1970-01-01')), + createAsset(user1, new Date('1970-02-10')), + createAsset(user1, new Date('1970-02-11'), { + isFavorite: true, + isArchived: true, + isExternal: true, + isReadOnly: true, + type: AssetType.VIDEO, + fileCreatedAt: yesterday.toJSDate(), + fileModifiedAt: yesterday.toJSDate(), + createdAt: yesterday.toJSDate(), + updatedAt: yesterday.toJSDate(), + localDateTime: yesterday.toJSDate(), + encodedVideoPath: '/path/to/encoded-video.mp4', + webpPath: '/path/to/thumb.webp', + resizePath: '/path/to/thumb.jpg', + }), + createAsset(user2, new Date('1970-01-01')), + createAsset(user1, new Date('1970-01-01'), { + deletedAt: yesterday.toJSDate(), + }), + ]); + + await assetRepository.upsertExif({ + assetId: asset3.id, + latitude: 90, + longitude: 90, + city: 'Immich', + state: 'Nebraska', + country: 'United States', + make: 'Cannon', + model: 'EOS Rebel T7', + lensModel: 'Fancy lens', }); }); + afterAll(async () => { + await testApp.teardown(); + }); + + describe('GET /assets', () => { + it('should require authentication', async () => { + const { status, body } = await request(server).get('/assets'); + expect(body).toEqual(errorStub.unauthorized); + expect(status).toBe(401); + }); + + const badTests = [ + // + { + should: 'should reject page as a string', + query: { page: 'abc' }, + expected: ['page must not be less than 1', 'page must be an integer number'], + }, + { + should: 'should reject page as a decimal', + query: { page: 1.5 }, + expected: ['page must be an integer number'], + }, + { + should: 'should reject page as a negative number', + query: { page: -10 }, + expected: ['page must not be less than 1'], + }, + { + should: 'should reject page as 0', + query: { page: 0 }, + expected: ['page must not be less than 1'], + }, + { + should: 'should reject size as a string', + query: { size: 'abc' }, + expected: ['size must not be less than 1', 'size must be an integer number'], + }, + { + should: 'should reject an invalid size', + query: { size: -1.5 }, + expected: ['size must not be less than 1', 'size must be an integer number'], + }, + ...[ + 'isArchived', + 'isFavorite', + 'isReadOnly', + 'isExternal', + 'isEncoded', + 'isMotion', + 'isOffline', + 'isVisible', + ].map((value) => ({ + should: `should reject ${value} not a boolean`, + query: { [value]: 'immich' }, + expected: [`${value} must be a boolean value`], + })), + ]; + + for (const { should, query, expected } of badTests) { + it(should, async () => { + const { status, body } = await request(server) + .get('/assets') + .set('Authorization', `Bearer ${user1.accessToken}`) + .query(query); + expect(status).toBe(400); + expect(body).toEqual(errorStub.badRequest(expected)); + }); + } + + const searchTests = [ + { + should: 'should only return my own assets', + deferred: () => ({ + query: {}, + assets: [asset3, asset2, asset1], + }), + }, + { + should: 'should sort my assets in reverse', + deferred: () => ({ + query: { order: 'asc' }, + assets: [asset1, asset2, asset3], + }), + }, + { + should: 'should support custom page sizes', + deferred: () => ({ + query: { size: 1 }, + assets: [asset3], + }), + }, + { + should: 'should support pagination', + deferred: () => ({ + query: { size: 1, page: 2 }, + assets: [asset2], + }), + }, + { + should: 'should search by checksum (base64)', + deferred: () => ({ + query: { checksum: asset1.checksum }, + assets: [asset1], + }), + }, + { + should: 'should search by checksum (hex)', + deferred: () => ({ + query: { checksum: Buffer.from(asset1.checksum, 'base64').toString('hex') }, + assets: [asset1], + }), + }, + { + should: 'should search by id', + deferred: () => ({ + query: { id: asset1.id }, + assets: [asset1], + }), + }, + { + should: 'should search by isFavorite (true)', + deferred: () => ({ + query: { isFavorite: true }, + assets: [asset3], + }), + }, + { + should: 'should search by isFavorite (false)', + deferred: () => ({ + query: { isFavorite: false }, + assets: [asset2, asset1], + }), + }, + { + should: 'should search by isArchived (true)', + deferred: () => ({ + query: { isArchived: true }, + assets: [asset3], + }), + }, + { + should: 'should search by isArchived (false)', + deferred: () => ({ + query: { isArchived: false }, + assets: [asset2, asset1], + }), + }, + { + should: 'should search by isReadOnly (true)', + deferred: () => ({ + query: { isReadOnly: true }, + assets: [asset3], + }), + }, + { + should: 'should search by isReadOnly (false)', + deferred: () => ({ + query: { isReadOnly: false }, + assets: [asset2, asset1], + }), + }, + { + should: 'should search by type (image)', + deferred: () => ({ + query: { type: 'IMAGE' }, + assets: [asset2, asset1], + }), + }, + { + should: 'should search by type (video)', + deferred: () => ({ + query: { type: 'VIDEO' }, + assets: [asset3], + }), + }, + { + should: 'should search by createdBefore', + deferred: () => ({ + query: { createdBefore: yesterday.plus({ hour: 1 }).toJSDate() }, + assets: [asset3], + }), + }, + { + should: 'should search by createdBefore (no results)', + deferred: () => ({ + query: { createdBefore: yesterday.minus({ hour: 1 }).toJSDate() }, + assets: [], + }), + }, + { + should: 'should search by createdAfter', + deferred: () => ({ + query: { createdAfter: yesterday.minus({ hour: 1 }).toJSDate() }, + assets: [asset3, asset2, asset1], + }), + }, + { + should: 'should search by createdAfter (no results)', + deferred: () => ({ + query: { createdAfter: today.plus({ hour: 1 }).toJSDate() }, + assets: [], + }), + }, + { + should: 'should search by updatedBefore', + deferred: () => ({ + query: { updatedBefore: yesterday.plus({ hour: 1 }).toJSDate() }, + assets: [asset3], + }), + }, + { + should: 'should search by updatedBefore (no results)', + deferred: () => ({ + query: { updatedBefore: yesterday.minus({ hour: 1 }).toJSDate() }, + assets: [], + }), + }, + { + should: 'should search by updatedAfter', + deferred: () => ({ + query: { updatedAfter: yesterday.minus({ hour: 1 }).toJSDate() }, + assets: [asset3, asset2, asset1], + }), + }, + { + should: 'should search by updatedAfter (no results)', + deferred: () => ({ + query: { updatedAfter: today.plus({ hour: 1 }).toJSDate() }, + assets: [], + }), + }, + { + should: 'should search by trashedBefore', + deferred: () => ({ + query: { trashedBefore: yesterday.plus({ hour: 1 }).toJSDate() }, + assets: [asset5], + }), + }, + { + should: 'should search by trashedBefore (no results)', + deferred: () => ({ + query: { trashedBefore: yesterday.minus({ hour: 1 }).toJSDate() }, + assets: [], + }), + }, + { + should: 'should search by trashedAfter', + deferred: () => ({ + query: { trashedAfter: yesterday.minus({ hour: 1 }).toJSDate() }, + assets: [asset5], + }), + }, + { + should: 'should search by trashedAfter (no results)', + deferred: () => ({ + query: { trashedAfter: today.plus({ hour: 1 }).toJSDate() }, + assets: [], + }), + }, + { + should: 'should search by takenBefore', + deferred: () => ({ + query: { takenBefore: yesterday.plus({ hour: 1 }).toJSDate() }, + assets: [asset3, asset2, asset1], + }), + }, + { + should: 'should search by takenBefore (no results)', + deferred: () => ({ + query: { takenBefore: yesterday.minus({ years: 100 }).toJSDate() }, + assets: [], + }), + }, + { + should: 'should search by takenAfter', + deferred: () => ({ + query: { takenAfter: yesterday.minus({ hour: 1 }).toJSDate() }, + assets: [asset3], + }), + }, + { + should: 'should search by takenAfter (no results)', + deferred: () => ({ + query: { takenAfter: today.plus({ hour: 1 }).toJSDate() }, + assets: [], + }), + }, + { + should: 'should search by originalPath', + deferred: () => ({ + query: { originalPath: asset1.originalPath }, + assets: [asset1], + }), + }, + { + should: 'should search by originalFilename', + deferred: () => ({ + query: { originalFileName: asset1.originalFileName }, + assets: [asset1], + }), + }, + { + should: 'should search by encodedVideoPath', + deferred: () => ({ + query: { encodedVideoPath: '/path/to/encoded-video.mp4' }, + assets: [asset3], + }), + }, + { + should: 'should search by resizePath', + deferred: () => ({ + query: { resizePath: '/path/to/thumb.jpg' }, + assets: [asset3], + }), + }, + { + should: 'should search by webpPath', + deferred: () => ({ + query: { webpPath: '/path/to/thumb.webp' }, + assets: [asset3], + }), + }, + { + should: 'should search by city', + deferred: () => ({ + query: { city: 'Immich' }, + assets: [asset3], + }), + }, + { + should: 'should search by state', + deferred: () => ({ + query: { state: 'Nebraska' }, + assets: [asset3], + }), + }, + { + should: 'should search by country', + deferred: () => ({ + query: { country: 'United States' }, + assets: [asset3], + }), + }, + { + should: 'sohuld search by make', + deferred: () => ({ + query: { make: 'Cannon' }, + assets: [asset3], + }), + }, + { + should: 'should search by country', + deferred: () => ({ + query: { model: 'EOS Rebel T7' }, + assets: [asset3], + }), + }, + { + should: 'should search by lensModel', + deferred: () => ({ + query: { lensModel: 'Fancy lens' }, + assets: [asset3], + }), + }, + ]; + + for (const { should, deferred } of searchTests) { + it(should, async () => { + const { assets, query } = deferred(); + const { status, body } = await request(server) + .get('/assets') + .query(query) + .set('Authorization', `Bearer ${user1.accessToken}`); + + expect(status).toBe(200); + expect(body.length).toBe(assets.length); + for (let i = 0; i < assets.length; i++) { + expect(body[i]).toEqual(expect.objectContaining({ id: assets[i].id })); + } + }); + } + }); + describe('POST /asset/upload', () => { it('should require authentication', async () => { const { status, body } = await request(server) @@ -369,8 +788,8 @@ describe(`${AssetController.name} (e2e)`, () => { .get('/asset/statistics') .set('Authorization', `Bearer ${user1.accessToken}`); + expect(body).toEqual({ images: 5, videos: 1, total: 6 }); expect(status).toBe(200); - expect(body).toEqual({ images: 6, videos: 0, total: 6 }); }); it('should return stats of all favored assets', async () => { @@ -380,7 +799,7 @@ describe(`${AssetController.name} (e2e)`, () => { .query({ isFavorite: true }); expect(status).toBe(200); - expect(body).toEqual({ images: 2, videos: 0, total: 2 }); + expect(body).toEqual({ images: 2, videos: 1, total: 3 }); }); it('should return stats of all archived assets', async () => { @@ -390,7 +809,7 @@ describe(`${AssetController.name} (e2e)`, () => { .query({ isArchived: true }); expect(status).toBe(200); - expect(body).toEqual({ images: 2, videos: 0, total: 2 }); + expect(body).toEqual({ images: 2, videos: 1, total: 3 }); }); it('should return stats of all favored and archived assets', async () => { @@ -400,7 +819,7 @@ describe(`${AssetController.name} (e2e)`, () => { .query({ isFavorite: true, isArchived: true }); expect(status).toBe(200); - expect(body).toEqual({ images: 1, videos: 0, total: 1 }); + expect(body).toEqual({ images: 1, videos: 1, total: 2 }); }); it('should return stats of all assets neither favored nor archived', async () => { @@ -410,19 +829,19 @@ describe(`${AssetController.name} (e2e)`, () => { .query({ isFavorite: false, isArchived: false }); expect(status).toBe(200); - expect(body).toEqual({ images: 3, videos: 0, total: 3 }); + expect(body).toEqual({ images: 2, videos: 0, total: 2 }); }); }); describe('GET /asset/random', () => { beforeAll(async () => { await Promise.all([ - createAsset(assetRepository, user1, defaultLibrary.id, new Date('1970-02-01')), - createAsset(assetRepository, user1, defaultLibrary.id, new Date('1970-02-01')), - createAsset(assetRepository, user1, defaultLibrary.id, new Date('1970-02-01')), - createAsset(assetRepository, user1, defaultLibrary.id, new Date('1970-02-01')), - createAsset(assetRepository, user1, defaultLibrary.id, new Date('1970-02-01')), - createAsset(assetRepository, user1, defaultLibrary.id, new Date('1970-02-01')), + createAsset(user1, new Date('1970-02-01')), + createAsset(user1, new Date('1970-02-01')), + createAsset(user1, new Date('1970-02-01')), + createAsset(user1, new Date('1970-02-01')), + createAsset(user1, new Date('1970-02-01')), + createAsset(user1, new Date('1970-02-01')), ]); }); it('should require authentication', async () => { @@ -432,7 +851,7 @@ describe(`${AssetController.name} (e2e)`, () => { expect(body).toEqual(errorStub.unauthorized); }); - it('should return 1 random assets', async () => { + it.each(Array(10))('should return 1 random assets', async () => { const { status, body } = await request(server) .get('/asset/random') .set('Authorization', `Bearer ${user1.accessToken}`); @@ -442,13 +861,14 @@ describe(`${AssetController.name} (e2e)`, () => { const assets: AssetResponseDto[] = body; expect(assets.length).toBe(1); expect(assets[0].ownerId).toBe(user1.userId); - // assets owned by user1 - expect([asset1.id, asset2.id, asset3.id]).toContain(assets[0].id); + // // assets owned by user2 expect(assets[0].id).not.toBe(asset4.id); + // assets owned by user1 + expect([asset1.id, asset2.id, asset3.id]).toContain(assets[0].id); }); - it('should return 2 random assets', async () => { + it.each(Array(10))('should return 2 random assets', async () => { const { status, body } = await request(server) .get('/asset/random?count=2') .set('Authorization', `Bearer ${user1.accessToken}`); @@ -505,13 +925,19 @@ describe(`${AssetController.name} (e2e)`, () => { expect(status).toBe(200); expect(body).toEqual( expect.arrayContaining([ - { count: 1, timeBucket: asset3.fileCreatedAt.toISOString() }, - { count: 2, timeBucket: asset1.fileCreatedAt.toISOString() }, + { count: 1, timeBucket: '2023-11-01T00:00:00.000Z' }, + { count: 1, timeBucket: '1970-01-01T00:00:00.000Z' }, + { count: 1, timeBucket: '1970-02-01T00:00:00.000Z' }, ]), ); }); it('should not allow access for unrelated shared links', async () => { + const sharedLink = await api.sharedLinkApi.create(server, user1.accessToken, { + type: SharedLinkType.INDIVIDUAL, + assetIds: [asset1.id, asset2.id], + }); + const { status, body } = await request(server) .get('/asset/time-buckets') .query({ key: sharedLink.key, size: TimeBucketSize.MONTH }); @@ -575,12 +1001,7 @@ describe(`${AssetController.name} (e2e)`, () => { .query({ size: TimeBucketSize.MONTH, timeBucket }); expect(status).toBe(200); - expect(body).toEqual( - expect.arrayContaining([ - expect.objectContaining({ id: asset1.id }), - expect.objectContaining({ id: asset2.id }), - ]), - ); + expect(body).toEqual(expect.arrayContaining([expect.objectContaining({ id: asset2.id })])); }); it('should return error if time bucket is requested with partners asset and archived', async () => { @@ -649,16 +1070,12 @@ describe(`${AssetController.name} (e2e)`, () => { it('should get map markers for all non-archived assets', async () => { const { status, body } = await request(server) .get('/asset/map-marker') + .query({ isArchived: false }) .set('Authorization', `Bearer ${user1.accessToken}`); expect(status).toBe(200); - expect(body).toHaveLength(2); - expect(body).toEqual( - expect.arrayContaining([ - expect.objectContaining({ id: asset1.id }), - expect.objectContaining({ id: asset2.id }), - ]), - ); + expect(body).toHaveLength(1); + expect(body).toEqual(expect.arrayContaining([expect.objectContaining({ id: asset2.id })])); }); it('should get all map markers', async () => { @@ -711,8 +1128,10 @@ describe(`${AssetController.name} (e2e)`, () => { }); it('should add stack children', async () => { - const parent = await createAsset(assetRepository, user1, defaultLibrary.id, new Date('1970-01-01')); - const child = await createAsset(assetRepository, user1, defaultLibrary.id, new Date('1970-01-01')); + const [parent, child] = await Promise.all([ + createAsset(user1, new Date('1970-01-01')), + createAsset(user1, new Date('1970-01-01')), + ]); const { status } = await request(server) .put('/asset') @@ -752,7 +1171,7 @@ describe(`${AssetController.name} (e2e)`, () => { }); it('should merge stack children', async () => { - const newParent = await createAsset(assetRepository, user1, defaultLibrary.id, new Date('1970-01-01')); + const newParent = await createAsset(user1, new Date('1970-01-01')); const { status } = await request(server) .put('/asset') .set('Authorization', `Bearer ${user1.accessToken}`) diff --git a/server/test/repositories/asset.repository.mock.ts b/server/test/repositories/asset.repository.mock.ts index 3b584e09c..566b7733a 100644 --- a/server/test/repositories/asset.repository.mock.ts +++ b/server/test/repositories/asset.repository.mock.ts @@ -31,5 +31,6 @@ export const newAssetRepositoryMock = (): jest.Mocked => { getTimeBuckets: jest.fn(), restoreAll: jest.fn(), softDeleteAll: jest.fn(), + search: jest.fn(), }; }; diff --git a/server/test/test-utils.ts b/server/test/test-utils.ts index 4ac0cf0bf..819d04556 100644 --- a/server/test/test-utils.ts +++ b/server/test/test-utils.ts @@ -5,25 +5,39 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import * as fs from 'fs'; import path from 'path'; +import { EntityTarget, ObjectLiteral } from 'typeorm'; import { AppService } from '../src/microservices/app.service'; export const IMMICH_TEST_ASSET_PATH = process.env.IMMICH_TEST_ASSET_PATH; export const IMMICH_TEST_ASSET_TEMP_PATH = path.normalize(`${IMMICH_TEST_ASSET_PATH}/temp/`); +export interface ResetOptions { + entities?: EntityTarget[]; +} export const db = { - reset: async () => { + reset: async (options?: ResetOptions) => { if (!dataSource.isInitialized) { await dataSource.initialize(); } await dataSource.transaction(async (em) => { - for (const entity of dataSource.entityMetadatas) { - if (entity.tableName === 'users') { + const entities = options?.entities || []; + const tableNames = + entities.length > 0 + ? entities.map((entity) => em.getRepository(entity).metadata.tableName) + : dataSource.entityMetadatas.map((entity) => entity.tableName); + + let deleteUsers = false; + for (const tableName of tableNames) { + if (tableName === 'users') { + deleteUsers = true; continue; } - await em.query(`DELETE FROM ${entity.tableName} CASCADE;`); + await em.query(`DELETE FROM ${tableName} CASCADE;`); + } + if (deleteUsers) { + await em.query(`DELETE FROM "users" CASCADE;`); } - await em.query(`DELETE FROM "users" CASCADE;`); }); }, disconnect: async () => { diff --git a/web/src/api/open-api/api.ts b/web/src/api/open-api/api.ts index ecb0fb39e..3828babd7 100644 --- a/web/src/api/open-api/api.ts +++ b/web/src/api/open-api/api.ts @@ -670,6 +670,20 @@ export interface AssetJobsDto { } +/** + * + * @export + * @enum {string} + */ + +export const AssetOrder = { + Asc: 'asc', + Desc: 'desc' +} as const; + +export type AssetOrder = typeof AssetOrder[keyof typeof AssetOrder]; + + /** * * @export @@ -7822,6 +7836,260 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration options: localVarRequestOptions, }; }, + /** + * + * @param {string} [id] + * @param {string} [libraryId] + * @param {AssetTypeEnum} [type] + * @param {AssetOrder} [order] + * @param {string} [deviceAssetId] + * @param {string} [deviceId] + * @param {string} [checksum] + * @param {boolean} [isArchived] + * @param {boolean} [isEncoded] + * @param {boolean} [isExternal] + * @param {boolean} [isFavorite] + * @param {boolean} [isMotion] + * @param {boolean} [isOffline] + * @param {boolean} [isReadOnly] + * @param {boolean} [isVisible] + * @param {boolean} [withDeleted] + * @param {boolean} [withStacked] + * @param {boolean} [withExif] + * @param {boolean} [withPeople] + * @param {string} [createdBefore] + * @param {string} [createdAfter] + * @param {string} [updatedBefore] + * @param {string} [updatedAfter] + * @param {string} [trashedBefore] + * @param {string} [trashedAfter] + * @param {string} [takenBefore] + * @param {string} [takenAfter] + * @param {string} [originalFileName] + * @param {string} [originalPath] + * @param {string} [resizePath] + * @param {string} [webpPath] + * @param {string} [encodedVideoPath] + * @param {string} [city] + * @param {string} [state] + * @param {string} [country] + * @param {string} [make] + * @param {string} [model] + * @param {string} [lensModel] + * @param {number} [page] + * @param {number} [size] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + searchAssets: async (id?: string, libraryId?: string, type?: AssetTypeEnum, order?: AssetOrder, deviceAssetId?: string, deviceId?: string, checksum?: string, isArchived?: boolean, isEncoded?: boolean, isExternal?: boolean, isFavorite?: boolean, isMotion?: boolean, isOffline?: boolean, isReadOnly?: boolean, isVisible?: boolean, withDeleted?: boolean, withStacked?: boolean, withExif?: boolean, withPeople?: boolean, createdBefore?: string, createdAfter?: string, updatedBefore?: string, updatedAfter?: string, trashedBefore?: string, trashedAfter?: string, takenBefore?: string, takenAfter?: string, originalFileName?: string, originalPath?: string, resizePath?: string, webpPath?: string, encodedVideoPath?: string, city?: string, state?: string, country?: string, make?: string, model?: string, lensModel?: string, page?: number, size?: number, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/assets`; + // 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) + + if (id !== undefined) { + localVarQueryParameter['id'] = id; + } + + if (libraryId !== undefined) { + localVarQueryParameter['libraryId'] = libraryId; + } + + if (type !== undefined) { + localVarQueryParameter['type'] = type; + } + + if (order !== undefined) { + localVarQueryParameter['order'] = order; + } + + if (deviceAssetId !== undefined) { + localVarQueryParameter['deviceAssetId'] = deviceAssetId; + } + + if (deviceId !== undefined) { + localVarQueryParameter['deviceId'] = deviceId; + } + + if (checksum !== undefined) { + localVarQueryParameter['checksum'] = checksum; + } + + if (isArchived !== undefined) { + localVarQueryParameter['isArchived'] = isArchived; + } + + if (isEncoded !== undefined) { + localVarQueryParameter['isEncoded'] = isEncoded; + } + + if (isExternal !== undefined) { + localVarQueryParameter['isExternal'] = isExternal; + } + + if (isFavorite !== undefined) { + localVarQueryParameter['isFavorite'] = isFavorite; + } + + if (isMotion !== undefined) { + localVarQueryParameter['isMotion'] = isMotion; + } + + if (isOffline !== undefined) { + localVarQueryParameter['isOffline'] = isOffline; + } + + if (isReadOnly !== undefined) { + localVarQueryParameter['isReadOnly'] = isReadOnly; + } + + if (isVisible !== undefined) { + localVarQueryParameter['isVisible'] = isVisible; + } + + if (withDeleted !== undefined) { + localVarQueryParameter['withDeleted'] = withDeleted; + } + + if (withStacked !== undefined) { + localVarQueryParameter['withStacked'] = withStacked; + } + + if (withExif !== undefined) { + localVarQueryParameter['withExif'] = withExif; + } + + if (withPeople !== undefined) { + localVarQueryParameter['withPeople'] = withPeople; + } + + if (createdBefore !== undefined) { + localVarQueryParameter['createdBefore'] = (createdBefore as any instanceof Date) ? + (createdBefore as any).toISOString() : + createdBefore; + } + + if (createdAfter !== undefined) { + localVarQueryParameter['createdAfter'] = (createdAfter as any instanceof Date) ? + (createdAfter as any).toISOString() : + createdAfter; + } + + if (updatedBefore !== undefined) { + localVarQueryParameter['updatedBefore'] = (updatedBefore as any instanceof Date) ? + (updatedBefore as any).toISOString() : + updatedBefore; + } + + if (updatedAfter !== undefined) { + localVarQueryParameter['updatedAfter'] = (updatedAfter as any instanceof Date) ? + (updatedAfter as any).toISOString() : + updatedAfter; + } + + if (trashedBefore !== undefined) { + localVarQueryParameter['trashedBefore'] = (trashedBefore as any instanceof Date) ? + (trashedBefore as any).toISOString() : + trashedBefore; + } + + if (trashedAfter !== undefined) { + localVarQueryParameter['trashedAfter'] = (trashedAfter as any instanceof Date) ? + (trashedAfter as any).toISOString() : + trashedAfter; + } + + if (takenBefore !== undefined) { + localVarQueryParameter['takenBefore'] = (takenBefore as any instanceof Date) ? + (takenBefore as any).toISOString() : + takenBefore; + } + + if (takenAfter !== undefined) { + localVarQueryParameter['takenAfter'] = (takenAfter as any instanceof Date) ? + (takenAfter as any).toISOString() : + takenAfter; + } + + if (originalFileName !== undefined) { + localVarQueryParameter['originalFileName'] = originalFileName; + } + + if (originalPath !== undefined) { + localVarQueryParameter['originalPath'] = originalPath; + } + + if (resizePath !== undefined) { + localVarQueryParameter['resizePath'] = resizePath; + } + + if (webpPath !== undefined) { + localVarQueryParameter['webpPath'] = webpPath; + } + + if (encodedVideoPath !== undefined) { + localVarQueryParameter['encodedVideoPath'] = encodedVideoPath; + } + + if (city !== undefined) { + localVarQueryParameter['city'] = city; + } + + if (state !== undefined) { + localVarQueryParameter['state'] = state; + } + + if (country !== undefined) { + localVarQueryParameter['country'] = country; + } + + if (make !== undefined) { + localVarQueryParameter['make'] = make; + } + + if (model !== undefined) { + localVarQueryParameter['model'] = model; + } + + if (lensModel !== undefined) { + localVarQueryParameter['lensModel'] = lensModel; + } + + if (page !== undefined) { + localVarQueryParameter['page'] = page; + } + + if (size !== undefined) { + localVarQueryParameter['size'] = size; + } + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, /** * * @param {string} id @@ -8440,6 +8708,55 @@ export const AssetApiFp = function(configuration?: Configuration) { const localVarAxiosArgs = await localVarAxiosParamCreator.searchAsset(searchAssetDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, + /** + * + * @param {string} [id] + * @param {string} [libraryId] + * @param {AssetTypeEnum} [type] + * @param {AssetOrder} [order] + * @param {string} [deviceAssetId] + * @param {string} [deviceId] + * @param {string} [checksum] + * @param {boolean} [isArchived] + * @param {boolean} [isEncoded] + * @param {boolean} [isExternal] + * @param {boolean} [isFavorite] + * @param {boolean} [isMotion] + * @param {boolean} [isOffline] + * @param {boolean} [isReadOnly] + * @param {boolean} [isVisible] + * @param {boolean} [withDeleted] + * @param {boolean} [withStacked] + * @param {boolean} [withExif] + * @param {boolean} [withPeople] + * @param {string} [createdBefore] + * @param {string} [createdAfter] + * @param {string} [updatedBefore] + * @param {string} [updatedAfter] + * @param {string} [trashedBefore] + * @param {string} [trashedAfter] + * @param {string} [takenBefore] + * @param {string} [takenAfter] + * @param {string} [originalFileName] + * @param {string} [originalPath] + * @param {string} [resizePath] + * @param {string} [webpPath] + * @param {string} [encodedVideoPath] + * @param {string} [city] + * @param {string} [state] + * @param {string} [country] + * @param {string} [make] + * @param {string} [model] + * @param {string} [lensModel] + * @param {number} [page] + * @param {number} [size] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async searchAssets(id?: string, libraryId?: string, type?: AssetTypeEnum, order?: AssetOrder, deviceAssetId?: string, deviceId?: string, checksum?: string, isArchived?: boolean, isEncoded?: boolean, isExternal?: boolean, isFavorite?: boolean, isMotion?: boolean, isOffline?: boolean, isReadOnly?: boolean, isVisible?: boolean, withDeleted?: boolean, withStacked?: boolean, withExif?: boolean, withPeople?: boolean, createdBefore?: string, createdAfter?: string, updatedBefore?: string, updatedAfter?: string, trashedBefore?: string, trashedAfter?: string, takenBefore?: string, takenAfter?: string, originalFileName?: string, originalPath?: string, resizePath?: string, webpPath?: string, encodedVideoPath?: string, city?: string, state?: string, country?: string, make?: string, model?: string, lensModel?: string, page?: number, size?: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.searchAssets(id, libraryId, type, order, deviceAssetId, deviceId, checksum, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, withDeleted, withStacked, withExif, withPeople, createdBefore, createdAfter, updatedBefore, updatedAfter, trashedBefore, trashedAfter, takenBefore, takenAfter, originalFileName, originalPath, resizePath, webpPath, encodedVideoPath, city, state, country, make, model, lensModel, page, size, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, /** * * @param {string} id @@ -8739,6 +9056,15 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath searchAsset(requestParameters: AssetApiSearchAssetRequest, options?: AxiosRequestConfig): AxiosPromise> { return localVarFp.searchAsset(requestParameters.searchAssetDto, options).then((request) => request(axios, basePath)); }, + /** + * + * @param {AssetApiSearchAssetsRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + searchAssets(requestParameters: AssetApiSearchAssetsRequest = {}, options?: AxiosRequestConfig): AxiosPromise> { + return localVarFp.searchAssets(requestParameters.id, requestParameters.libraryId, requestParameters.type, requestParameters.order, requestParameters.deviceAssetId, requestParameters.deviceId, requestParameters.checksum, requestParameters.isArchived, requestParameters.isEncoded, requestParameters.isExternal, requestParameters.isFavorite, requestParameters.isMotion, requestParameters.isOffline, requestParameters.isReadOnly, requestParameters.isVisible, requestParameters.withDeleted, requestParameters.withStacked, requestParameters.withExif, requestParameters.withPeople, requestParameters.createdBefore, requestParameters.createdAfter, requestParameters.updatedBefore, requestParameters.updatedAfter, requestParameters.trashedBefore, requestParameters.trashedAfter, requestParameters.takenBefore, requestParameters.takenAfter, requestParameters.originalFileName, requestParameters.originalPath, requestParameters.resizePath, requestParameters.webpPath, requestParameters.encodedVideoPath, requestParameters.city, requestParameters.state, requestParameters.country, requestParameters.make, requestParameters.model, requestParameters.lensModel, requestParameters.page, requestParameters.size, options).then((request) => request(axios, basePath)); + }, /** * * @param {AssetApiServeFileRequest} requestParameters Request parameters. @@ -9333,6 +9659,293 @@ export interface AssetApiSearchAssetRequest { readonly searchAssetDto: SearchAssetDto } +/** + * Request parameters for searchAssets operation in AssetApi. + * @export + * @interface AssetApiSearchAssetsRequest + */ +export interface AssetApiSearchAssetsRequest { + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly id?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly libraryId?: string + + /** + * + * @type {AssetTypeEnum} + * @memberof AssetApiSearchAssets + */ + readonly type?: AssetTypeEnum + + /** + * + * @type {AssetOrder} + * @memberof AssetApiSearchAssets + */ + readonly order?: AssetOrder + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly deviceAssetId?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly deviceId?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly checksum?: string + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly isArchived?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly isEncoded?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly isExternal?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly isFavorite?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly isMotion?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly isOffline?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly isReadOnly?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly isVisible?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly withDeleted?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly withStacked?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly withExif?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiSearchAssets + */ + readonly withPeople?: boolean + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly createdBefore?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly createdAfter?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly updatedBefore?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly updatedAfter?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly trashedBefore?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly trashedAfter?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly takenBefore?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly takenAfter?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly originalFileName?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly originalPath?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly resizePath?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly webpPath?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly encodedVideoPath?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly city?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly state?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly country?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly make?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly model?: string + + /** + * + * @type {string} + * @memberof AssetApiSearchAssets + */ + readonly lensModel?: string + + /** + * + * @type {number} + * @memberof AssetApiSearchAssets + */ + readonly page?: number + + /** + * + * @type {number} + * @memberof AssetApiSearchAssets + */ + readonly size?: number +} + /** * Request parameters for serveFile operation in AssetApi. * @export @@ -9813,6 +10426,17 @@ export class AssetApi extends BaseAPI { return AssetApiFp(this.configuration).searchAsset(requestParameters.searchAssetDto, options).then((request) => request(this.axios, this.basePath)); } + /** + * + * @param {AssetApiSearchAssetsRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof AssetApi + */ + public searchAssets(requestParameters: AssetApiSearchAssetsRequest = {}, options?: AxiosRequestConfig) { + return AssetApiFp(this.configuration).searchAssets(requestParameters.id, requestParameters.libraryId, requestParameters.type, requestParameters.order, requestParameters.deviceAssetId, requestParameters.deviceId, requestParameters.checksum, requestParameters.isArchived, requestParameters.isEncoded, requestParameters.isExternal, requestParameters.isFavorite, requestParameters.isMotion, requestParameters.isOffline, requestParameters.isReadOnly, requestParameters.isVisible, requestParameters.withDeleted, requestParameters.withStacked, requestParameters.withExif, requestParameters.withPeople, requestParameters.createdBefore, requestParameters.createdAfter, requestParameters.updatedBefore, requestParameters.updatedAfter, requestParameters.trashedBefore, requestParameters.trashedAfter, requestParameters.takenBefore, requestParameters.takenAfter, requestParameters.originalFileName, requestParameters.originalPath, requestParameters.resizePath, requestParameters.webpPath, requestParameters.encodedVideoPath, requestParameters.city, requestParameters.state, requestParameters.country, requestParameters.make, requestParameters.model, requestParameters.lensModel, requestParameters.page, requestParameters.size, options).then((request) => request(this.axios, this.basePath)); + } + /** * * @param {AssetApiServeFileRequest} requestParameters Request parameters.