1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-01-01 08:31:59 +00:00

refactor(server): trash endpoints (#6652)

* refactor(server): trash endpoints

* chore: open api

* chore: fix wrong rename
This commit is contained in:
Jason Rasmussen 2024-01-26 11:48:37 -05:00 committed by GitHub
parent 33757689fe
commit 96b7885583
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 601 additions and 104 deletions

View file

@ -22,7 +22,7 @@ class TrashService {
try { try {
List<String> remoteIds = List<String> remoteIds =
assetList.where((a) => a.isRemote).map((e) => e.remoteId!).toList(); assetList.where((a) => a.isRemote).map((e) => e.remoteId!).toList();
await _apiService.assetApi.restoreAssets(BulkIdsDto(ids: remoteIds)); await _apiService.assetApi.restoreAssetsOld(BulkIdsDto(ids: remoteIds));
return true; return true;
} catch (error, stack) { } catch (error, stack) {
_log.severe("Cannot restore assets ${error.toString()}", error, stack); _log.severe("Cannot restore assets ${error.toString()}", error, stack);
@ -32,7 +32,7 @@ class TrashService {
Future<void> emptyTrash() async { Future<void> emptyTrash() async {
try { try {
await _apiService.assetApi.emptyTrash(); await _apiService.assetApi.emptyTrashOld();
} catch (error, stack) { } catch (error, stack) {
_log.severe("Cannot empty trash ${error.toString()}", error, stack); _log.severe("Cannot empty trash ${error.toString()}", error, stack);
} }
@ -40,7 +40,7 @@ class TrashService {
Future<void> restoreTrash() async { Future<void> restoreTrash() async {
try { try {
await _apiService.assetApi.restoreTrash(); await _apiService.assetApi.restoreTrashOld();
} catch (error, stack) { } catch (error, stack) {
_log.severe("Cannot restore trash ${error.toString()}", error, stack); _log.severe("Cannot restore trash ${error.toString()}", error, stack);
} }

View file

@ -165,6 +165,7 @@ doc/TimeBucketSize.md
doc/ToneMapping.md doc/ToneMapping.md
doc/TranscodeHWAccel.md doc/TranscodeHWAccel.md
doc/TranscodePolicy.md doc/TranscodePolicy.md
doc/TrashApi.md
doc/UpdateAlbumDto.md doc/UpdateAlbumDto.md
doc/UpdateAssetDto.md doc/UpdateAssetDto.md
doc/UpdateLibraryDto.md doc/UpdateLibraryDto.md
@ -199,6 +200,7 @@ lib/api/server_info_api.dart
lib/api/shared_link_api.dart lib/api/shared_link_api.dart
lib/api/system_config_api.dart lib/api/system_config_api.dart
lib/api/tag_api.dart lib/api/tag_api.dart
lib/api/trash_api.dart
lib/api/user_api.dart lib/api/user_api.dart
lib/api_client.dart lib/api_client.dart
lib/api_exception.dart lib/api_exception.dart
@ -528,6 +530,7 @@ test/time_bucket_size_test.dart
test/tone_mapping_test.dart test/tone_mapping_test.dart
test/transcode_hw_accel_test.dart test/transcode_hw_accel_test.dart
test/transcode_policy_test.dart test/transcode_policy_test.dart
test/trash_api_test.dart
test/update_album_dto_test.dart test/update_album_dto_test.dart
test/update_asset_dto_test.dart test/update_asset_dto_test.dart
test/update_library_dto_test.dart test/update_library_dto_test.dart

BIN
mobile/openapi/README.md generated

Binary file not shown.

Binary file not shown.

BIN
mobile/openapi/doc/TrashApi.md generated Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
mobile/openapi/lib/api/trash_api.dart generated Normal file

Binary file not shown.

Binary file not shown.

BIN
mobile/openapi/test/trash_api_test.dart generated Normal file

Binary file not shown.

View file

@ -1734,7 +1734,7 @@
}, },
"/asset/restore": { "/asset/restore": {
"post": { "post": {
"operationId": "restoreAssets", "operationId": "restoreAssetsOld",
"parameters": [], "parameters": [],
"requestBody": { "requestBody": {
"content": { "content": {
@ -2219,7 +2219,7 @@
}, },
"/asset/trash/empty": { "/asset/trash/empty": {
"post": { "post": {
"operationId": "emptyTrash", "operationId": "emptyTrashOld",
"parameters": [], "parameters": [],
"responses": { "responses": {
"204": { "204": {
@ -2244,7 +2244,7 @@
}, },
"/asset/trash/restore": { "/asset/trash/restore": {
"post": { "post": {
"operationId": "restoreTrash", "operationId": "restoreTrashOld",
"parameters": [], "parameters": [],
"responses": { "responses": {
"204": { "204": {
@ -5983,6 +5983,91 @@
] ]
} }
}, },
"/trash/empty": {
"post": {
"operationId": "emptyTrash",
"parameters": [],
"responses": {
"204": {
"description": ""
}
},
"security": [
{
"bearer": []
},
{
"cookie": []
},
{
"api_key": []
}
],
"tags": [
"Trash"
]
}
},
"/trash/restore": {
"post": {
"operationId": "restoreTrash",
"parameters": [],
"responses": {
"204": {
"description": ""
}
},
"security": [
{
"bearer": []
},
{
"cookie": []
},
{
"api_key": []
}
],
"tags": [
"Trash"
]
}
},
"/trash/restore/assets": {
"post": {
"operationId": "restoreAssets",
"parameters": [],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BulkIdsDto"
}
}
},
"required": true
},
"responses": {
"204": {
"description": ""
}
},
"security": [
{
"bearer": []
},
{
"cookie": []
},
{
"api_key": []
}
],
"tags": [
"Trash"
]
}
},
"/user": { "/user": {
"get": { "get": {
"operationId": "getAllUsers", "operationId": "getAllUsers",

View file

@ -7026,7 +7026,7 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
emptyTrash: async (options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => { emptyTrashOld: async (options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/asset/trash/empty`; const localVarPath = `/asset/trash/empty`;
// use dummy base URL string because the URL constructor only accepts absolute URLs. // use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
@ -7896,9 +7896,9 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
restoreAssets: async (bulkIdsDto: BulkIdsDto, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => { restoreAssetsOld: async (bulkIdsDto: BulkIdsDto, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'bulkIdsDto' is not null or undefined // verify required parameter 'bulkIdsDto' is not null or undefined
assertParamExists('restoreAssets', 'bulkIdsDto', bulkIdsDto) assertParamExists('restoreAssetsOld', 'bulkIdsDto', bulkIdsDto)
const localVarPath = `/asset/restore`; const localVarPath = `/asset/restore`;
// use dummy base URL string because the URL constructor only accepts absolute URLs. // use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
@ -7939,7 +7939,7 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
restoreTrash: async (options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => { restoreTrashOld: async (options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/asset/trash/restore`; const localVarPath = `/asset/trash/restore`;
// use dummy base URL string because the URL constructor only accepts absolute URLs. // use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
@ -8672,10 +8672,10 @@ export const AssetApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async emptyTrash(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> { async emptyTrashOld(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.emptyTrash(options); const localVarAxiosArgs = await localVarAxiosParamCreator.emptyTrashOld(options);
const index = configuration?.serverIndex ?? 0; const index = configuration?.serverIndex ?? 0;
const operationBasePath = operationServerMap['AssetApi.emptyTrash']?.[index]?.url; const operationBasePath = operationServerMap['AssetApi.emptyTrashOld']?.[index]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath);
}, },
/** /**
@ -8899,10 +8899,10 @@ export const AssetApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async restoreAssets(bulkIdsDto: BulkIdsDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> { async restoreAssetsOld(bulkIdsDto: BulkIdsDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.restoreAssets(bulkIdsDto, options); const localVarAxiosArgs = await localVarAxiosParamCreator.restoreAssetsOld(bulkIdsDto, options);
const index = configuration?.serverIndex ?? 0; const index = configuration?.serverIndex ?? 0;
const operationBasePath = operationServerMap['AssetApi.restoreAssets']?.[index]?.url; const operationBasePath = operationServerMap['AssetApi.restoreAssetsOld']?.[index]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath);
}, },
/** /**
@ -8910,10 +8910,10 @@ export const AssetApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async restoreTrash(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> { async restoreTrashOld(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.restoreTrash(options); const localVarAxiosArgs = await localVarAxiosParamCreator.restoreTrashOld(options);
const index = configuration?.serverIndex ?? 0; const index = configuration?.serverIndex ?? 0;
const operationBasePath = operationServerMap['AssetApi.restoreTrash']?.[index]?.url; const operationBasePath = operationServerMap['AssetApi.restoreTrashOld']?.[index]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath);
}, },
/** /**
@ -9118,8 +9118,8 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
emptyTrash(options?: RawAxiosRequestConfig): AxiosPromise<void> { emptyTrashOld(options?: RawAxiosRequestConfig): AxiosPromise<void> {
return localVarFp.emptyTrash(options).then((request) => request(axios, basePath)); return localVarFp.emptyTrashOld(options).then((request) => request(axios, basePath));
}, },
/** /**
* Get all AssetEntity belong to the user * Get all AssetEntity belong to the user
@ -9256,20 +9256,20 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
}, },
/** /**
* *
* @param {AssetApiRestoreAssetsRequest} requestParameters Request parameters. * @param {AssetApiRestoreAssetsOldRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
restoreAssets(requestParameters: AssetApiRestoreAssetsRequest, options?: RawAxiosRequestConfig): AxiosPromise<void> { restoreAssetsOld(requestParameters: AssetApiRestoreAssetsOldRequest, options?: RawAxiosRequestConfig): AxiosPromise<void> {
return localVarFp.restoreAssets(requestParameters.bulkIdsDto, options).then((request) => request(axios, basePath)); return localVarFp.restoreAssetsOld(requestParameters.bulkIdsDto, options).then((request) => request(axios, basePath));
}, },
/** /**
* *
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
restoreTrash(options?: RawAxiosRequestConfig): AxiosPromise<void> { restoreTrashOld(options?: RawAxiosRequestConfig): AxiosPromise<void> {
return localVarFp.restoreTrash(options).then((request) => request(axios, basePath)); return localVarFp.restoreTrashOld(options).then((request) => request(axios, basePath));
}, },
/** /**
* *
@ -9849,15 +9849,15 @@ export interface AssetApiGetTimeBucketsRequest {
} }
/** /**
* Request parameters for restoreAssets operation in AssetApi. * Request parameters for restoreAssetsOld operation in AssetApi.
* @export * @export
* @interface AssetApiRestoreAssetsRequest * @interface AssetApiRestoreAssetsOldRequest
*/ */
export interface AssetApiRestoreAssetsRequest { export interface AssetApiRestoreAssetsOldRequest {
/** /**
* *
* @type {BulkIdsDto} * @type {BulkIdsDto}
* @memberof AssetApiRestoreAssets * @memberof AssetApiRestoreAssetsOld
*/ */
readonly bulkIdsDto: BulkIdsDto readonly bulkIdsDto: BulkIdsDto
} }
@ -10434,8 +10434,8 @@ export class AssetApi extends BaseAPI {
* @throws {RequiredError} * @throws {RequiredError}
* @memberof AssetApi * @memberof AssetApi
*/ */
public emptyTrash(options?: RawAxiosRequestConfig) { public emptyTrashOld(options?: RawAxiosRequestConfig) {
return AssetApiFp(this.configuration).emptyTrash(options).then((request) => request(this.axios, this.basePath)); return AssetApiFp(this.configuration).emptyTrashOld(options).then((request) => request(this.axios, this.basePath));
} }
/** /**
@ -10603,13 +10603,13 @@ export class AssetApi extends BaseAPI {
/** /**
* *
* @param {AssetApiRestoreAssetsRequest} requestParameters Request parameters. * @param {AssetApiRestoreAssetsOldRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
* @memberof AssetApi * @memberof AssetApi
*/ */
public restoreAssets(requestParameters: AssetApiRestoreAssetsRequest, options?: RawAxiosRequestConfig) { public restoreAssetsOld(requestParameters: AssetApiRestoreAssetsOldRequest, options?: RawAxiosRequestConfig) {
return AssetApiFp(this.configuration).restoreAssets(requestParameters.bulkIdsDto, options).then((request) => request(this.axios, this.basePath)); return AssetApiFp(this.configuration).restoreAssetsOld(requestParameters.bulkIdsDto, options).then((request) => request(this.axios, this.basePath));
} }
/** /**
@ -10618,8 +10618,8 @@ export class AssetApi extends BaseAPI {
* @throws {RequiredError} * @throws {RequiredError}
* @memberof AssetApi * @memberof AssetApi
*/ */
public restoreTrash(options?: RawAxiosRequestConfig) { public restoreTrashOld(options?: RawAxiosRequestConfig) {
return AssetApiFp(this.configuration).restoreTrash(options).then((request) => request(this.axios, this.basePath)); return AssetApiFp(this.configuration).restoreTrashOld(options).then((request) => request(this.axios, this.basePath));
} }
/** /**
@ -18135,6 +18135,269 @@ export class TagApi extends BaseAPI {
/**
* TrashApi - axios parameter creator
* @export
*/
export const TrashApiAxiosParamCreator = function (configuration?: Configuration) {
return {
/**
*
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
emptyTrash: async (options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/trash/empty`;
// 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: 'POST', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication cookie required
// authentication api_key required
await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration)
// authentication bearer required
// http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration)
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @param {BulkIdsDto} bulkIdsDto
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
restoreAssets: async (bulkIdsDto: BulkIdsDto, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'bulkIdsDto' is not null or undefined
assertParamExists('restoreAssets', 'bulkIdsDto', bulkIdsDto)
const localVarPath = `/trash/restore/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: 'POST', ...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)
localVarHeaderParameter['Content-Type'] = 'application/json';
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
localVarRequestOptions.data = serializeDataIfNeeded(bulkIdsDto, localVarRequestOptions, configuration)
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
restoreTrash: async (options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/trash/restore`;
// 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: 'POST', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication cookie required
// authentication api_key required
await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration)
// authentication bearer required
// http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration)
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
}
};
/**
* TrashApi - functional programming interface
* @export
*/
export const TrashApiFp = function(configuration?: Configuration) {
const localVarAxiosParamCreator = TrashApiAxiosParamCreator(configuration)
return {
/**
*
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async emptyTrash(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.emptyTrash(options);
const index = configuration?.serverIndex ?? 0;
const operationBasePath = operationServerMap['TrashApi.emptyTrash']?.[index]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath);
},
/**
*
* @param {BulkIdsDto} bulkIdsDto
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async restoreAssets(bulkIdsDto: BulkIdsDto, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.restoreAssets(bulkIdsDto, options);
const index = configuration?.serverIndex ?? 0;
const operationBasePath = operationServerMap['TrashApi.restoreAssets']?.[index]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath);
},
/**
*
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async restoreTrash(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.restoreTrash(options);
const index = configuration?.serverIndex ?? 0;
const operationBasePath = operationServerMap['TrashApi.restoreTrash']?.[index]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath);
},
}
};
/**
* TrashApi - factory interface
* @export
*/
export const TrashApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
const localVarFp = TrashApiFp(configuration)
return {
/**
*
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
emptyTrash(options?: RawAxiosRequestConfig): AxiosPromise<void> {
return localVarFp.emptyTrash(options).then((request) => request(axios, basePath));
},
/**
*
* @param {TrashApiRestoreAssetsRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
restoreAssets(requestParameters: TrashApiRestoreAssetsRequest, options?: RawAxiosRequestConfig): AxiosPromise<void> {
return localVarFp.restoreAssets(requestParameters.bulkIdsDto, options).then((request) => request(axios, basePath));
},
/**
*
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
restoreTrash(options?: RawAxiosRequestConfig): AxiosPromise<void> {
return localVarFp.restoreTrash(options).then((request) => request(axios, basePath));
},
};
};
/**
* Request parameters for restoreAssets operation in TrashApi.
* @export
* @interface TrashApiRestoreAssetsRequest
*/
export interface TrashApiRestoreAssetsRequest {
/**
*
* @type {BulkIdsDto}
* @memberof TrashApiRestoreAssets
*/
readonly bulkIdsDto: BulkIdsDto
}
/**
* TrashApi - object-oriented interface
* @export
* @class TrashApi
* @extends {BaseAPI}
*/
export class TrashApi extends BaseAPI {
/**
*
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof TrashApi
*/
public emptyTrash(options?: RawAxiosRequestConfig) {
return TrashApiFp(this.configuration).emptyTrash(options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @param {TrashApiRestoreAssetsRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof TrashApi
*/
public restoreAssets(requestParameters: TrashApiRestoreAssetsRequest, options?: RawAxiosRequestConfig) {
return TrashApiFp(this.configuration).restoreAssets(requestParameters.bulkIdsDto, options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof TrashApi
*/
public restoreTrash(options?: RawAxiosRequestConfig) {
return TrashApiFp(this.configuration).restoreTrash(options).then((request) => request(this.axios, this.basePath));
}
}
/** /**
* UserApi - axios parameter creator * UserApi - axios parameter creator
* @export * @export

View file

@ -679,25 +679,6 @@ describe(AssetService.name, () => {
}); });
}); });
describe('restoreAll', () => {
it('should require asset restore access for all ids', async () => {
await expect(
sut.deleteAll(authStub.user1, {
ids: ['asset-1'],
}),
).rejects.toBeInstanceOf(BadRequestException);
});
it('should restore a batch of assets', async () => {
accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset1', 'asset2']));
await sut.restoreAll(authStub.user1, { ids: ['asset1', 'asset2'] });
expect(assetMock.restoreAll).toHaveBeenCalledWith(['asset1', 'asset2']);
expect(jobMock.queue.mock.calls).toEqual([]);
});
});
describe('handleAssetDeletion', () => { describe('handleAssetDeletion', () => {
beforeEach(() => { beforeEach(() => {
when(jobMock.queue) when(jobMock.queue)

View file

@ -37,14 +37,12 @@ import {
MemoryLaneDto, MemoryLaneDto,
TimeBucketAssetDto, TimeBucketAssetDto,
TimeBucketDto, TimeBucketDto,
TrashAction,
UpdateAssetDto, UpdateAssetDto,
UpdateStackParentDto, UpdateStackParentDto,
mapStats, mapStats,
} from './dto'; } from './dto';
import { import {
AssetResponseDto, AssetResponseDto,
BulkIdsDto,
MapMarkerResponseDto, MapMarkerResponseDto,
MemoryLaneResponseDto, MemoryLaneResponseDto,
SanitizedAssetResponseDto, SanitizedAssetResponseDto,
@ -451,37 +449,6 @@ export class AssetService {
} }
} }
async handleTrashAction(auth: AuthDto, action: TrashAction): Promise<void> {
const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) =>
this.assetRepository.getByUserId(pagination, auth.user.id, { trashedBefore: DateTime.now().toJSDate() }),
);
if (action == TrashAction.RESTORE_ALL) {
for await (const assets of assetPagination) {
const ids = assets.map((a) => a.id);
await this.assetRepository.restoreAll(ids);
this.communicationRepository.send(ClientEvent.ASSET_RESTORE, auth.user.id, ids);
}
return;
}
if (action == TrashAction.EMPTY_ALL) {
for await (const assets of assetPagination) {
await this.jobRepository.queueAll(
assets.map((asset) => ({ name: JobName.ASSET_DELETION, data: { id: asset.id } })),
);
}
return;
}
}
async restoreAll(auth: AuthDto, dto: BulkIdsDto): Promise<void> {
const { ids } = dto;
await this.access.requirePermission(auth, Permission.ASSET_RESTORE, ids);
await this.assetRepository.restoreAll(ids);
this.communicationRepository.send(ClientEvent.ASSET_RESTORE, auth.user.id, ids);
}
async updateStackParent(auth: AuthDto, dto: UpdateStackParentDto): Promise<void> { async updateStackParent(auth: AuthDto, dto: UpdateStackParentDto): Promise<void> {
const { oldParentId, newParentId } = dto; const { oldParentId, newParentId } = dto;
await this.access.requirePermission(auth, Permission.ASSET_READ, oldParentId); await this.access.requirePermission(auth, Permission.ASSET_READ, oldParentId);

View file

@ -246,11 +246,6 @@ export class RandomAssetsDto {
count?: number; count?: number;
} }
export enum TrashAction {
EMPTY_ALL = 'empty-all',
RESTORE_ALL = 'restore-all',
}
export class AssetBulkDeleteDto extends BulkIdsDto { export class AssetBulkDeleteDto extends BulkIdsDto {
@Optional() @Optional()
@IsBoolean() @IsBoolean()

View file

@ -22,6 +22,7 @@ import { StorageService } from './storage';
import { StorageTemplateService } from './storage-template'; import { StorageTemplateService } from './storage-template';
import { SystemConfigService } from './system-config'; import { SystemConfigService } from './system-config';
import { TagService } from './tag'; import { TagService } from './tag';
import { TrashService } from './trash';
import { UserService } from './user'; import { UserService } from './user';
const providers: Provider[] = [ const providers: Provider[] = [
@ -48,6 +49,7 @@ const providers: Provider[] = [
StorageTemplateService, StorageTemplateService,
SystemConfigService, SystemConfigService,
TagService, TagService,
TrashService,
UserService, UserService,
]; ];

View file

@ -26,4 +26,5 @@ export * from './storage';
export * from './storage-template'; export * from './storage-template';
export * from './system-config'; export * from './system-config';
export * from './tag'; export * from './tag';
export * from './trash';
export * from './user'; export * from './user';

View file

@ -0,0 +1 @@
export * from './trash.service';

View file

@ -0,0 +1,87 @@
import { BadRequestException } from '@nestjs/common';
import {
IAccessRepositoryMock,
assetStub,
authStub,
newAccessRepositoryMock,
newAssetRepositoryMock,
newCommunicationRepositoryMock,
newJobRepositoryMock,
} from '@test';
import { JobName } from '..';
import { ClientEvent, IAssetRepository, ICommunicationRepository, IJobRepository } from '../repositories';
import { TrashService } from './trash.service';
describe(TrashService.name, () => {
let sut: TrashService;
let accessMock: IAccessRepositoryMock;
let assetMock: jest.Mocked<IAssetRepository>;
let jobMock: jest.Mocked<IJobRepository>;
let communicationMock: jest.Mocked<ICommunicationRepository>;
it('should work', () => {
expect(sut).toBeDefined();
});
beforeEach(async () => {
accessMock = newAccessRepositoryMock();
assetMock = newAssetRepositoryMock();
communicationMock = newCommunicationRepositoryMock();
jobMock = newJobRepositoryMock();
sut = new TrashService(accessMock, assetMock, jobMock, communicationMock);
});
describe('restoreAssets', () => {
it('should require asset restore access for all ids', async () => {
await expect(
sut.restoreAssets(authStub.user1, {
ids: ['asset-1'],
}),
).rejects.toBeInstanceOf(BadRequestException);
});
it('should restore a batch of assets', async () => {
accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset1', 'asset2']));
await sut.restoreAssets(authStub.user1, { ids: ['asset1', 'asset2'] });
expect(assetMock.restoreAll).toHaveBeenCalledWith(['asset1', 'asset2']);
expect(jobMock.queue.mock.calls).toEqual([]);
});
});
describe('restore', () => {
it('should handle an empty trash', async () => {
assetMock.getByUserId.mockResolvedValue({ items: [], hasNextPage: false });
await expect(sut.restore(authStub.user1)).resolves.toBeUndefined();
expect(assetMock.restoreAll).not.toHaveBeenCalled();
expect(communicationMock.send).not.toHaveBeenCalled();
});
it('should restore and notify', async () => {
assetMock.getByUserId.mockResolvedValue({ items: [assetStub.image], hasNextPage: false });
await expect(sut.restore(authStub.user1)).resolves.toBeUndefined();
expect(assetMock.restoreAll).toHaveBeenCalledWith([assetStub.image.id]);
expect(communicationMock.send).toHaveBeenCalledWith(ClientEvent.ASSET_RESTORE, authStub.user1.user.id, [
assetStub.image.id,
]);
});
});
describe('empty', () => {
it('should handle an empty trash', async () => {
assetMock.getByUserId.mockResolvedValue({ items: [], hasNextPage: false });
await expect(sut.empty(authStub.user1)).resolves.toBeUndefined();
expect(jobMock.queueAll).toHaveBeenCalledWith([]);
});
it('should empty the trash', async () => {
assetMock.getByUserId.mockResolvedValue({ items: [assetStub.image], hasNextPage: false });
await expect(sut.empty(authStub.user1)).resolves.toBeUndefined();
expect(jobMock.queueAll).toHaveBeenCalledWith([
{ name: JobName.ASSET_DELETION, data: { id: assetStub.image.id } },
]);
});
});
});

View file

@ -0,0 +1,65 @@
import { Inject } from '@nestjs/common';
import { DateTime } from 'luxon';
import { AccessCore, Permission } from '../access';
import { BulkIdsDto } from '../asset';
import { AuthDto } from '../auth';
import { usePagination } from '../domain.util';
import { JOBS_ASSET_PAGINATION_SIZE, JobName } from '../job';
import {
ClientEvent,
IAccessRepository,
IAssetRepository,
ICommunicationRepository,
IJobRepository,
} from '../repositories';
export class TrashService {
private access: AccessCore;
constructor(
@Inject(IAccessRepository) accessRepository: IAccessRepository,
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
@Inject(IJobRepository) private jobRepository: IJobRepository,
@Inject(ICommunicationRepository) private communicationRepository: ICommunicationRepository,
) {
this.access = AccessCore.create(accessRepository);
}
async restoreAssets(auth: AuthDto, dto: BulkIdsDto): Promise<void> {
const { ids } = dto;
await this.access.requirePermission(auth, Permission.ASSET_RESTORE, ids);
await this.restoreAndSend(auth, ids);
}
async restore(auth: AuthDto): Promise<void> {
const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) =>
this.assetRepository.getByUserId(pagination, auth.user.id, { trashedBefore: DateTime.now().toJSDate() }),
);
for await (const assets of assetPagination) {
const ids = assets.map((a) => a.id);
await this.restoreAndSend(auth, ids);
}
}
async empty(auth: AuthDto): Promise<void> {
const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) =>
this.assetRepository.getByUserId(pagination, auth.user.id, { trashedBefore: DateTime.now().toJSDate() }),
);
for await (const assets of assetPagination) {
await this.jobRepository.queueAll(
assets.map((asset) => ({ name: JobName.ASSET_DELETION, data: { id: asset.id } })),
);
}
}
private async restoreAndSend(auth: AuthDto, ids: string[]) {
if (ids.length === 0) {
return;
}
await this.assetRepository.restoreAll(ids);
this.communicationRepository.send(ClientEvent.ASSET_RESTORE, auth.user.id, ids);
}
}

View file

@ -31,6 +31,7 @@ import {
SharedLinkController, SharedLinkController,
SystemConfigController, SystemConfigController,
TagController, TagController,
TrashController,
UserController, UserController,
} from './controllers'; } from './controllers';
import { ErrorInterceptor, FileUploadInterceptor } from './interceptors'; import { ErrorInterceptor, FileUploadInterceptor } from './interceptors';
@ -64,6 +65,7 @@ import { ErrorInterceptor, FileUploadInterceptor } from './interceptors';
SharedLinkController, SharedLinkController,
SystemConfigController, SystemConfigController,
TagController, TagController,
TrashController,
UserController, UserController,
PersonController, PersonController,
], ],

View file

@ -22,7 +22,7 @@ import {
TimeBucketAssetDto, TimeBucketAssetDto,
TimeBucketDto, TimeBucketDto,
TimeBucketResponseDto, TimeBucketResponseDto,
TrashAction, TrashService,
UpdateAssetDto as UpdateDto, UpdateAssetDto as UpdateDto,
UpdateStackParentDto, UpdateStackParentDto,
} from '@app/domain'; } from '@app/domain';
@ -69,6 +69,7 @@ export class AssetController {
constructor( constructor(
private service: AssetService, private service: AssetService,
private downloadService: DownloadService, private downloadService: DownloadService,
private trashService: TrashService,
) {} ) {}
@Get('map-marker') @Get('map-marker')
@ -165,22 +166,31 @@ export class AssetController {
return this.service.deleteAll(auth, dto); return this.service.deleteAll(auth, dto);
} }
/**
* @deprecated use `POST /trash/restore/assets`
*/
@Post('restore') @Post('restore')
@HttpCode(HttpStatus.NO_CONTENT) @HttpCode(HttpStatus.NO_CONTENT)
restoreAssets(@Auth() auth: AuthDto, @Body() dto: BulkIdsDto): Promise<void> { restoreAssetsOld(@Auth() auth: AuthDto, @Body() dto: BulkIdsDto): Promise<void> {
return this.service.restoreAll(auth, dto); return this.trashService.restoreAssets(auth, dto);
} }
/**
* @deprecated use `POST /trash/empty`
*/
@Post('trash/empty') @Post('trash/empty')
@HttpCode(HttpStatus.NO_CONTENT) @HttpCode(HttpStatus.NO_CONTENT)
emptyTrash(@Auth() auth: AuthDto): Promise<void> { emptyTrashOld(@Auth() auth: AuthDto): Promise<void> {
return this.service.handleTrashAction(auth, TrashAction.EMPTY_ALL); return this.trashService.empty(auth);
} }
/**
* @deprecated use `POST /trash/restore`
*/
@Post('trash/restore') @Post('trash/restore')
@HttpCode(HttpStatus.NO_CONTENT) @HttpCode(HttpStatus.NO_CONTENT)
restoreTrash(@Auth() auth: AuthDto): Promise<void> { restoreTrashOld(@Auth() auth: AuthDto): Promise<void> {
return this.service.handleTrashAction(auth, TrashAction.RESTORE_ALL); return this.trashService.restore(auth);
} }
@Put('stack/parent') @Put('stack/parent')

View file

@ -17,4 +17,5 @@ export * from './server-info.controller';
export * from './shared-link.controller'; export * from './shared-link.controller';
export * from './system-config.controller'; export * from './system-config.controller';
export * from './tag.controller'; export * from './tag.controller';
export * from './trash.controller';
export * from './user.controller'; export * from './user.controller';

View file

@ -0,0 +1,31 @@
import { AuthDto, BulkIdsDto, TrashService } from '@app/domain';
import { Body, Controller, HttpCode, HttpStatus, Post } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { Auth, Authenticated } from '../app.guard';
import { UseValidation } from '../app.utils';
@ApiTags('Trash')
@Controller('trash')
@Authenticated()
@UseValidation()
export class TrashController {
constructor(private service: TrashService) {}
@Post('empty')
@HttpCode(HttpStatus.NO_CONTENT)
emptyTrash(@Auth() auth: AuthDto): Promise<void> {
return this.service.empty(auth);
}
@Post('restore')
@HttpCode(HttpStatus.NO_CONTENT)
restoreTrash(@Auth() auth: AuthDto): Promise<void> {
return this.service.restore(auth);
}
@Post('restore/assets')
@HttpCode(HttpStatus.NO_CONTENT)
restoreAssets(@Auth() auth: AuthDto, @Body() dto: BulkIdsDto): Promise<void> {
return this.service.restoreAssets(auth, dto);
}
}

View file

@ -19,6 +19,7 @@ import {
ServerInfoApi, ServerInfoApi,
SharedLinkApi, SharedLinkApi,
SystemConfigApi, SystemConfigApi,
TrashApi,
UserApi, UserApi,
UserApiFp, UserApiFp,
base, base,
@ -46,6 +47,7 @@ class ImmichApi {
public personApi: PersonApi; public personApi: PersonApi;
public systemConfigApi: SystemConfigApi; public systemConfigApi: SystemConfigApi;
public userApi: UserApi; public userApi: UserApi;
public trashApi: TrashApi;
private config: configuration.Configuration; private config: configuration.Configuration;
private key?: string; private key?: string;
@ -75,6 +77,7 @@ class ImmichApi {
this.personApi = new PersonApi(this.config); this.personApi = new PersonApi(this.config);
this.systemConfigApi = new SystemConfigApi(this.config); this.systemConfigApi = new SystemConfigApi(this.config);
this.userApi = new UserApi(this.config); this.userApi = new UserApi(this.config);
this.trashApi = new TrashApi(this.config);
} }
private createUrl(path: string, params?: Record<string, unknown>) { private createUrl(path: string, params?: Record<string, unknown>) {

View file

@ -22,7 +22,7 @@
try { try {
const ids = Array.from(getAssets()).map((a) => a.id); const ids = Array.from(getAssets()).map((a) => a.id);
await api.assetApi.restoreAssets({ bulkIdsDto: { ids } }); await api.trashApi.restoreAssets({ bulkIdsDto: { ids } });
onRestore?.(ids); onRestore?.(ids);
notificationController.show({ notificationController.show({

View file

@ -37,7 +37,7 @@
const handleEmptyTrash = async () => { const handleEmptyTrash = async () => {
isShowEmptyConfirmation = false; isShowEmptyConfirmation = false;
try { try {
await api.assetApi.emptyTrash(); await api.trashApi.emptyTrash();
notificationController.show({ notificationController.show({
message: `Empty trash initiated. Refresh the page to see the changes`, message: `Empty trash initiated. Refresh the page to see the changes`,
@ -50,7 +50,7 @@
const handleRestoreTrash = async () => { const handleRestoreTrash = async () => {
try { try {
await api.assetApi.restoreTrash(); await api.trashApi.restoreTrash();
notificationController.show({ notificationController.show({
message: `Restore trash initiated. Refresh the page to see the changes`, message: `Restore trash initiated. Refresh the page to see the changes`,