diff --git a/mobile/openapi/.openapi-generator/FILES b/mobile/openapi/.openapi-generator/FILES index eafeb6851..9335f934b 100644 --- a/mobile/openapi/.openapi-generator/FILES +++ b/mobile/openapi/.openapi-generator/FILES @@ -25,6 +25,8 @@ doc/AssetCountByTimeBucket.md doc/AssetCountByTimeBucketResponseDto.md doc/AssetCountByUserIdResponseDto.md doc/AssetFileUploadResponseDto.md +doc/AssetIdsDto.md +doc/AssetIdsResponseDto.md doc/AssetResponseDto.md doc/AssetTypeEnum.md doc/AuthDeviceResponseDto.md @@ -154,6 +156,8 @@ lib/model/asset_count_by_time_bucket.dart lib/model/asset_count_by_time_bucket_response_dto.dart lib/model/asset_count_by_user_id_response_dto.dart lib/model/asset_file_upload_response_dto.dart +lib/model/asset_ids_dto.dart +lib/model/asset_ids_response_dto.dart lib/model/asset_response_dto.dart lib/model/asset_type_enum.dart lib/model/auth_device_response_dto.dart @@ -252,6 +256,8 @@ test/asset_count_by_time_bucket_response_dto_test.dart test/asset_count_by_time_bucket_test.dart test/asset_count_by_user_id_response_dto_test.dart test/asset_file_upload_response_dto_test.dart +test/asset_ids_dto_test.dart +test/asset_ids_response_dto_test.dart test/asset_response_dto_test.dart test/asset_type_enum_test.dart test/auth_device_response_dto_test.dart diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index c0b851126..99d6f2da2 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -154,11 +154,14 @@ Class | Method | HTTP request | Description *SystemConfigApi* | [**getDefaults**](doc//SystemConfigApi.md#getdefaults) | **GET** /system-config/defaults | *SystemConfigApi* | [**getStorageTemplateOptions**](doc//SystemConfigApi.md#getstoragetemplateoptions) | **GET** /system-config/storage-template-options | *SystemConfigApi* | [**updateConfig**](doc//SystemConfigApi.md#updateconfig) | **PUT** /system-config | -*TagApi* | [**create**](doc//TagApi.md#create) | **POST** /tag | -*TagApi* | [**delete**](doc//TagApi.md#delete) | **DELETE** /tag/{id} | -*TagApi* | [**findAll**](doc//TagApi.md#findall) | **GET** /tag | -*TagApi* | [**findOne**](doc//TagApi.md#findone) | **GET** /tag/{id} | -*TagApi* | [**update**](doc//TagApi.md#update) | **PATCH** /tag/{id} | +*TagApi* | [**createTag**](doc//TagApi.md#createtag) | **POST** /tag | +*TagApi* | [**deleteTag**](doc//TagApi.md#deletetag) | **DELETE** /tag/{id} | +*TagApi* | [**getAllTags**](doc//TagApi.md#getalltags) | **GET** /tag | +*TagApi* | [**getTagAssets**](doc//TagApi.md#gettagassets) | **GET** /tag/{id}/assets | +*TagApi* | [**getTagById**](doc//TagApi.md#gettagbyid) | **GET** /tag/{id} | +*TagApi* | [**tagAssets**](doc//TagApi.md#tagassets) | **PUT** /tag/{id}/assets | +*TagApi* | [**untagAssets**](doc//TagApi.md#untagassets) | **DELETE** /tag/{id}/assets | +*TagApi* | [**updateTag**](doc//TagApi.md#updatetag) | **PATCH** /tag/{id} | *UserApi* | [**createProfileImage**](doc//UserApi.md#createprofileimage) | **POST** /user/profile-image | *UserApi* | [**createUser**](doc//UserApi.md#createuser) | **POST** /user | *UserApi* | [**deleteUser**](doc//UserApi.md#deleteuser) | **DELETE** /user/{userId} | @@ -192,6 +195,8 @@ Class | Method | HTTP request | Description - [AssetCountByTimeBucketResponseDto](doc//AssetCountByTimeBucketResponseDto.md) - [AssetCountByUserIdResponseDto](doc//AssetCountByUserIdResponseDto.md) - [AssetFileUploadResponseDto](doc//AssetFileUploadResponseDto.md) + - [AssetIdsDto](doc//AssetIdsDto.md) + - [AssetIdsResponseDto](doc//AssetIdsResponseDto.md) - [AssetResponseDto](doc//AssetResponseDto.md) - [AssetTypeEnum](doc//AssetTypeEnum.md) - [AuthDeviceResponseDto](doc//AuthDeviceResponseDto.md) diff --git a/mobile/openapi/doc/AssetIdsDto.md b/mobile/openapi/doc/AssetIdsDto.md new file mode 100644 index 000000000..002c3ee5e --- /dev/null +++ b/mobile/openapi/doc/AssetIdsDto.md @@ -0,0 +1,15 @@ +# openapi.model.AssetIdsDto + +## Load the model package +```dart +import 'package:openapi/api.dart'; +``` + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**assetIds** | **List** | | [default to const []] + +[[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/doc/AssetIdsResponseDto.md b/mobile/openapi/doc/AssetIdsResponseDto.md new file mode 100644 index 000000000..4aaebf73a --- /dev/null +++ b/mobile/openapi/doc/AssetIdsResponseDto.md @@ -0,0 +1,17 @@ +# openapi.model.AssetIdsResponseDto + +## Load the model package +```dart +import 'package:openapi/api.dart'; +``` + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**assetId** | **String** | | +**success** | **bool** | | +**error** | **String** | | [optional] + +[[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/doc/TagApi.md b/mobile/openapi/doc/TagApi.md index 3dd11aab6..7c5b3d9a1 100644 --- a/mobile/openapi/doc/TagApi.md +++ b/mobile/openapi/doc/TagApi.md @@ -9,15 +9,18 @@ All URIs are relative to */api* Method | HTTP request | Description ------------- | ------------- | ------------- -[**create**](TagApi.md#create) | **POST** /tag | -[**delete**](TagApi.md#delete) | **DELETE** /tag/{id} | -[**findAll**](TagApi.md#findall) | **GET** /tag | -[**findOne**](TagApi.md#findone) | **GET** /tag/{id} | -[**update**](TagApi.md#update) | **PATCH** /tag/{id} | +[**createTag**](TagApi.md#createtag) | **POST** /tag | +[**deleteTag**](TagApi.md#deletetag) | **DELETE** /tag/{id} | +[**getAllTags**](TagApi.md#getalltags) | **GET** /tag | +[**getTagAssets**](TagApi.md#gettagassets) | **GET** /tag/{id}/assets | +[**getTagById**](TagApi.md#gettagbyid) | **GET** /tag/{id} | +[**tagAssets**](TagApi.md#tagassets) | **PUT** /tag/{id}/assets | +[**untagAssets**](TagApi.md#untagassets) | **DELETE** /tag/{id}/assets | +[**updateTag**](TagApi.md#updatetag) | **PATCH** /tag/{id} | -# **create** -> TagResponseDto create(createTagDto) +# **createTag** +> TagResponseDto createTag(createTagDto) @@ -43,10 +46,10 @@ final api_instance = TagApi(); final createTagDto = CreateTagDto(); // CreateTagDto | try { - final result = api_instance.create(createTagDto); + final result = api_instance.createTag(createTagDto); print(result); } catch (e) { - print('Exception when calling TagApi->create: $e\n'); + print('Exception when calling TagApi->createTag: $e\n'); } ``` @@ -71,8 +74,8 @@ 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) -# **delete** -> delete(id) +# **deleteTag** +> deleteTag(id) @@ -98,9 +101,9 @@ final api_instance = TagApi(); final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | try { - api_instance.delete(id); + api_instance.deleteTag(id); } catch (e) { - print('Exception when calling TagApi->delete: $e\n'); + print('Exception when calling TagApi->deleteTag: $e\n'); } ``` @@ -125,8 +128,8 @@ void (empty response body) [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) -# **findAll** -> List findAll() +# **getAllTags** +> List getAllTags() @@ -151,10 +154,10 @@ import 'package:openapi/api.dart'; final api_instance = TagApi(); try { - final result = api_instance.findAll(); + final result = api_instance.getAllTags(); print(result); } catch (e) { - print('Exception when calling TagApi->findAll: $e\n'); + print('Exception when calling TagApi->getAllTags: $e\n'); } ``` @@ -176,8 +179,8 @@ This endpoint does not need any parameter. [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) -# **findOne** -> TagResponseDto findOne(id) +# **getTagAssets** +> List getTagAssets(id) @@ -203,10 +206,65 @@ final api_instance = TagApi(); final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | try { - final result = api_instance.findOne(id); + final result = api_instance.getTagAssets(id); print(result); } catch (e) { - print('Exception when calling TagApi->findOne: $e\n'); + print('Exception when calling TagApi->getTagAssets: $e\n'); +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **id** | **String**| | + +### 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) + +# **getTagById** +> TagResponseDto getTagById(id) + + + +### 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 = TagApi(); +final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | + +try { + final result = api_instance.getTagById(id); + print(result); +} catch (e) { + print('Exception when calling TagApi->getTagById: $e\n'); } ``` @@ -231,8 +289,122 @@ 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) -# **update** -> TagResponseDto update(id, updateTagDto) +# **tagAssets** +> List tagAssets(id, assetIdsDto) + + + +### 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 = TagApi(); +final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | +final assetIdsDto = AssetIdsDto(); // AssetIdsDto | + +try { + final result = api_instance.tagAssets(id, assetIdsDto); + print(result); +} catch (e) { + print('Exception when calling TagApi->tagAssets: $e\n'); +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **id** | **String**| | + **assetIdsDto** | [**AssetIdsDto**](AssetIdsDto.md)| | + +### Return type + +[**List**](AssetIdsResponseDto.md) + +### Authorization + +[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer) + +### HTTP request headers + + - **Content-Type**: application/json + - **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) + +# **untagAssets** +> List untagAssets(id, assetIdsDto) + + + +### 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 = TagApi(); +final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | +final assetIdsDto = AssetIdsDto(); // AssetIdsDto | + +try { + final result = api_instance.untagAssets(id, assetIdsDto); + print(result); +} catch (e) { + print('Exception when calling TagApi->untagAssets: $e\n'); +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **id** | **String**| | + **assetIdsDto** | [**AssetIdsDto**](AssetIdsDto.md)| | + +### Return type + +[**List**](AssetIdsResponseDto.md) + +### Authorization + +[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer) + +### HTTP request headers + + - **Content-Type**: application/json + - **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) + +# **updateTag** +> TagResponseDto updateTag(id, updateTagDto) @@ -259,10 +431,10 @@ final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | final updateTagDto = UpdateTagDto(); // UpdateTagDto | try { - final result = api_instance.update(id, updateTagDto); + final result = api_instance.updateTag(id, updateTagDto); print(result); } catch (e) { - print('Exception when calling TagApi->update: $e\n'); + print('Exception when calling TagApi->updateTag: $e\n'); } ``` diff --git a/mobile/openapi/doc/TagResponseDto.md b/mobile/openapi/doc/TagResponseDto.md index 31805c2a8..366e95033 100644 --- a/mobile/openapi/doc/TagResponseDto.md +++ b/mobile/openapi/doc/TagResponseDto.md @@ -8,11 +8,10 @@ import 'package:openapi/api.dart'; ## Properties Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**id** | **String** | | **type** | [**TagTypeEnum**](TagTypeEnum.md) | | +**id** | **String** | | **name** | **String** | | **userId** | **String** | | -**renameTagId** | **String** | | [optional] [[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/doc/UpdateTagDto.md b/mobile/openapi/doc/UpdateTagDto.md index ad4e551be..42aae30b5 100644 --- a/mobile/openapi/doc/UpdateTagDto.md +++ b/mobile/openapi/doc/UpdateTagDto.md @@ -9,7 +9,6 @@ import 'package:openapi/api.dart'; Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **name** | **String** | | [optional] -**renameTagId** | **String** | | [optional] [[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 833628d62..20f225513 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -62,6 +62,8 @@ part 'model/asset_count_by_time_bucket.dart'; part 'model/asset_count_by_time_bucket_response_dto.dart'; part 'model/asset_count_by_user_id_response_dto.dart'; part 'model/asset_file_upload_response_dto.dart'; +part 'model/asset_ids_dto.dart'; +part 'model/asset_ids_response_dto.dart'; part 'model/asset_response_dto.dart'; part 'model/asset_type_enum.dart'; part 'model/auth_device_response_dto.dart'; diff --git a/mobile/openapi/lib/api/tag_api.dart b/mobile/openapi/lib/api/tag_api.dart index 3591cf11c..e420792ca 100644 --- a/mobile/openapi/lib/api/tag_api.dart +++ b/mobile/openapi/lib/api/tag_api.dart @@ -20,7 +20,7 @@ class TagApi { /// Parameters: /// /// * [CreateTagDto] createTagDto (required): - Future createWithHttpInfo(CreateTagDto createTagDto,) async { + Future createTagWithHttpInfo(CreateTagDto createTagDto,) async { // ignore: prefer_const_declarations final path = r'/tag'; @@ -48,8 +48,8 @@ class TagApi { /// Parameters: /// /// * [CreateTagDto] createTagDto (required): - Future create(CreateTagDto createTagDto,) async { - final response = await createWithHttpInfo(createTagDto,); + Future createTag(CreateTagDto createTagDto,) async { + final response = await createTagWithHttpInfo(createTagDto,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -67,7 +67,7 @@ class TagApi { /// Parameters: /// /// * [String] id (required): - Future deleteWithHttpInfo(String id,) async { + Future deleteTagWithHttpInfo(String id,) async { // ignore: prefer_const_declarations final path = r'/tag/{id}' .replaceAll('{id}', id); @@ -96,15 +96,15 @@ class TagApi { /// Parameters: /// /// * [String] id (required): - Future delete(String id,) async { - final response = await deleteWithHttpInfo(id,); + Future deleteTag(String id,) async { + final response = await deleteTagWithHttpInfo(id,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } } /// Performs an HTTP 'GET /tag' operation and returns the [Response]. - Future findAllWithHttpInfo() async { + Future getAllTagsWithHttpInfo() async { // ignore: prefer_const_declarations final path = r'/tag'; @@ -129,8 +129,8 @@ class TagApi { ); } - Future?> findAll() async { - final response = await findAllWithHttpInfo(); + Future?> getAllTags() async { + final response = await getAllTagsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -147,11 +147,62 @@ class TagApi { return null; } + /// Performs an HTTP 'GET /tag/{id}/assets' operation and returns the [Response]. + /// Parameters: + /// + /// * [String] id (required): + Future getTagAssetsWithHttpInfo(String id,) async { + // ignore: prefer_const_declarations + final path = r'/tag/{id}/assets' + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + path, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [String] id (required): + Future?> getTagAssets(String id,) async { + final response = await getTagAssetsWithHttpInfo(id,); + 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 /tag/{id}' operation and returns the [Response]. /// Parameters: /// /// * [String] id (required): - Future findOneWithHttpInfo(String id,) async { + Future getTagByIdWithHttpInfo(String id,) async { // ignore: prefer_const_declarations final path = r'/tag/{id}' .replaceAll('{id}', id); @@ -180,8 +231,8 @@ class TagApi { /// Parameters: /// /// * [String] id (required): - Future findOne(String id,) async { - final response = await findOneWithHttpInfo(id,); + Future getTagById(String id,) async { + final response = await getTagByIdWithHttpInfo(id,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -195,13 +246,123 @@ class TagApi { return null; } + /// Performs an HTTP 'PUT /tag/{id}/assets' operation and returns the [Response]. + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [AssetIdsDto] assetIdsDto (required): + Future tagAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto,) async { + // ignore: prefer_const_declarations + final path = r'/tag/{id}/assets' + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody = assetIdsDto; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = ['application/json']; + + + return apiClient.invokeAPI( + path, + 'PUT', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [AssetIdsDto] assetIdsDto (required): + Future?> tagAssets(String id, AssetIdsDto assetIdsDto,) async { + final response = await tagAssetsWithHttpInfo(id, assetIdsDto,); + 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 'DELETE /tag/{id}/assets' operation and returns the [Response]. + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [AssetIdsDto] assetIdsDto (required): + Future untagAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto,) async { + // ignore: prefer_const_declarations + final path = r'/tag/{id}/assets' + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody = assetIdsDto; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = ['application/json']; + + + return apiClient.invokeAPI( + path, + 'DELETE', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [AssetIdsDto] assetIdsDto (required): + Future?> untagAssets(String id, AssetIdsDto assetIdsDto,) async { + final response = await untagAssetsWithHttpInfo(id, assetIdsDto,); + 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 'PATCH /tag/{id}' operation and returns the [Response]. /// Parameters: /// /// * [String] id (required): /// /// * [UpdateTagDto] updateTagDto (required): - Future updateWithHttpInfo(String id, UpdateTagDto updateTagDto,) async { + Future updateTagWithHttpInfo(String id, UpdateTagDto updateTagDto,) async { // ignore: prefer_const_declarations final path = r'/tag/{id}' .replaceAll('{id}', id); @@ -232,8 +393,8 @@ class TagApi { /// * [String] id (required): /// /// * [UpdateTagDto] updateTagDto (required): - Future update(String id, UpdateTagDto updateTagDto,) async { - final response = await updateWithHttpInfo(id, updateTagDto,); + Future updateTag(String id, UpdateTagDto updateTagDto,) async { + final response = await updateTagWithHttpInfo(id, updateTagDto,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index 7b46ea15f..d3f079074 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -219,6 +219,10 @@ class ApiClient { return AssetCountByUserIdResponseDto.fromJson(value); case 'AssetFileUploadResponseDto': return AssetFileUploadResponseDto.fromJson(value); + case 'AssetIdsDto': + return AssetIdsDto.fromJson(value); + case 'AssetIdsResponseDto': + return AssetIdsResponseDto.fromJson(value); case 'AssetResponseDto': return AssetResponseDto.fromJson(value); case 'AssetTypeEnum': diff --git a/mobile/openapi/lib/model/asset_ids_dto.dart b/mobile/openapi/lib/model/asset_ids_dto.dart new file mode 100644 index 000000000..d76462847 --- /dev/null +++ b/mobile/openapi/lib/model/asset_ids_dto.dart @@ -0,0 +1,111 @@ +// +// 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 AssetIdsDto { + /// Returns a new [AssetIdsDto] instance. + AssetIdsDto({ + this.assetIds = const [], + }); + + List assetIds; + + @override + bool operator ==(Object other) => identical(this, other) || other is AssetIdsDto && + other.assetIds == assetIds; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (assetIds.hashCode); + + @override + String toString() => 'AssetIdsDto[assetIds=$assetIds]'; + + Map toJson() { + final json = {}; + json[r'assetIds'] = this.assetIds; + return json; + } + + /// Returns a new [AssetIdsDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static AssetIdsDto? fromJson(dynamic value) { + if (value is Map) { + final json = value.cast(); + + // Ensure that the map contains the required keys. + // Note 1: the values aren't checked for validity beyond being non-null. + // Note 2: this code is stripped in release mode! + assert(() { + requiredKeys.forEach((key) { + assert(json.containsKey(key), 'Required key "AssetIdsDto[$key]" is missing from JSON.'); + assert(json[key] != null, 'Required key "AssetIdsDto[$key]" has a null value in JSON.'); + }); + return true; + }()); + + return AssetIdsDto( + assetIds: json[r'assetIds'] is Iterable + ? (json[r'assetIds'] as Iterable).cast().toList(growable: false) + : const [], + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = AssetIdsDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = AssetIdsDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of AssetIdsDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = AssetIdsDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'assetIds', + }; +} + diff --git a/mobile/openapi/lib/model/asset_ids_response_dto.dart b/mobile/openapi/lib/model/asset_ids_response_dto.dart new file mode 100644 index 000000000..6f9befea7 --- /dev/null +++ b/mobile/openapi/lib/model/asset_ids_response_dto.dart @@ -0,0 +1,205 @@ +// +// 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 AssetIdsResponseDto { + /// Returns a new [AssetIdsResponseDto] instance. + AssetIdsResponseDto({ + required this.assetId, + required this.success, + this.error, + }); + + String assetId; + + bool success; + + AssetIdsResponseDtoErrorEnum? error; + + @override + bool operator ==(Object other) => identical(this, other) || other is AssetIdsResponseDto && + other.assetId == assetId && + other.success == success && + other.error == error; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (assetId.hashCode) + + (success.hashCode) + + (error == null ? 0 : error!.hashCode); + + @override + String toString() => 'AssetIdsResponseDto[assetId=$assetId, success=$success, error=$error]'; + + Map toJson() { + final json = {}; + json[r'assetId'] = this.assetId; + json[r'success'] = this.success; + if (this.error != null) { + json[r'error'] = this.error; + } else { + // json[r'error'] = null; + } + return json; + } + + /// Returns a new [AssetIdsResponseDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static AssetIdsResponseDto? fromJson(dynamic value) { + if (value is Map) { + final json = value.cast(); + + // Ensure that the map contains the required keys. + // Note 1: the values aren't checked for validity beyond being non-null. + // Note 2: this code is stripped in release mode! + assert(() { + requiredKeys.forEach((key) { + assert(json.containsKey(key), 'Required key "AssetIdsResponseDto[$key]" is missing from JSON.'); + assert(json[key] != null, 'Required key "AssetIdsResponseDto[$key]" has a null value in JSON.'); + }); + return true; + }()); + + return AssetIdsResponseDto( + assetId: mapValueOfType(json, r'assetId')!, + success: mapValueOfType(json, r'success')!, + error: AssetIdsResponseDtoErrorEnum.fromJson(json[r'error']), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = AssetIdsResponseDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = AssetIdsResponseDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of AssetIdsResponseDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = AssetIdsResponseDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'assetId', + 'success', + }; +} + + +class AssetIdsResponseDtoErrorEnum { + /// Instantiate a new enum with the provided [value]. + const AssetIdsResponseDtoErrorEnum._(this.value); + + /// The underlying value of this enum member. + final String value; + + @override + String toString() => value; + + String toJson() => value; + + static const duplicate = AssetIdsResponseDtoErrorEnum._(r'duplicate'); + static const noPermission = AssetIdsResponseDtoErrorEnum._(r'no_permission'); + static const notFound = AssetIdsResponseDtoErrorEnum._(r'not_found'); + + /// List of all possible values in this [enum][AssetIdsResponseDtoErrorEnum]. + static const values = [ + duplicate, + noPermission, + notFound, + ]; + + static AssetIdsResponseDtoErrorEnum? fromJson(dynamic value) => AssetIdsResponseDtoErrorEnumTypeTransformer().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 = AssetIdsResponseDtoErrorEnum.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } +} + +/// Transformation class that can [encode] an instance of [AssetIdsResponseDtoErrorEnum] to String, +/// and [decode] dynamic data back to [AssetIdsResponseDtoErrorEnum]. +class AssetIdsResponseDtoErrorEnumTypeTransformer { + factory AssetIdsResponseDtoErrorEnumTypeTransformer() => _instance ??= const AssetIdsResponseDtoErrorEnumTypeTransformer._(); + + const AssetIdsResponseDtoErrorEnumTypeTransformer._(); + + String encode(AssetIdsResponseDtoErrorEnum data) => data.value; + + /// Decodes a [dynamic value][data] to a AssetIdsResponseDtoErrorEnum. + /// + /// 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. + AssetIdsResponseDtoErrorEnum? decode(dynamic data, {bool allowNull = true}) { + if (data != null) { + switch (data) { + case r'duplicate': return AssetIdsResponseDtoErrorEnum.duplicate; + case r'no_permission': return AssetIdsResponseDtoErrorEnum.noPermission; + case r'not_found': return AssetIdsResponseDtoErrorEnum.notFound; + default: + if (!allowNull) { + throw ArgumentError('Unknown enum value to decode: $data'); + } + } + } + return null; + } + + /// Singleton [AssetIdsResponseDtoErrorEnumTypeTransformer] instance. + static AssetIdsResponseDtoErrorEnumTypeTransformer? _instance; +} + + diff --git a/mobile/openapi/lib/model/tag_response_dto.dart b/mobile/openapi/lib/model/tag_response_dto.dart index 42c386df3..8c4dd45ed 100644 --- a/mobile/openapi/lib/model/tag_response_dto.dart +++ b/mobile/openapi/lib/model/tag_response_dto.dart @@ -13,54 +13,44 @@ part of openapi.api; class TagResponseDto { /// Returns a new [TagResponseDto] instance. TagResponseDto({ - required this.id, required this.type, + required this.id, required this.name, required this.userId, - this.renameTagId, }); - String id; - TagTypeEnum type; + String id; + String name; String userId; - String? renameTagId; - @override bool operator ==(Object other) => identical(this, other) || other is TagResponseDto && - other.id == id && other.type == type && + other.id == id && other.name == name && - other.userId == userId && - other.renameTagId == renameTagId; + other.userId == userId; @override int get hashCode => // ignore: unnecessary_parenthesis - (id.hashCode) + (type.hashCode) + + (id.hashCode) + (name.hashCode) + - (userId.hashCode) + - (renameTagId == null ? 0 : renameTagId!.hashCode); + (userId.hashCode); @override - String toString() => 'TagResponseDto[id=$id, type=$type, name=$name, userId=$userId, renameTagId=$renameTagId]'; + String toString() => 'TagResponseDto[type=$type, id=$id, name=$name, userId=$userId]'; Map toJson() { final json = {}; - json[r'id'] = this.id; json[r'type'] = this.type; + json[r'id'] = this.id; json[r'name'] = this.name; json[r'userId'] = this.userId; - if (this.renameTagId != null) { - json[r'renameTagId'] = this.renameTagId; - } else { - // json[r'renameTagId'] = null; - } return json; } @@ -83,11 +73,10 @@ class TagResponseDto { }()); return TagResponseDto( - id: mapValueOfType(json, r'id')!, type: TagTypeEnum.fromJson(json[r'type'])!, + id: mapValueOfType(json, r'id')!, name: mapValueOfType(json, r'name')!, userId: mapValueOfType(json, r'userId')!, - renameTagId: mapValueOfType(json, r'renameTagId'), ); } return null; @@ -135,8 +124,8 @@ class TagResponseDto { /// The list of required keys that must be present in a JSON. static const requiredKeys = { - 'id', 'type', + 'id', 'name', 'userId', }; diff --git a/mobile/openapi/lib/model/update_tag_dto.dart b/mobile/openapi/lib/model/update_tag_dto.dart index 85c1761a6..325131480 100644 --- a/mobile/openapi/lib/model/update_tag_dto.dart +++ b/mobile/openapi/lib/model/update_tag_dto.dart @@ -14,7 +14,6 @@ class UpdateTagDto { /// Returns a new [UpdateTagDto] instance. UpdateTagDto({ this.name, - this.renameTagId, }); /// @@ -25,27 +24,17 @@ class UpdateTagDto { /// String? name; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// - String? renameTagId; - @override bool operator ==(Object other) => identical(this, other) || other is UpdateTagDto && - other.name == name && - other.renameTagId == renameTagId; + other.name == name; @override int get hashCode => // ignore: unnecessary_parenthesis - (name == null ? 0 : name!.hashCode) + - (renameTagId == null ? 0 : renameTagId!.hashCode); + (name == null ? 0 : name!.hashCode); @override - String toString() => 'UpdateTagDto[name=$name, renameTagId=$renameTagId]'; + String toString() => 'UpdateTagDto[name=$name]'; Map toJson() { final json = {}; @@ -54,11 +43,6 @@ class UpdateTagDto { } else { // json[r'name'] = null; } - if (this.renameTagId != null) { - json[r'renameTagId'] = this.renameTagId; - } else { - // json[r'renameTagId'] = null; - } return json; } @@ -82,7 +66,6 @@ class UpdateTagDto { return UpdateTagDto( name: mapValueOfType(json, r'name'), - renameTagId: mapValueOfType(json, r'renameTagId'), ); } return null; diff --git a/mobile/openapi/test/asset_ids_dto_test.dart b/mobile/openapi/test/asset_ids_dto_test.dart new file mode 100644 index 000000000..840f6f5cc --- /dev/null +++ b/mobile/openapi/test/asset_ids_dto_test.dart @@ -0,0 +1,27 @@ +// +// 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 AssetIdsDto +void main() { + // final instance = AssetIdsDto(); + + group('test AssetIdsDto', () { + // List assetIds (default value: const []) + test('to test the property `assetIds`', () async { + // TODO + }); + + + }); + +} diff --git a/mobile/openapi/test/asset_ids_response_dto_test.dart b/mobile/openapi/test/asset_ids_response_dto_test.dart new file mode 100644 index 000000000..df6a8642d --- /dev/null +++ b/mobile/openapi/test/asset_ids_response_dto_test.dart @@ -0,0 +1,37 @@ +// +// 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 AssetIdsResponseDto +void main() { + // final instance = AssetIdsResponseDto(); + + group('test AssetIdsResponseDto', () { + // String assetId + test('to test the property `assetId`', () async { + // TODO + }); + + // bool success + test('to test the property `success`', () async { + // TODO + }); + + // String error + test('to test the property `error`', () async { + // TODO + }); + + + }); + +} diff --git a/mobile/openapi/test/tag_api_test.dart b/mobile/openapi/test/tag_api_test.dart index a504aedfa..1b4d79744 100644 --- a/mobile/openapi/test/tag_api_test.dart +++ b/mobile/openapi/test/tag_api_test.dart @@ -17,28 +17,43 @@ void main() { // final instance = TagApi(); group('tests for TagApi', () { - //Future create(CreateTagDto createTagDto) async - test('test create', () async { + //Future createTag(CreateTagDto createTagDto) async + test('test createTag', () async { // TODO }); - //Future delete(String id) async - test('test delete', () async { + //Future deleteTag(String id) async + test('test deleteTag', () async { // TODO }); - //Future> findAll() async - test('test findAll', () async { + //Future> getAllTags() async + test('test getAllTags', () async { // TODO }); - //Future findOne(String id) async - test('test findOne', () async { + //Future> getTagAssets(String id) async + test('test getTagAssets', () async { // TODO }); - //Future update(String id, UpdateTagDto updateTagDto) async - test('test update', () async { + //Future getTagById(String id) async + test('test getTagById', () async { + // TODO + }); + + //Future> tagAssets(String id, AssetIdsDto assetIdsDto) async + test('test tagAssets', () async { + // TODO + }); + + //Future> untagAssets(String id, AssetIdsDto assetIdsDto) async + test('test untagAssets', () async { + // TODO + }); + + //Future updateTag(String id, UpdateTagDto updateTagDto) async + test('test updateTag', () async { // TODO }); diff --git a/mobile/openapi/test/tag_response_dto_test.dart b/mobile/openapi/test/tag_response_dto_test.dart index 41b581efb..705a21ecd 100644 --- a/mobile/openapi/test/tag_response_dto_test.dart +++ b/mobile/openapi/test/tag_response_dto_test.dart @@ -16,13 +16,13 @@ void main() { // final instance = TagResponseDto(); group('test TagResponseDto', () { - // String id - test('to test the property `id`', () async { + // TagTypeEnum type + test('to test the property `type`', () async { // TODO }); - // TagTypeEnum type - test('to test the property `type`', () async { + // String id + test('to test the property `id`', () async { // TODO }); @@ -36,11 +36,6 @@ void main() { // TODO }); - // String renameTagId - test('to test the property `renameTagId`', () async { - // TODO - }); - }); diff --git a/mobile/openapi/test/update_tag_dto_test.dart b/mobile/openapi/test/update_tag_dto_test.dart index 4f4e9498b..7c67e55d7 100644 --- a/mobile/openapi/test/update_tag_dto_test.dart +++ b/mobile/openapi/test/update_tag_dto_test.dart @@ -21,11 +21,6 @@ void main() { // TODO }); - // String renameTagId - test('to test the property `renameTagId`', () async { - // TODO - }); - }); diff --git a/server/apps/immich/src/api-v1/asset/asset-repository.ts b/server/apps/immich/src/api-v1/asset/asset-repository.ts index 982e5c9f5..de7a9df9e 100644 --- a/server/apps/immich/src/api-v1/asset/asset-repository.ts +++ b/server/apps/immich/src/api-v1/asset/asset-repository.ts @@ -1,7 +1,7 @@ import { SearchPropertiesDto } from './dto/search-properties.dto'; import { CuratedLocationsResponseDto } from './response-dto/curated-locations-response.dto'; import { AssetEntity, AssetType, ExifEntity } from '@app/infra/entities'; -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm/repository/Repository'; import { CuratedObjectsResponseDto } from './response-dto/curated-objects-response.dto'; @@ -12,7 +12,6 @@ import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-use import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto'; import { In } from 'typeorm/find-options/operator/In'; import { UpdateAssetDto } from './dto/update-asset.dto'; -import { ITagRepository } from '../tag/tag.repository'; import { IsNull, Not } from 'typeorm'; import { AssetSearchDto } from './dto/asset-search.dto'; @@ -52,10 +51,7 @@ export const IAssetRepository = 'IAssetRepository'; @Injectable() export class AssetRepository implements IAssetRepository { constructor( - @InjectRepository(AssetEntity) - private assetRepository: Repository, - - @Inject(ITagRepository) private _tagRepository: ITagRepository, + @InjectRepository(AssetEntity) private assetRepository: Repository, @InjectRepository(ExifEntity) private exifRepository: Repository, ) {} diff --git a/server/apps/immich/src/api-v1/asset/asset.module.ts b/server/apps/immich/src/api-v1/asset/asset.module.ts index ebadeca75..6ec9c603a 100644 --- a/server/apps/immich/src/api-v1/asset/asset.module.ts +++ b/server/apps/immich/src/api-v1/asset/asset.module.ts @@ -5,7 +5,6 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { AssetEntity, ExifEntity } from '@app/infra/entities'; import { AssetRepository, IAssetRepository } from './asset-repository'; import { DownloadModule } from '../../modules/download/download.module'; -import { TagModule } from '../tag/tag.module'; import { AlbumModule } from '../album/album.module'; const ASSET_REPOSITORY_PROVIDER = { @@ -18,7 +17,6 @@ const ASSET_REPOSITORY_PROVIDER = { // TypeOrmModule.forFeature([AssetEntity, ExifEntity]), DownloadModule, - TagModule, AlbumModule, ], controllers: [AssetController], diff --git a/server/apps/immich/src/api-v1/tag/dto/update-tag.dto.ts b/server/apps/immich/src/api-v1/tag/dto/update-tag.dto.ts deleted file mode 100644 index 64632f1f6..000000000 --- a/server/apps/immich/src/api-v1/tag/dto/update-tag.dto.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { IsOptional, IsString } from 'class-validator'; - -export class UpdateTagDto { - @IsString() - @IsOptional() - name?: string; - - @IsString() - @IsOptional() - renameTagId?: string; -} diff --git a/server/apps/immich/src/api-v1/tag/tag.controller.ts b/server/apps/immich/src/api-v1/tag/tag.controller.ts deleted file mode 100644 index df12513df..000000000 --- a/server/apps/immich/src/api-v1/tag/tag.controller.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Controller, Get, Post, Body, Patch, Param, Delete, ValidationPipe } from '@nestjs/common'; -import { TagService } from './tag.service'; -import { CreateTagDto } from './dto/create-tag.dto'; -import { UpdateTagDto } from './dto/update-tag.dto'; -import { Authenticated } from '../../decorators/authenticated.decorator'; -import { ApiTags } from '@nestjs/swagger'; -import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator'; -import { mapTag, TagResponseDto } from '@app/domain'; -import { UUIDParamDto } from '../../controllers/dto/uuid-param.dto'; - -@ApiTags('Tag') -@Controller('tag') -@Authenticated() -export class TagController { - constructor(private readonly tagService: TagService) {} - - @Post() - create( - @GetAuthUser() authUser: AuthUserDto, - @Body(ValidationPipe) createTagDto: CreateTagDto, - ): Promise { - return this.tagService.create(authUser, createTagDto); - } - - @Get() - findAll(@GetAuthUser() authUser: AuthUserDto): Promise { - return this.tagService.findAll(authUser); - } - - @Get(':id') - async findOne(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise { - const tag = await this.tagService.findOne(authUser, id); - return mapTag(tag); - } - - @Patch(':id') - update( - @GetAuthUser() authUser: AuthUserDto, - @Param() { id }: UUIDParamDto, - @Body(ValidationPipe) updateTagDto: UpdateTagDto, - ): Promise { - return this.tagService.update(authUser, id, updateTagDto); - } - - @Delete(':id') - delete(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise { - return this.tagService.remove(authUser, id); - } -} diff --git a/server/apps/immich/src/api-v1/tag/tag.module.ts b/server/apps/immich/src/api-v1/tag/tag.module.ts deleted file mode 100644 index 38b7924a6..000000000 --- a/server/apps/immich/src/api-v1/tag/tag.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TagService } from './tag.service'; -import { TagController } from './tag.controller'; -import { TagEntity } from '@app/infra/entities'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { TagRepository, ITagRepository } from './tag.repository'; - -const TAG_REPOSITORY_PROVIDER = { - provide: ITagRepository, - useClass: TagRepository, -}; -@Module({ - imports: [TypeOrmModule.forFeature([TagEntity])], - controllers: [TagController], - providers: [TagService, TAG_REPOSITORY_PROVIDER], - exports: [TAG_REPOSITORY_PROVIDER], -}) -export class TagModule {} diff --git a/server/apps/immich/src/api-v1/tag/tag.repository.ts b/server/apps/immich/src/api-v1/tag/tag.repository.ts deleted file mode 100644 index ec32d8b74..000000000 --- a/server/apps/immich/src/api-v1/tag/tag.repository.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { TagEntity, TagType } from '@app/infra/entities'; -import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { In, Repository } from 'typeorm'; -import { UpdateTagDto } from './dto/update-tag.dto'; - -export interface ITagRepository { - create(userId: string, tagType: TagType, tagName: string): Promise; - getByIds(userId: string, tagIds: string[]): Promise; - getById(tagId: string, userId: string): Promise; - getByUserId(userId: string): Promise; - update(tag: TagEntity, updateTagDto: UpdateTagDto): Promise; - remove(tag: TagEntity): Promise; -} - -export const ITagRepository = 'ITagRepository'; - -@Injectable() -export class TagRepository implements ITagRepository { - constructor( - @InjectRepository(TagEntity) - private tagRepository: Repository, - ) {} - - async create(userId: string, tagType: TagType, tagName: string): Promise { - const tag = new TagEntity(); - tag.name = tagName; - tag.type = tagType; - tag.userId = userId; - - return this.tagRepository.save(tag); - } - - async getById(tagId: string, userId: string): Promise { - return await this.tagRepository.findOne({ where: { id: tagId, userId }, relations: ['user'] }); - } - - async getByIds(userId: string, tagIds: string[]): Promise { - return await this.tagRepository.find({ - where: { id: In(tagIds), userId }, - relations: { - user: true, - }, - }); - } - - async getByUserId(userId: string): Promise { - return await this.tagRepository.find({ where: { userId } }); - } - - async update(tag: TagEntity, updateTagDto: UpdateTagDto): Promise { - tag.name = updateTagDto.name ?? tag.name; - tag.renameTagId = updateTagDto.renameTagId ?? tag.renameTagId; - - return this.tagRepository.save(tag); - } - - async remove(tag: TagEntity): Promise { - return await this.tagRepository.remove(tag); - } -} diff --git a/server/apps/immich/src/api-v1/tag/tag.service.spec.ts b/server/apps/immich/src/api-v1/tag/tag.service.spec.ts deleted file mode 100644 index 4609bb781..000000000 --- a/server/apps/immich/src/api-v1/tag/tag.service.spec.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { TagEntity, TagType, UserEntity } from '@app/infra/entities'; -import { AuthUserDto } from '../../decorators/auth-user.decorator'; -import { ITagRepository } from './tag.repository'; -import { TagService } from './tag.service'; - -describe('TagService', () => { - let sut: TagService; - let tagRepositoryMock: jest.Mocked; - - const user1AuthUser: AuthUserDto = Object.freeze({ - id: '1111', - email: 'testuser@email.com', - isAdmin: false, - }); - - const user1: UserEntity = Object.freeze({ - id: '1111', - firstName: 'Alex', - lastName: 'Tran', - isAdmin: true, - email: 'testuser@email.com', - profileImagePath: '', - shouldChangePassword: true, - createdAt: new Date('2022-12-02T19:29:23.603Z'), - deletedAt: null, - updatedAt: new Date('2022-12-02T19:29:23.603Z'), - tags: [], - assets: [], - oauthId: 'oauth-id-1', - storageLabel: null, - }); - - // const user2: UserEntity = Object.freeze({ - // id: '2222', - // firstName: 'Alex', - // lastName: 'Tran', - // isAdmin: true, - // email: 'testuser2@email.com', - // profileImagePath: '', - // shouldChangePassword: true, - // createdAt: '2022-12-02T19:29:23.603Z', - // deletedAt: undefined, - // tags: [], - // oauthId: 'oauth-id-2', - // }); - - const user1Tag1: TagEntity = Object.freeze({ - name: 'user 1 tag 1', - type: TagType.CUSTOM, - userId: user1.id, - user: user1, - renameTagId: '', - id: 'user1-tag-1-id', - assets: [], - }); - - // const user1Tag2: TagEntity = Object.freeze({ - // name: 'user 1 tag 2', - // type: TagType.CUSTOM, - // userId: user1.id, - // user: user1, - // renameTagId: '', - // id: 'user1-tag-2-id', - // assets: [], - // }); - - beforeAll(() => { - tagRepositoryMock = { - create: jest.fn(), - getByIds: jest.fn(), - getById: jest.fn(), - getByUserId: jest.fn(), - remove: jest.fn(), - update: jest.fn(), - }; - - sut = new TagService(tagRepositoryMock); - }); - - it('creates tag', async () => { - const createTagDto = { - name: 'user 1 tag 1', - type: TagType.CUSTOM, - }; - - tagRepositoryMock.create.mockResolvedValue(user1Tag1); - - const result = await sut.create(user1AuthUser, createTagDto); - - expect(result.userId).toEqual(user1AuthUser.id); - expect(result.name).toEqual(createTagDto.name); - expect(result.type).toEqual(createTagDto.type); - }); -}); diff --git a/server/apps/immich/src/api-v1/tag/tag.service.ts b/server/apps/immich/src/api-v1/tag/tag.service.ts deleted file mode 100644 index 71998661a..000000000 --- a/server/apps/immich/src/api-v1/tag/tag.service.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { TagEntity } from '@app/infra/entities'; -import { BadRequestException, Inject, Injectable, Logger } from '@nestjs/common'; -import { AuthUserDto } from '../../decorators/auth-user.decorator'; -import { CreateTagDto } from './dto/create-tag.dto'; -import { UpdateTagDto } from './dto/update-tag.dto'; -import { ITagRepository } from './tag.repository'; -import { mapTag, TagResponseDto } from '@app/domain'; - -@Injectable() -export class TagService { - readonly logger = new Logger(TagService.name); - - constructor(@Inject(ITagRepository) private _tagRepository: ITagRepository) {} - - async create(authUser: AuthUserDto, createTagDto: CreateTagDto) { - try { - const newTag = await this._tagRepository.create(authUser.id, createTagDto.type, createTagDto.name); - return mapTag(newTag); - } catch (e: any) { - this.logger.error(e, e.stack); - throw new BadRequestException(`Failed to create tag: ${e.detail}`); - } - } - - async findAll(authUser: AuthUserDto) { - const tags = await this._tagRepository.getByUserId(authUser.id); - return tags.map(mapTag); - } - - async findOne(authUser: AuthUserDto, id: string): Promise { - const tag = await this._tagRepository.getById(id, authUser.id); - - if (!tag) { - throw new BadRequestException('Tag not found'); - } - - return tag; - } - - async update(authUser: AuthUserDto, id: string, updateTagDto: UpdateTagDto): Promise { - const tag = await this.findOne(authUser, id); - - await this._tagRepository.update(tag, updateTagDto); - - return mapTag(tag); - } - - async remove(authUser: AuthUserDto, id: string): Promise { - const tag = await this.findOne(authUser, id); - await this._tagRepository.remove(tag); - } -} diff --git a/server/apps/immich/src/app.module.ts b/server/apps/immich/src/app.module.ts index a9168975b..51f5e3207 100644 --- a/server/apps/immich/src/app.module.ts +++ b/server/apps/immich/src/app.module.ts @@ -3,7 +3,6 @@ import { AssetModule } from './api-v1/asset/asset.module'; import { AlbumModule } from './api-v1/album/album.module'; import { AppController } from './app.controller'; import { ScheduleModule } from '@nestjs/schedule'; -import { TagModule } from './api-v1/tag/tag.module'; import { DomainModule, SearchService } from '@app/domain'; import { InfraModule } from '@app/infra'; import { @@ -20,6 +19,7 @@ import { SharedLinkController, SystemConfigController, UserController, + TagController, } from './controllers'; import { APP_GUARD } from '@nestjs/core'; import { AuthGuard } from './middlewares/auth.guard'; @@ -27,11 +27,11 @@ import { AppCronJobs } from './app.cron-jobs'; @Module({ imports: [ + // DomainModule.register({ imports: [InfraModule] }), AssetModule, AlbumModule, ScheduleModule.forRoot(), - TagModule, ], controllers: [ AppController, @@ -46,6 +46,7 @@ import { AppCronJobs } from './app.cron-jobs'; ServerInfoController, SharedLinkController, SystemConfigController, + TagController, UserController, PersonController, ], diff --git a/server/apps/immich/src/controllers/index.ts b/server/apps/immich/src/controllers/index.ts index d86db2beb..5917f0f79 100644 --- a/server/apps/immich/src/controllers/index.ts +++ b/server/apps/immich/src/controllers/index.ts @@ -10,4 +10,5 @@ export * from './search.controller'; export * from './server-info.controller'; export * from './shared-link.controller'; export * from './system-config.controller'; +export * from './tag.controller'; export * from './user.controller'; diff --git a/server/apps/immich/src/controllers/tag.controller.ts b/server/apps/immich/src/controllers/tag.controller.ts new file mode 100644 index 000000000..45b5233e2 --- /dev/null +++ b/server/apps/immich/src/controllers/tag.controller.ts @@ -0,0 +1,75 @@ +import { + AssetIdsDto, + AssetIdsResponseDto, + AssetResponseDto, + CreateTagDto, + TagResponseDto, + TagService, + UpdateTagDto, +} from '@app/domain'; +import { Body, Controller, Delete, Get, Param, Patch, Post, Put } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { AuthUserDto, GetAuthUser } from '../decorators/auth-user.decorator'; +import { Authenticated } from '../decorators/authenticated.decorator'; +import { UseValidation } from '../decorators/use-validation.decorator'; +import { UUIDParamDto } from './dto/uuid-param.dto'; + +@ApiTags('Tag') +@Controller('tag') +@Authenticated() +@UseValidation() +export class TagController { + constructor(private service: TagService) {} + + @Post() + createTag(@GetAuthUser() authUser: AuthUserDto, @Body() dto: CreateTagDto): Promise { + return this.service.create(authUser, dto); + } + + @Get() + getAllTags(@GetAuthUser() authUser: AuthUserDto): Promise { + return this.service.getAll(authUser); + } + + @Get(':id') + getTagById(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise { + return this.service.getById(authUser, id); + } + + @Patch(':id') + updateTag( + @GetAuthUser() authUser: AuthUserDto, + @Param() { id }: UUIDParamDto, + @Body() dto: UpdateTagDto, + ): Promise { + return this.service.update(authUser, id, dto); + } + + @Delete(':id') + deleteTag(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise { + return this.service.remove(authUser, id); + } + + @Get(':id/assets') + getTagAssets(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise { + return this.service.getAssets(authUser, id); + } + + @Put(':id/assets') + tagAssets( + @GetAuthUser() authUser: AuthUserDto, + @Param() { id }: UUIDParamDto, + @Body() dto: AssetIdsDto, + ): Promise { + return this.service.addAssets(authUser, id, dto); + } + + @Delete(':id/assets') + untagAssets( + @GetAuthUser() authUser: AuthUserDto, + @Body() dto: AssetIdsDto, + @Param() { id }: UUIDParamDto, + ): Promise { + return this.service.removeAssets(authUser, id, dto); + } +} diff --git a/server/immich-openapi-specs.json b/server/immich-openapi-specs.json index 45a6dcde3..a09c0e493 100644 --- a/server/immich-openapi-specs.json +++ b/server/immich-openapi-specs.json @@ -1785,6 +1785,357 @@ ] } }, + "/tag": { + "post": { + "operationId": "createTag", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTagDto" + } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TagResponseDto" + } + } + } + } + }, + "tags": [ + "Tag" + ], + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ] + }, + "get": { + "operationId": "getAllTags", + "parameters": [], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TagResponseDto" + } + } + } + } + } + }, + "tags": [ + "Tag" + ], + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ] + } + }, + "/tag/{id}": { + "get": { + "operationId": "getTagById", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TagResponseDto" + } + } + } + } + }, + "tags": [ + "Tag" + ], + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ] + }, + "patch": { + "operationId": "updateTag", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateTagDto" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TagResponseDto" + } + } + } + } + }, + "tags": [ + "Tag" + ], + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ] + }, + "delete": { + "operationId": "deleteTag", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "Tag" + ], + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ] + } + }, + "/tag/{id}/assets": { + "get": { + "operationId": "getTagAssets", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AssetResponseDto" + } + } + } + } + } + }, + "tags": [ + "Tag" + ], + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ] + }, + "put": { + "operationId": "tagAssets", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AssetIdsDto" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AssetIdsResponseDto" + } + } + } + } + } + }, + "tags": [ + "Tag" + ], + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ] + }, + "delete": { + "operationId": "untagAssets", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AssetIdsDto" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AssetIdsResponseDto" + } + } + } + } + } + }, + "tags": [ + "Tag" + ], + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ] + } + }, "/user": { "get": { "operationId": "getAllUsers", @@ -3598,206 +3949,6 @@ ] } }, - "/tag": { - "post": { - "operationId": "create", - "parameters": [], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateTagDto" - } - } - } - }, - "responses": { - "201": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TagResponseDto" - } - } - } - } - }, - "tags": [ - "Tag" - ], - "security": [ - { - "bearer": [] - }, - { - "cookie": [] - }, - { - "api_key": [] - } - ] - }, - "get": { - "operationId": "findAll", - "parameters": [], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TagResponseDto" - } - } - } - } - } - }, - "tags": [ - "Tag" - ], - "security": [ - { - "bearer": [] - }, - { - "cookie": [] - }, - { - "api_key": [] - } - ] - } - }, - "/tag/{id}": { - "get": { - "operationId": "findOne", - "parameters": [ - { - "name": "id", - "required": true, - "in": "path", - "schema": { - "format": "uuid", - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TagResponseDto" - } - } - } - } - }, - "tags": [ - "Tag" - ], - "security": [ - { - "bearer": [] - }, - { - "cookie": [] - }, - { - "api_key": [] - } - ] - }, - "patch": { - "operationId": "update", - "parameters": [ - { - "name": "id", - "required": true, - "in": "path", - "schema": { - "format": "uuid", - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateTagDto" - } - } - } - }, - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TagResponseDto" - } - } - } - } - }, - "tags": [ - "Tag" - ], - "security": [ - { - "bearer": [] - }, - { - "cookie": [] - }, - { - "api_key": [] - } - ] - }, - "delete": { - "operationId": "delete", - "parameters": [ - { - "name": "id", - "required": true, - "in": "path", - "schema": { - "format": "uuid", - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "" - } - }, - "tags": [ - "Tag" - ], - "security": [ - { - "bearer": [] - }, - { - "cookie": [] - }, - { - "api_key": [] - } - ] - } - }, "/album/count-by-user-id": { "get": { "operationId": "getAlbumCountByUserId", @@ -4384,26 +4535,22 @@ "TagResponseDto": { "type": "object", "properties": { - "id": { - "type": "string" - }, "type": { "$ref": "#/components/schemas/TagTypeEnum" }, + "id": { + "type": "string" + }, "name": { "type": "string" }, "userId": { "type": "string" - }, - "renameTagId": { - "type": "string", - "nullable": true } }, "required": [ - "id", "type", + "id", "name", "userId" ] @@ -5665,6 +5812,67 @@ "presetOptions" ] }, + "CreateTagDto": { + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/TagTypeEnum" + }, + "name": { + "type": "string" + } + }, + "required": [ + "type", + "name" + ] + }, + "UpdateTagDto": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "AssetIdsDto": { + "type": "object", + "properties": { + "assetIds": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + }, + "required": [ + "assetIds" + ] + }, + "AssetIdsResponseDto": { + "type": "object", + "properties": { + "assetId": { + "type": "string" + }, + "success": { + "type": "boolean" + }, + "error": { + "enum": [ + "duplicate", + "no_permission", + "not_found" + ], + "type": "string" + } + }, + "required": [ + "assetId", + "success" + ] + }, "CreateUserDto": { "type": "object", "properties": { @@ -6318,32 +6526,6 @@ "assetIds" ] }, - "CreateTagDto": { - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/TagTypeEnum" - }, - "name": { - "type": "string" - } - }, - "required": [ - "type", - "name" - ] - }, - "UpdateTagDto": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "renameTagId": { - "type": "string" - } - } - }, "AlbumCountResponseDto": { "type": "object", "properties": { diff --git a/server/libs/domain/src/asset/dto/asset-ids.dto.ts b/server/libs/domain/src/asset/dto/asset-ids.dto.ts new file mode 100644 index 000000000..8c9b60b3d --- /dev/null +++ b/server/libs/domain/src/asset/dto/asset-ids.dto.ts @@ -0,0 +1,6 @@ +import { ValidateUUID } from '../../../../../apps/immich/src/decorators/validate-uuid.decorator'; + +export class AssetIdsDto { + @ValidateUUID({ each: true }) + assetIds!: string[]; +} diff --git a/server/libs/domain/src/asset/dto/index.ts b/server/libs/domain/src/asset/dto/index.ts new file mode 100644 index 000000000..900b25d28 --- /dev/null +++ b/server/libs/domain/src/asset/dto/index.ts @@ -0,0 +1,2 @@ +export * from './asset-ids.dto'; +export * from './map-marker.dto'; diff --git a/server/libs/domain/src/asset/dto/map-marker.dto.ts b/server/libs/domain/src/asset/dto/map-marker.dto.ts index 5398b9547..af39a4980 100644 --- a/server/libs/domain/src/asset/dto/map-marker.dto.ts +++ b/server/libs/domain/src/asset/dto/map-marker.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; -import { toBoolean } from 'apps/immich/src/utils/transform.util'; import { Transform, Type } from 'class-transformer'; import { IsBoolean, IsDate, IsOptional } from 'class-validator'; +import { toBoolean } from '../../../../../apps/immich/src/utils/transform.util'; export class MapMarkerDto { @ApiProperty() diff --git a/server/libs/domain/src/asset/index.ts b/server/libs/domain/src/asset/index.ts index aa429787d..833df4c56 100644 --- a/server/libs/domain/src/asset/index.ts +++ b/server/libs/domain/src/asset/index.ts @@ -1,3 +1,4 @@ export * from './asset.repository'; export * from './asset.service'; +export * from './dto'; export * from './response-dto'; diff --git a/server/libs/domain/src/asset/response-dto/asset-ids-response.dto.ts b/server/libs/domain/src/asset/response-dto/asset-ids-response.dto.ts new file mode 100644 index 000000000..928bed24d --- /dev/null +++ b/server/libs/domain/src/asset/response-dto/asset-ids-response.dto.ts @@ -0,0 +1,11 @@ +export enum AssetIdErrorReason { + DUPLICATE = 'duplicate', + NO_PERMISSION = 'no_permission', + NOT_FOUND = 'not_found', +} + +export class AssetIdsResponseDto { + assetId!: string; + success!: boolean; + error?: AssetIdErrorReason; +} diff --git a/server/libs/domain/src/asset/response-dto/index.ts b/server/libs/domain/src/asset/response-dto/index.ts index 7e17e324f..b82249d2b 100644 --- a/server/libs/domain/src/asset/response-dto/index.ts +++ b/server/libs/domain/src/asset/response-dto/index.ts @@ -1,4 +1,5 @@ +export * from './asset-ids-response.dto'; export * from './asset-response.dto'; export * from './exif-response.dto'; -export * from './smart-info-response.dto'; export * from './map-marker-response.dto'; +export * from './smart-info-response.dto'; diff --git a/server/libs/domain/src/domain.module.ts b/server/libs/domain/src/domain.module.ts index 9aea15b44..ec2443cd7 100644 --- a/server/libs/domain/src/domain.module.ts +++ b/server/libs/domain/src/domain.module.ts @@ -17,6 +17,7 @@ import { SmartInfoService } from './smart-info'; import { StorageService } from './storage'; import { StorageTemplateService } from './storage-template'; import { INITIAL_SYSTEM_CONFIG, SystemConfigService } from './system-config'; +import { TagService } from './tag'; import { UserService } from './user'; const providers: Provider[] = [ @@ -38,6 +39,7 @@ const providers: Provider[] = [ StorageService, StorageTemplateService, SystemConfigService, + TagService, UserService, { provide: INITIAL_SYSTEM_CONFIG, diff --git a/server/libs/domain/src/tag/index.ts b/server/libs/domain/src/tag/index.ts index 0b415ca92..0ea02172e 100644 --- a/server/libs/domain/src/tag/index.ts +++ b/server/libs/domain/src/tag/index.ts @@ -1 +1,4 @@ -export * from './response-dto'; +export * from './tag-response.dto'; +export * from './tag.dto'; +export * from './tag.repository'; +export * from './tag.service'; diff --git a/server/libs/domain/src/tag/response-dto/index.ts b/server/libs/domain/src/tag/response-dto/index.ts deleted file mode 100644 index b08b1f61c..000000000 --- a/server/libs/domain/src/tag/response-dto/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './tag-response.dto'; diff --git a/server/libs/domain/src/tag/response-dto/tag-response.dto.ts b/server/libs/domain/src/tag/tag-response.dto.ts similarity index 83% rename from server/libs/domain/src/tag/response-dto/tag-response.dto.ts rename to server/libs/domain/src/tag/tag-response.dto.ts index b679a378c..a533b15c9 100644 --- a/server/libs/domain/src/tag/response-dto/tag-response.dto.ts +++ b/server/libs/domain/src/tag/tag-response.dto.ts @@ -2,17 +2,11 @@ import { TagEntity, TagType } from '@app/infra/entities'; import { ApiProperty } from '@nestjs/swagger'; export class TagResponseDto { - @ApiProperty() id!: string; - @ApiProperty({ enumName: 'TagTypeEnum', enum: TagType }) type!: string; - name!: string; - userId!: string; - - renameTagId?: string | null; } export function mapTag(entity: TagEntity): TagResponseDto { @@ -21,6 +15,5 @@ export function mapTag(entity: TagEntity): TagResponseDto { type: entity.type, name: entity.name, userId: entity.userId, - renameTagId: entity.renameTagId, }; } diff --git a/server/apps/immich/src/api-v1/tag/dto/create-tag.dto.ts b/server/libs/domain/src/tag/tag.dto.ts similarity index 64% rename from server/apps/immich/src/api-v1/tag/dto/create-tag.dto.ts rename to server/libs/domain/src/tag/tag.dto.ts index 69dd946eb..4cce47530 100644 --- a/server/apps/immich/src/api-v1/tag/dto/create-tag.dto.ts +++ b/server/libs/domain/src/tag/tag.dto.ts @@ -1,6 +1,6 @@ import { TagType } from '@app/infra/entities'; import { ApiProperty } from '@nestjs/swagger'; -import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; +import { IsEnum, IsNotEmpty, IsOptional, IsString } from 'class-validator'; export class CreateTagDto { @IsString() @@ -12,3 +12,9 @@ export class CreateTagDto { @ApiProperty({ enumName: 'TagTypeEnum', enum: TagType }) type!: TagType; } + +export class UpdateTagDto { + @IsString() + @IsOptional() + name?: string; +} diff --git a/server/libs/domain/src/tag/tag.repository.ts b/server/libs/domain/src/tag/tag.repository.ts new file mode 100644 index 000000000..4e6f583b4 --- /dev/null +++ b/server/libs/domain/src/tag/tag.repository.ts @@ -0,0 +1,16 @@ +import { AssetEntity, TagEntity } from '@app/infra/entities'; + +export const ITagRepository = 'ITagRepository'; + +export interface ITagRepository { + getById(userId: string, tagId: string): Promise; + getAll(userId: string): Promise; + create(tag: Partial): Promise; + update(tag: Partial): Promise; + remove(tag: TagEntity): Promise; + hasName(userId: string, name: string): Promise; + hasAsset(userId: string, tagId: string, assetId: string): Promise; + getAssets(userId: string, tagId: string): Promise; + addAssets(userId: string, tagId: string, assetIds: string[]): Promise; + removeAssets(userId: string, tagId: string, assetIds: string[]): Promise; +} diff --git a/server/libs/domain/src/tag/tag.service.spec.ts b/server/libs/domain/src/tag/tag.service.spec.ts new file mode 100644 index 000000000..62c145080 --- /dev/null +++ b/server/libs/domain/src/tag/tag.service.spec.ts @@ -0,0 +1,178 @@ +import { TagType } from '@app/infra/entities'; +import { BadRequestException } from '@nestjs/common'; +import { when } from 'jest-when'; +import { assetEntityStub, authStub, newTagRepositoryMock, tagResponseStub, tagStub } from '../../test'; +import { AssetIdErrorReason } from '../asset'; +import { ITagRepository } from './tag.repository'; +import { TagService } from './tag.service'; + +describe(TagService.name, () => { + let sut: TagService; + let tagMock: jest.Mocked; + + beforeEach(() => { + tagMock = newTagRepositoryMock(); + sut = new TagService(tagMock); + }); + + it('should work', () => { + expect(sut).toBeDefined(); + }); + + describe('getAll', () => { + it('should return all tags for a user', async () => { + tagMock.getAll.mockResolvedValue([tagStub.tag1]); + await expect(sut.getAll(authStub.admin)).resolves.toEqual([tagResponseStub.tag1]); + expect(tagMock.getAll).toHaveBeenCalledWith(authStub.admin.id); + }); + }); + + describe('getById', () => { + it('should throw an error for an invalid id', async () => { + tagMock.getById.mockResolvedValue(null); + await expect(sut.getById(authStub.admin, 'tag-1')).rejects.toBeInstanceOf(BadRequestException); + expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.id, 'tag-1'); + }); + + it('should return a tag for a user', async () => { + tagMock.getById.mockResolvedValue(tagStub.tag1); + await expect(sut.getById(authStub.admin, 'tag-1')).resolves.toEqual(tagResponseStub.tag1); + expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.id, 'tag-1'); + }); + }); + + describe('create', () => { + it('should throw an error for a duplicate tag', async () => { + tagMock.hasName.mockResolvedValue(true); + await expect(sut.create(authStub.admin, { name: 'tag-1', type: TagType.CUSTOM })).rejects.toBeInstanceOf( + BadRequestException, + ); + expect(tagMock.hasName).toHaveBeenCalledWith(authStub.admin.id, 'tag-1'); + expect(tagMock.create).not.toHaveBeenCalled(); + }); + + it('should create a new tag', async () => { + tagMock.create.mockResolvedValue(tagStub.tag1); + await expect(sut.create(authStub.admin, { name: 'tag-1', type: TagType.CUSTOM })).resolves.toEqual( + tagResponseStub.tag1, + ); + expect(tagMock.create).toHaveBeenCalledWith({ + userId: authStub.admin.id, + name: 'tag-1', + type: TagType.CUSTOM, + }); + }); + }); + + describe('update', () => { + it('should throw an error for an invalid id', async () => { + tagMock.getById.mockResolvedValue(null); + await expect(sut.update(authStub.admin, 'tag-1', { name: 'tag-2' })).rejects.toBeInstanceOf(BadRequestException); + expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.id, 'tag-1'); + expect(tagMock.remove).not.toHaveBeenCalled(); + }); + + it('should update a tag', async () => { + tagMock.getById.mockResolvedValue(tagStub.tag1); + tagMock.update.mockResolvedValue(tagStub.tag1); + await expect(sut.update(authStub.admin, 'tag-1', { name: 'tag-2' })).resolves.toEqual(tagResponseStub.tag1); + expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.id, 'tag-1'); + expect(tagMock.update).toHaveBeenCalledWith({ id: 'tag-1', name: 'tag-2' }); + }); + }); + + describe('remove', () => { + it('should throw an error for an invalid id', async () => { + tagMock.getById.mockResolvedValue(null); + await expect(sut.remove(authStub.admin, 'tag-1')).rejects.toBeInstanceOf(BadRequestException); + expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.id, 'tag-1'); + expect(tagMock.remove).not.toHaveBeenCalled(); + }); + + it('should remove a tag', async () => { + tagMock.getById.mockResolvedValue(tagStub.tag1); + await sut.remove(authStub.admin, 'tag-1'); + expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.id, 'tag-1'); + expect(tagMock.remove).toHaveBeenCalledWith(tagStub.tag1); + }); + }); + + describe('getAssets', () => { + it('should throw an error for an invalid id', async () => { + tagMock.getById.mockResolvedValue(null); + await expect(sut.remove(authStub.admin, 'tag-1')).rejects.toBeInstanceOf(BadRequestException); + expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.id, 'tag-1'); + expect(tagMock.remove).not.toHaveBeenCalled(); + }); + + it('should get the assets for a tag', async () => { + tagMock.getById.mockResolvedValue(tagStub.tag1); + tagMock.getAssets.mockResolvedValue([assetEntityStub.image]); + await sut.getAssets(authStub.admin, 'tag-1'); + expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.id, 'tag-1'); + expect(tagMock.getAssets).toHaveBeenCalledWith(authStub.admin.id, 'tag-1'); + }); + }); + + describe('addAssets', () => { + it('should throw an error for an invalid id', async () => { + tagMock.getById.mockResolvedValue(null); + await expect(sut.addAssets(authStub.admin, 'tag-1', { assetIds: ['asset-1'] })).rejects.toBeInstanceOf( + BadRequestException, + ); + expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.id, 'tag-1'); + expect(tagMock.addAssets).not.toHaveBeenCalled(); + }); + + it('should reject duplicate asset ids and accept new ones', async () => { + tagMock.getById.mockResolvedValue(tagStub.tag1); + + when(tagMock.hasAsset).calledWith(authStub.admin.id, 'tag-1', 'asset-1').mockResolvedValue(true); + when(tagMock.hasAsset).calledWith(authStub.admin.id, 'tag-1', 'asset-2').mockResolvedValue(false); + + await expect( + sut.addAssets(authStub.admin, 'tag-1', { + assetIds: ['asset-1', 'asset-2'], + }), + ).resolves.toEqual([ + { assetId: 'asset-1', success: false, error: AssetIdErrorReason.DUPLICATE }, + { assetId: 'asset-2', success: true }, + ]); + + expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.id, 'tag-1'); + expect(tagMock.hasAsset).toHaveBeenCalledTimes(2); + expect(tagMock.addAssets).toHaveBeenCalledWith(authStub.admin.id, 'tag-1', ['asset-2']); + }); + }); + + describe('removeAssets', () => { + it('should throw an error for an invalid id', async () => { + tagMock.getById.mockResolvedValue(null); + await expect(sut.removeAssets(authStub.admin, 'tag-1', { assetIds: ['asset-1'] })).rejects.toBeInstanceOf( + BadRequestException, + ); + expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.id, 'tag-1'); + expect(tagMock.removeAssets).not.toHaveBeenCalled(); + }); + + it('should accept accept ids that are tagged and reject the rest', async () => { + tagMock.getById.mockResolvedValue(tagStub.tag1); + + when(tagMock.hasAsset).calledWith(authStub.admin.id, 'tag-1', 'asset-1').mockResolvedValue(true); + when(tagMock.hasAsset).calledWith(authStub.admin.id, 'tag-1', 'asset-2').mockResolvedValue(false); + + await expect( + sut.removeAssets(authStub.admin, 'tag-1', { + assetIds: ['asset-1', 'asset-2'], + }), + ).resolves.toEqual([ + { assetId: 'asset-1', success: true }, + { assetId: 'asset-2', success: false, error: AssetIdErrorReason.NOT_FOUND }, + ]); + + expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.id, 'tag-1'); + expect(tagMock.hasAsset).toHaveBeenCalledTimes(2); + expect(tagMock.removeAssets).toHaveBeenCalledWith(authStub.admin.id, 'tag-1', ['asset-1']); + }); + }); +}); diff --git a/server/libs/domain/src/tag/tag.service.ts b/server/libs/domain/src/tag/tag.service.ts new file mode 100644 index 000000000..25075bd4e --- /dev/null +++ b/server/libs/domain/src/tag/tag.service.ts @@ -0,0 +1,104 @@ +import { BadRequestException, Inject, Injectable } from '@nestjs/common'; +import { AssetIdErrorReason, AssetIdsDto, AssetIdsResponseDto, AssetResponseDto, mapAsset } from '../asset'; +import { AuthUserDto } from '../auth'; +import { mapTag, TagResponseDto } from './tag-response.dto'; +import { CreateTagDto, UpdateTagDto } from './tag.dto'; +import { ITagRepository } from './tag.repository'; + +@Injectable() +export class TagService { + constructor(@Inject(ITagRepository) private repository: ITagRepository) {} + + getAll(authUser: AuthUserDto) { + return this.repository.getAll(authUser.id).then((tags) => tags.map(mapTag)); + } + + async getById(authUser: AuthUserDto, id: string): Promise { + const tag = await this.findOrFail(authUser, id); + return mapTag(tag); + } + + async create(authUser: AuthUserDto, dto: CreateTagDto) { + const duplicate = await this.repository.hasName(authUser.id, dto.name); + if (duplicate) { + throw new BadRequestException(`A tag with that name already exists`); + } + + const tag = await this.repository.create({ + userId: authUser.id, + name: dto.name, + type: dto.type, + }); + + return mapTag(tag); + } + + async update(authUser: AuthUserDto, id: string, dto: UpdateTagDto): Promise { + await this.findOrFail(authUser, id); + const tag = await this.repository.update({ id, name: dto.name }); + return mapTag(tag); + } + + async remove(authUser: AuthUserDto, id: string): Promise { + const tag = await this.findOrFail(authUser, id); + await this.repository.remove(tag); + } + + async getAssets(authUser: AuthUserDto, id: string): Promise { + await this.findOrFail(authUser, id); + const assets = await this.repository.getAssets(authUser.id, id); + return assets.map(mapAsset); + } + + async addAssets(authUser: AuthUserDto, id: string, dto: AssetIdsDto): Promise { + await this.findOrFail(authUser, id); + + const results: AssetIdsResponseDto[] = []; + for (const assetId of dto.assetIds) { + const hasAsset = await this.repository.hasAsset(authUser.id, id, assetId); + if (hasAsset) { + results.push({ assetId, success: false, error: AssetIdErrorReason.DUPLICATE }); + } else { + results.push({ assetId, success: true }); + } + } + + await this.repository.addAssets( + authUser.id, + id, + results.filter((result) => result.success).map((result) => result.assetId), + ); + + return results; + } + + async removeAssets(authUser: AuthUserDto, id: string, dto: AssetIdsDto): Promise { + await this.findOrFail(authUser, id); + + const results: AssetIdsResponseDto[] = []; + for (const assetId of dto.assetIds) { + const hasAsset = await this.repository.hasAsset(authUser.id, id, assetId); + if (!hasAsset) { + results.push({ assetId, success: false, error: AssetIdErrorReason.NOT_FOUND }); + } else { + results.push({ assetId, success: true }); + } + } + + await this.repository.removeAssets( + authUser.id, + id, + results.filter((result) => result.success).map((result) => result.assetId), + ); + + return results; + } + + private async findOrFail(authUser: AuthUserDto, id: string) { + const tag = await this.repository.getById(authUser.id, id); + if (!tag) { + throw new BadRequestException('Tag not found'); + } + return tag; + } +} diff --git a/server/libs/domain/test/fixtures.ts b/server/libs/domain/test/fixtures.ts index 3d00e7f24..50cf697cb 100644 --- a/server/libs/domain/test/fixtures.ts +++ b/server/libs/domain/test/fixtures.ts @@ -2,17 +2,19 @@ import { AlbumEntity, APIKeyEntity, AssetEntity, + AssetFaceEntity, AssetType, - PersonEntity, + ExifEntity, PartnerEntity, + PersonEntity, SharedLinkEntity, SharedLinkType, SystemConfig, + TagEntity, + TagType, TranscodePreset, UserEntity, UserTokenEntity, - AssetFaceEntity, - ExifEntity, } from '@app/infra/entities'; import { AlbumResponseDto, @@ -23,6 +25,7 @@ import { mapUser, SearchResult, SharedLinkResponseDto, + TagResponseDto, VideoFormat, VideoInfo, VideoStreamInfo, @@ -988,3 +991,24 @@ export const faceStub = { embedding: [1, 2, 3, 4], }), }; + +export const tagStub = { + tag1: Object.freeze({ + id: 'tag-1', + name: 'Tag1', + type: TagType.CUSTOM, + userId: userEntityStub.admin.id, + user: userEntityStub.admin, + renameTagId: null, + assets: [], + }), +}; + +export const tagResponseStub = { + tag1: Object.freeze({ + id: 'tag-1', + name: 'Tag1', + type: 'CUSTOM', + userId: 'admin_id', + }), +}; diff --git a/server/libs/domain/test/index.ts b/server/libs/domain/test/index.ts index 546b7ced5..f984d366c 100644 --- a/server/libs/domain/test/index.ts +++ b/server/libs/domain/test/index.ts @@ -15,6 +15,7 @@ export * from './shared-link.repository.mock'; export * from './smart-info.repository.mock'; export * from './storage.repository.mock'; export * from './system-config.repository.mock'; +export * from './tag.repository.mock'; export * from './user-token.repository.mock'; export * from './user.repository.mock'; diff --git a/server/libs/domain/test/tag.repository.mock.ts b/server/libs/domain/test/tag.repository.mock.ts new file mode 100644 index 000000000..d42d97e28 --- /dev/null +++ b/server/libs/domain/test/tag.repository.mock.ts @@ -0,0 +1,16 @@ +import { ITagRepository } from '../src'; + +export const newTagRepositoryMock = (): jest.Mocked => { + return { + getAll: jest.fn(), + getById: jest.fn(), + create: jest.fn(), + update: jest.fn(), + remove: jest.fn(), + hasAsset: jest.fn(), + hasName: jest.fn(), + getAssets: jest.fn(), + addAssets: jest.fn(), + removeAssets: jest.fn(), + }; +}; diff --git a/server/libs/infra/src/entities/index.ts b/server/libs/infra/src/entities/index.ts index f166b5927..6864a3f73 100644 --- a/server/libs/infra/src/entities/index.ts +++ b/server/libs/infra/src/entities/index.ts @@ -7,6 +7,7 @@ import { PersonEntity } from './person.entity'; import { SharedLinkEntity } from './shared-link.entity'; import { SmartInfoEntity } from './smart-info.entity'; import { SystemConfigEntity } from './system-config.entity'; +import { TagEntity } from './tag.entity'; import { UserTokenEntity } from './user-token.entity'; import { UserEntity } from './user.entity'; @@ -34,6 +35,7 @@ export const databaseEntities = [ SharedLinkEntity, SmartInfoEntity, SystemConfigEntity, + TagEntity, UserEntity, UserTokenEntity, ]; diff --git a/server/libs/infra/src/entities/tag.entity.ts b/server/libs/infra/src/entities/tag.entity.ts index cad4d4c97..a364529db 100644 --- a/server/libs/infra/src/entities/tag.entity.ts +++ b/server/libs/infra/src/entities/tag.entity.ts @@ -21,7 +21,7 @@ export class TagEntity { userId!: string; @Column({ type: 'uuid', comment: 'The new renamed tagId', nullable: true }) - renameTagId!: string; + renameTagId!: string | null; @ManyToMany(() => AssetEntity, (asset) => asset.tags) assets!: AssetEntity[]; diff --git a/server/libs/infra/src/infra.module.ts b/server/libs/infra/src/infra.module.ts index 34cd5f7c8..70b462db1 100644 --- a/server/libs/infra/src/infra.module.ts +++ b/server/libs/infra/src/infra.module.ts @@ -17,6 +17,7 @@ import { ISmartInfoRepository, IStorageRepository, ISystemConfigRepository, + ITagRepository, IUserRepository, IUserTokenRepository, } from '@app/domain'; @@ -45,6 +46,7 @@ import { SharedLinkRepository, SmartInfoRepository, SystemConfigRepository, + TagRepository, TypesenseRepository, UserRepository, UserTokenRepository, @@ -68,6 +70,7 @@ const providers: Provider[] = [ { provide: ISmartInfoRepository, useClass: SmartInfoRepository }, { provide: IStorageRepository, useClass: FilesystemProvider }, { provide: ISystemConfigRepository, useClass: SystemConfigRepository }, + { provide: ITagRepository, useClass: TagRepository }, { provide: IUserRepository, useClass: UserRepository }, { provide: IUserTokenRepository, useClass: UserTokenRepository }, ]; diff --git a/server/libs/infra/src/repositories/index.ts b/server/libs/infra/src/repositories/index.ts index 75347863e..bb49e336e 100644 --- a/server/libs/infra/src/repositories/index.ts +++ b/server/libs/infra/src/repositories/index.ts @@ -14,6 +14,7 @@ export * from './person.repository'; export * from './shared-link.repository'; export * from './smart-info.repository'; export * from './system-config.repository'; +export * from './tag.repository'; export * from './typesense.repository'; export * from './user-token.repository'; export * from './user.repository'; diff --git a/server/libs/infra/src/repositories/tag.repository.ts b/server/libs/infra/src/repositories/tag.repository.ts new file mode 100644 index 000000000..8713f0650 --- /dev/null +++ b/server/libs/infra/src/repositories/tag.repository.ts @@ -0,0 +1,123 @@ +import { ITagRepository } from '@app/domain'; +import { AssetEntity, TagEntity } from '@app/infra/entities'; +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +@Injectable() +export class TagRepository implements ITagRepository { + constructor( + @InjectRepository(AssetEntity) private assetRepository: Repository, + @InjectRepository(TagEntity) private repository: Repository, + ) {} + + getById(userId: string, id: string): Promise { + return this.repository.findOne({ + where: { + id, + userId, + }, + relations: { + user: true, + }, + }); + } + + getAll(userId: string): Promise { + return this.repository.find({ where: { userId } }); + } + + create(tag: Partial): Promise { + return this.save(tag); + } + + update(tag: Partial): Promise { + return this.save(tag); + } + + async remove(tag: TagEntity): Promise { + await this.repository.remove(tag); + } + + async getAssets(userId: string, tagId: string): Promise { + return this.assetRepository.find({ + where: { + tags: { + userId, + id: tagId, + }, + }, + relations: { + exifInfo: true, + tags: true, + faces: { + person: true, + }, + }, + order: { + createdAt: 'ASC', + }, + }); + } + + async addAssets(userId: string, id: string, assetIds: string[]): Promise { + for (const assetId of assetIds) { + const asset = await this.assetRepository.findOneOrFail({ + where: { + ownerId: userId, + id: assetId, + }, + relations: { + tags: true, + }, + }); + asset.tags.push({ id } as TagEntity); + await this.assetRepository.save(asset); + } + } + + async removeAssets(userId: string, id: string, assetIds: string[]): Promise { + for (const assetId of assetIds) { + const asset = await this.assetRepository.findOneOrFail({ + where: { + ownerId: userId, + id: assetId, + }, + relations: { + tags: true, + }, + }); + asset.tags = asset.tags.filter((tag) => tag.id !== id); + await this.assetRepository.save(asset); + } + } + + hasAsset(userId: string, tagId: string, assetId: string): Promise { + return this.repository.exist({ + where: { + id: tagId, + userId, + assets: { + id: assetId, + }, + }, + relations: { + assets: true, + }, + }); + } + + hasName(userId: string, name: string): Promise { + return this.repository.exist({ + where: { + name, + userId, + }, + }); + } + + private async save(tag: Partial): Promise { + const { id } = await this.repository.save(tag); + return this.repository.findOneOrFail({ where: { id }, relations: { user: true } }); + } +} diff --git a/web/src/api/open-api/api.ts b/web/src/api/open-api/api.ts index e8afa9343..5fbe6bd5d 100644 --- a/web/src/api/open-api/api.ts +++ b/web/src/api/open-api/api.ts @@ -536,6 +536,53 @@ export interface AssetFileUploadResponseDto { */ 'duplicate': boolean; } +/** + * + * @export + * @interface AssetIdsDto + */ +export interface AssetIdsDto { + /** + * + * @type {Array} + * @memberof AssetIdsDto + */ + 'assetIds': Array; +} +/** + * + * @export + * @interface AssetIdsResponseDto + */ +export interface AssetIdsResponseDto { + /** + * + * @type {string} + * @memberof AssetIdsResponseDto + */ + 'assetId': string; + /** + * + * @type {boolean} + * @memberof AssetIdsResponseDto + */ + 'success': boolean; + /** + * + * @type {string} + * @memberof AssetIdsResponseDto + */ + 'error'?: AssetIdsResponseDtoErrorEnum; +} + +export const AssetIdsResponseDtoErrorEnum = { + Duplicate: 'duplicate', + NoPermission: 'no_permission', + NotFound: 'not_found' +} as const; + +export type AssetIdsResponseDtoErrorEnum = typeof AssetIdsResponseDtoErrorEnum[keyof typeof AssetIdsResponseDtoErrorEnum]; + /** * * @export @@ -2420,18 +2467,18 @@ export interface SystemConfigTemplateStorageOptionDto { * @interface TagResponseDto */ export interface TagResponseDto { - /** - * - * @type {string} - * @memberof TagResponseDto - */ - 'id': string; /** * * @type {TagTypeEnum} * @memberof TagResponseDto */ 'type': TagTypeEnum; + /** + * + * @type {string} + * @memberof TagResponseDto + */ + 'id': string; /** * * @type {string} @@ -2444,12 +2491,6 @@ export interface TagResponseDto { * @memberof TagResponseDto */ 'userId': string; - /** - * - * @type {string} - * @memberof TagResponseDto - */ - 'renameTagId'?: string | null; } @@ -2558,12 +2599,6 @@ export interface UpdateTagDto { * @memberof UpdateTagDto */ 'name'?: string; - /** - * - * @type {string} - * @memberof UpdateTagDto - */ - 'renameTagId'?: string; } /** * @@ -10647,57 +10682,15 @@ export class SystemConfigApi extends BaseAPI { */ export const TagApiAxiosParamCreator = function (configuration?: Configuration) { return { - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - _delete: async (id: string, options: AxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('_delete', 'id', id) - const localVarPath = `/tag/{id}` - .replace(`{${"id"}}`, encodeURIComponent(String(id))); - // 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: 'DELETE', ...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 {CreateTagDto} createTagDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ - create: async (createTagDto: CreateTagDto, options: AxiosRequestConfig = {}): Promise => { + createTag: async (createTagDto: CreateTagDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'createTagDto' is not null or undefined - assertParamExists('create', 'createTagDto', createTagDto) + assertParamExists('createTag', 'createTagDto', createTagDto) const localVarPath = `/tag`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); @@ -10735,10 +10728,52 @@ export const TagApiAxiosParamCreator = function (configuration?: Configuration) }, /** * + * @param {string} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ - findAll: async (options: AxiosRequestConfig = {}): Promise => { + deleteTag: async (id: string, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists('deleteTag', 'id', id) + const localVarPath = `/tag/{id}` + .replace(`{${"id"}}`, encodeURIComponent(String(id))); + // 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: 'DELETE', ...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 {*} [options] Override http request option. + * @throws {RequiredError} + */ + getAllTags: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/tag`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); @@ -10777,9 +10812,51 @@ export const TagApiAxiosParamCreator = function (configuration?: Configuration) * @param {*} [options] Override http request option. * @throws {RequiredError} */ - findOne: async (id: string, options: AxiosRequestConfig = {}): Promise => { + getTagAssets: async (id: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined - assertParamExists('findOne', 'id', id) + assertParamExists('getTagAssets', 'id', id) + const localVarPath = `/tag/{id}/assets` + .replace(`{${"id"}}`, encodeURIComponent(String(id))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication cookie required + + // authentication api_key required + await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) + + // authentication bearer required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getTagById: async (id: string, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists('getTagById', 'id', id) const localVarPath = `/tag/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. @@ -10813,6 +10890,102 @@ export const TagApiAxiosParamCreator = function (configuration?: Configuration) options: localVarRequestOptions, }; }, + /** + * + * @param {string} id + * @param {AssetIdsDto} assetIdsDto + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + tagAssets: async (id: string, assetIdsDto: AssetIdsDto, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists('tagAssets', 'id', id) + // verify required parameter 'assetIdsDto' is not null or undefined + assertParamExists('tagAssets', 'assetIdsDto', assetIdsDto) + const localVarPath = `/tag/{id}/assets` + .replace(`{${"id"}}`, encodeURIComponent(String(id))); + // 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: 'PUT', ...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(assetIdsDto, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @param {string} id + * @param {AssetIdsDto} assetIdsDto + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + untagAssets: async (id: string, assetIdsDto: AssetIdsDto, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists('untagAssets', 'id', id) + // verify required parameter 'assetIdsDto' is not null or undefined + assertParamExists('untagAssets', 'assetIdsDto', assetIdsDto) + const localVarPath = `/tag/{id}/assets` + .replace(`{${"id"}}`, encodeURIComponent(String(id))); + // 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: 'DELETE', ...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(assetIdsDto, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, /** * * @param {string} id @@ -10820,11 +10993,11 @@ export const TagApiAxiosParamCreator = function (configuration?: Configuration) * @param {*} [options] Override http request option. * @throws {RequiredError} */ - update: async (id: string, updateTagDto: UpdateTagDto, options: AxiosRequestConfig = {}): Promise => { + updateTag: async (id: string, updateTagDto: UpdateTagDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined - assertParamExists('update', 'id', id) + assertParamExists('updateTag', 'id', id) // verify required parameter 'updateTagDto' is not null or undefined - assertParamExists('update', 'updateTagDto', updateTagDto) + assertParamExists('updateTag', 'updateTagDto', updateTagDto) const localVarPath = `/tag/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. @@ -10871,33 +11044,14 @@ export const TagApiAxiosParamCreator = function (configuration?: Configuration) export const TagApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = TagApiAxiosParamCreator(configuration) return { - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async _delete(id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator._delete(id, options); - return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); - }, /** * * @param {CreateTagDto} createTagDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async create(createTagDto: CreateTagDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.create(createTagDto, options); - return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async findAll(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.findAll(options); + async createTag(createTagDto: CreateTagDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.createTag(createTagDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** @@ -10906,8 +11060,59 @@ export const TagApiFp = function(configuration?: Configuration) { * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async findOne(id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.findOne(id, options); + async deleteTag(id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.deleteTag(id, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getAllTags(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getAllTags(options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getTagAssets(id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getTagAssets(id, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getTagById(id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getTagById(id, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * + * @param {string} id + * @param {AssetIdsDto} assetIdsDto + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async tagAssets(id: string, assetIdsDto: AssetIdsDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.tagAssets(id, assetIdsDto, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * + * @param {string} id + * @param {AssetIdsDto} assetIdsDto + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async untagAssets(id: string, assetIdsDto: AssetIdsDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.untagAssets(id, assetIdsDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** @@ -10917,8 +11122,8 @@ export const TagApiFp = function(configuration?: Configuration) { * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async update(id: string, updateTagDto: UpdateTagDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.update(id, updateTagDto, options); + async updateTag(id: string, updateTagDto: UpdateTagDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.updateTag(id, updateTagDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } @@ -10931,31 +11136,14 @@ export const TagApiFp = function(configuration?: Configuration) { export const TagApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = TagApiFp(configuration) return { - /** - * - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - _delete(id: string, options?: any): AxiosPromise { - return localVarFp._delete(id, options).then((request) => request(axios, basePath)); - }, /** * * @param {CreateTagDto} createTagDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ - create(createTagDto: CreateTagDto, options?: any): AxiosPromise { - return localVarFp.create(createTagDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - findAll(options?: any): AxiosPromise> { - return localVarFp.findAll(options).then((request) => request(axios, basePath)); + createTag(createTagDto: CreateTagDto, options?: any): AxiosPromise { + return localVarFp.createTag(createTagDto, options).then((request) => request(axios, basePath)); }, /** * @@ -10963,8 +11151,54 @@ export const TagApiFactory = function (configuration?: Configuration, basePath?: * @param {*} [options] Override http request option. * @throws {RequiredError} */ - findOne(id: string, options?: any): AxiosPromise { - return localVarFp.findOne(id, options).then((request) => request(axios, basePath)); + deleteTag(id: string, options?: any): AxiosPromise { + return localVarFp.deleteTag(id, options).then((request) => request(axios, basePath)); + }, + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getAllTags(options?: any): AxiosPromise> { + return localVarFp.getAllTags(options).then((request) => request(axios, basePath)); + }, + /** + * + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getTagAssets(id: string, options?: any): AxiosPromise> { + return localVarFp.getTagAssets(id, options).then((request) => request(axios, basePath)); + }, + /** + * + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getTagById(id: string, options?: any): AxiosPromise { + return localVarFp.getTagById(id, options).then((request) => request(axios, basePath)); + }, + /** + * + * @param {string} id + * @param {AssetIdsDto} assetIdsDto + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + tagAssets(id: string, assetIdsDto: AssetIdsDto, options?: any): AxiosPromise> { + return localVarFp.tagAssets(id, assetIdsDto, options).then((request) => request(axios, basePath)); + }, + /** + * + * @param {string} id + * @param {AssetIdsDto} assetIdsDto + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + untagAssets(id: string, assetIdsDto: AssetIdsDto, options?: any): AxiosPromise> { + return localVarFp.untagAssets(id, assetIdsDto, options).then((request) => request(axios, basePath)); }, /** * @@ -10973,71 +11207,127 @@ export const TagApiFactory = function (configuration?: Configuration, basePath?: * @param {*} [options] Override http request option. * @throws {RequiredError} */ - update(id: string, updateTagDto: UpdateTagDto, options?: any): AxiosPromise { - return localVarFp.update(id, updateTagDto, options).then((request) => request(axios, basePath)); + updateTag(id: string, updateTagDto: UpdateTagDto, options?: any): AxiosPromise { + return localVarFp.updateTag(id, updateTagDto, options).then((request) => request(axios, basePath)); }, }; }; /** - * Request parameters for _delete operation in TagApi. + * Request parameters for createTag operation in TagApi. * @export - * @interface TagApiDeleteRequest + * @interface TagApiCreateTagRequest */ -export interface TagApiDeleteRequest { - /** - * - * @type {string} - * @memberof TagApiDelete - */ - readonly id: string -} - -/** - * Request parameters for create operation in TagApi. - * @export - * @interface TagApiCreateRequest - */ -export interface TagApiCreateRequest { +export interface TagApiCreateTagRequest { /** * * @type {CreateTagDto} - * @memberof TagApiCreate + * @memberof TagApiCreateTag */ readonly createTagDto: CreateTagDto } /** - * Request parameters for findOne operation in TagApi. + * Request parameters for deleteTag operation in TagApi. * @export - * @interface TagApiFindOneRequest + * @interface TagApiDeleteTagRequest */ -export interface TagApiFindOneRequest { +export interface TagApiDeleteTagRequest { /** * * @type {string} - * @memberof TagApiFindOne + * @memberof TagApiDeleteTag */ readonly id: string } /** - * Request parameters for update operation in TagApi. + * Request parameters for getTagAssets operation in TagApi. * @export - * @interface TagApiUpdateRequest + * @interface TagApiGetTagAssetsRequest */ -export interface TagApiUpdateRequest { +export interface TagApiGetTagAssetsRequest { /** * * @type {string} - * @memberof TagApiUpdate + * @memberof TagApiGetTagAssets + */ + readonly id: string +} + +/** + * Request parameters for getTagById operation in TagApi. + * @export + * @interface TagApiGetTagByIdRequest + */ +export interface TagApiGetTagByIdRequest { + /** + * + * @type {string} + * @memberof TagApiGetTagById + */ + readonly id: string +} + +/** + * Request parameters for tagAssets operation in TagApi. + * @export + * @interface TagApiTagAssetsRequest + */ +export interface TagApiTagAssetsRequest { + /** + * + * @type {string} + * @memberof TagApiTagAssets + */ + readonly id: string + + /** + * + * @type {AssetIdsDto} + * @memberof TagApiTagAssets + */ + readonly assetIdsDto: AssetIdsDto +} + +/** + * Request parameters for untagAssets operation in TagApi. + * @export + * @interface TagApiUntagAssetsRequest + */ +export interface TagApiUntagAssetsRequest { + /** + * + * @type {string} + * @memberof TagApiUntagAssets + */ + readonly id: string + + /** + * + * @type {AssetIdsDto} + * @memberof TagApiUntagAssets + */ + readonly assetIdsDto: AssetIdsDto +} + +/** + * Request parameters for updateTag operation in TagApi. + * @export + * @interface TagApiUpdateTagRequest + */ +export interface TagApiUpdateTagRequest { + /** + * + * @type {string} + * @memberof TagApiUpdateTag */ readonly id: string /** * * @type {UpdateTagDto} - * @memberof TagApiUpdate + * @memberof TagApiUpdateTag */ readonly updateTagDto: UpdateTagDto } @@ -11051,24 +11341,24 @@ export interface TagApiUpdateRequest { export class TagApi extends BaseAPI { /** * - * @param {TagApiDeleteRequest} requestParameters Request parameters. + * @param {TagApiCreateTagRequest} requestParameters Request parameters. * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TagApi */ - public _delete(requestParameters: TagApiDeleteRequest, options?: AxiosRequestConfig) { - return TagApiFp(this.configuration)._delete(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); + public createTag(requestParameters: TagApiCreateTagRequest, options?: AxiosRequestConfig) { + return TagApiFp(this.configuration).createTag(requestParameters.createTagDto, options).then((request) => request(this.axios, this.basePath)); } /** * - * @param {TagApiCreateRequest} requestParameters Request parameters. + * @param {TagApiDeleteTagRequest} requestParameters Request parameters. * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TagApi */ - public create(requestParameters: TagApiCreateRequest, options?: AxiosRequestConfig) { - return TagApiFp(this.configuration).create(requestParameters.createTagDto, options).then((request) => request(this.axios, this.basePath)); + public deleteTag(requestParameters: TagApiDeleteTagRequest, options?: AxiosRequestConfig) { + return TagApiFp(this.configuration).deleteTag(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); } /** @@ -11077,30 +11367,63 @@ export class TagApi extends BaseAPI { * @throws {RequiredError} * @memberof TagApi */ - public findAll(options?: AxiosRequestConfig) { - return TagApiFp(this.configuration).findAll(options).then((request) => request(this.axios, this.basePath)); + public getAllTags(options?: AxiosRequestConfig) { + return TagApiFp(this.configuration).getAllTags(options).then((request) => request(this.axios, this.basePath)); } /** * - * @param {TagApiFindOneRequest} requestParameters Request parameters. + * @param {TagApiGetTagAssetsRequest} requestParameters Request parameters. * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TagApi */ - public findOne(requestParameters: TagApiFindOneRequest, options?: AxiosRequestConfig) { - return TagApiFp(this.configuration).findOne(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); + public getTagAssets(requestParameters: TagApiGetTagAssetsRequest, options?: AxiosRequestConfig) { + return TagApiFp(this.configuration).getTagAssets(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); } /** * - * @param {TagApiUpdateRequest} requestParameters Request parameters. + * @param {TagApiGetTagByIdRequest} requestParameters Request parameters. * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TagApi */ - public update(requestParameters: TagApiUpdateRequest, options?: AxiosRequestConfig) { - return TagApiFp(this.configuration).update(requestParameters.id, requestParameters.updateTagDto, options).then((request) => request(this.axios, this.basePath)); + public getTagById(requestParameters: TagApiGetTagByIdRequest, options?: AxiosRequestConfig) { + return TagApiFp(this.configuration).getTagById(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @param {TagApiTagAssetsRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof TagApi + */ + public tagAssets(requestParameters: TagApiTagAssetsRequest, options?: AxiosRequestConfig) { + return TagApiFp(this.configuration).tagAssets(requestParameters.id, requestParameters.assetIdsDto, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @param {TagApiUntagAssetsRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof TagApi + */ + public untagAssets(requestParameters: TagApiUntagAssetsRequest, options?: AxiosRequestConfig) { + return TagApiFp(this.configuration).untagAssets(requestParameters.id, requestParameters.assetIdsDto, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @param {TagApiUpdateTagRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof TagApi + */ + public updateTag(requestParameters: TagApiUpdateTagRequest, options?: AxiosRequestConfig) { + return TagApiFp(this.configuration).updateTag(requestParameters.id, requestParameters.updateTagDto, options).then((request) => request(this.axios, this.basePath)); } }