Bladeren bron

refactor(server): device info service (#1071)

* refactor(server): device info service

* use upsertDeviceInfo in mobile app

* fix: return types and dedupe code

Co-authored-by: Fynn Petersen-Frey <zoodyy@users.noreply.github.com>
Jason Rasmussen 2 jaren geleden
bovenliggende
commit
cefdd86b7f

+ 2 - 2
mobile/lib/modules/backup/services/backup.service.dart

@@ -376,8 +376,8 @@ class BackupService {
     DeviceTypeEnum deviceType,
   ) async {
     try {
-      var updatedDeviceInfo = await _apiService.deviceInfoApi.updateDeviceInfo(
-        UpdateDeviceInfoDto(
+      var updatedDeviceInfo = await _apiService.deviceInfoApi.upsertDeviceInfo(
+        UpsertDeviceInfoDto(
           deviceId: deviceId,
           deviceType: deviceType,
           isAutoBackup: status,

+ 2 - 2
mobile/lib/modules/login/providers/authentication.provider.dart

@@ -210,8 +210,8 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
     // Register device info
     try {
       DeviceInfoResponseDto? deviceInfo =
-          await _apiService.deviceInfoApi.createDeviceInfo(
-        CreateDeviceInfoDto(
+          await _apiService.deviceInfoApi.upsertDeviceInfo(
+        UpsertDeviceInfoDto(
           deviceId: state.deviceId,
           deviceType: state.deviceType,
         ),

+ 3 - 6
mobile/openapi/.openapi-generator/FILES

@@ -24,7 +24,6 @@ doc/CheckDuplicateAssetResponseDto.md
 doc/CheckExistingAssetsDto.md
 doc/CheckExistingAssetsResponseDto.md
 doc/CreateAlbumDto.md
-doc/CreateDeviceInfoDto.md
 doc/CreateProfileImageResponseDto.md
 doc/CreateTagDto.md
 doc/CreateUserDto.md
@@ -72,9 +71,9 @@ doc/ThumbnailFormat.md
 doc/TimeGroupEnum.md
 doc/UpdateAlbumDto.md
 doc/UpdateAssetDto.md
-doc/UpdateDeviceInfoDto.md
 doc/UpdateTagDto.md
 doc/UpdateUserDto.md
+doc/UpsertDeviceInfoDto.md
 doc/UsageByUserDto.md
 doc/UserApi.md
 doc/UserCountResponseDto.md
@@ -118,7 +117,6 @@ lib/model/check_duplicate_asset_response_dto.dart
 lib/model/check_existing_assets_dto.dart
 lib/model/check_existing_assets_response_dto.dart
 lib/model/create_album_dto.dart
-lib/model/create_device_info_dto.dart
 lib/model/create_profile_image_response_dto.dart
 lib/model/create_tag_dto.dart
 lib/model/create_user_dto.dart
@@ -160,9 +158,9 @@ lib/model/thumbnail_format.dart
 lib/model/time_group_enum.dart
 lib/model/update_album_dto.dart
 lib/model/update_asset_dto.dart
-lib/model/update_device_info_dto.dart
 lib/model/update_tag_dto.dart
 lib/model/update_user_dto.dart
+lib/model/upsert_device_info_dto.dart
 lib/model/usage_by_user_dto.dart
 lib/model/user_count_response_dto.dart
 lib/model/user_response_dto.dart
@@ -189,7 +187,6 @@ test/check_duplicate_asset_response_dto_test.dart
 test/check_existing_assets_dto_test.dart
 test/check_existing_assets_response_dto_test.dart
 test/create_album_dto_test.dart
-test/create_device_info_dto_test.dart
 test/create_profile_image_response_dto_test.dart
 test/create_tag_dto_test.dart
 test/create_user_dto_test.dart
@@ -237,9 +234,9 @@ test/thumbnail_format_test.dart
 test/time_group_enum_test.dart
 test/update_album_dto_test.dart
 test/update_asset_dto_test.dart
-test/update_device_info_dto_test.dart
 test/update_tag_dto_test.dart
 test/update_user_dto_test.dart
+test/upsert_device_info_dto_test.dart
 test/usage_by_user_dto_test.dart
 test/user_api_test.dart
 test/user_count_response_dto_test.dart

+ 2 - 2
mobile/openapi/README.md

@@ -101,6 +101,7 @@ Class | Method | HTTP request | Description
 *AuthenticationApi* | [**validateAccessToken**](doc//AuthenticationApi.md#validateaccesstoken) | **POST** /auth/validateToken | 
 *DeviceInfoApi* | [**createDeviceInfo**](doc//DeviceInfoApi.md#createdeviceinfo) | **POST** /device-info | 
 *DeviceInfoApi* | [**updateDeviceInfo**](doc//DeviceInfoApi.md#updatedeviceinfo) | **PATCH** /device-info | 
+*DeviceInfoApi* | [**upsertDeviceInfo**](doc//DeviceInfoApi.md#upsertdeviceinfo) | **PUT** /device-info | 
 *JobApi* | [**getAllJobsStatus**](doc//JobApi.md#getalljobsstatus) | **GET** /jobs | 
 *JobApi* | [**getJobStatus**](doc//JobApi.md#getjobstatus) | **GET** /jobs/{jobId} | 
 *JobApi* | [**sendJobCommand**](doc//JobApi.md#sendjobcommand) | **PUT** /jobs/{jobId} | 
@@ -149,7 +150,6 @@ Class | Method | HTTP request | Description
  - [CheckExistingAssetsDto](doc//CheckExistingAssetsDto.md)
  - [CheckExistingAssetsResponseDto](doc//CheckExistingAssetsResponseDto.md)
  - [CreateAlbumDto](doc//CreateAlbumDto.md)
- - [CreateDeviceInfoDto](doc//CreateDeviceInfoDto.md)
  - [CreateProfileImageResponseDto](doc//CreateProfileImageResponseDto.md)
  - [CreateTagDto](doc//CreateTagDto.md)
  - [CreateUserDto](doc//CreateUserDto.md)
@@ -191,9 +191,9 @@ Class | Method | HTTP request | Description
  - [TimeGroupEnum](doc//TimeGroupEnum.md)
  - [UpdateAlbumDto](doc//UpdateAlbumDto.md)
  - [UpdateAssetDto](doc//UpdateAssetDto.md)
- - [UpdateDeviceInfoDto](doc//UpdateDeviceInfoDto.md)
  - [UpdateTagDto](doc//UpdateTagDto.md)
  - [UpdateUserDto](doc//UpdateUserDto.md)
+ - [UpsertDeviceInfoDto](doc//UpsertDeviceInfoDto.md)
  - [UsageByUserDto](doc//UsageByUserDto.md)
  - [UserCountResponseDto](doc//UserCountResponseDto.md)
  - [UserResponseDto](doc//UserResponseDto.md)

+ 60 - 8
mobile/openapi/doc/DeviceInfoApi.md

@@ -11,13 +11,16 @@ Method | HTTP request | Description
 ------------- | ------------- | -------------
 [**createDeviceInfo**](DeviceInfoApi.md#createdeviceinfo) | **POST** /device-info | 
 [**updateDeviceInfo**](DeviceInfoApi.md#updatedeviceinfo) | **PATCH** /device-info | 
+[**upsertDeviceInfo**](DeviceInfoApi.md#upsertdeviceinfo) | **PUT** /device-info | 
 
 
 # **createDeviceInfo**
-> DeviceInfoResponseDto createDeviceInfo(createDeviceInfoDto)
+> DeviceInfoResponseDto createDeviceInfo(upsertDeviceInfoDto)
 
 
 
+@deprecated
+
 ### Example
 ```dart
 import 'package:openapi/api.dart';
@@ -29,10 +32,10 @@ import 'package:openapi/api.dart';
 //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
 
 final api_instance = DeviceInfoApi();
-final createDeviceInfoDto = CreateDeviceInfoDto(); // CreateDeviceInfoDto | 
+final upsertDeviceInfoDto = UpsertDeviceInfoDto(); // UpsertDeviceInfoDto | 
 
 try {
-    final result = api_instance.createDeviceInfo(createDeviceInfoDto);
+    final result = api_instance.createDeviceInfo(upsertDeviceInfoDto);
     print(result);
 } catch (e) {
     print('Exception when calling DeviceInfoApi->createDeviceInfo: $e\n');
@@ -43,7 +46,7 @@ try {
 
 Name | Type | Description  | Notes
 ------------- | ------------- | ------------- | -------------
- **createDeviceInfoDto** | [**CreateDeviceInfoDto**](CreateDeviceInfoDto.md)|  | 
+ **upsertDeviceInfoDto** | [**UpsertDeviceInfoDto**](UpsertDeviceInfoDto.md)|  | 
 
 ### Return type
 
@@ -61,9 +64,11 @@ 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)
 
 # **updateDeviceInfo**
-> DeviceInfoResponseDto updateDeviceInfo(updateDeviceInfoDto)
+> DeviceInfoResponseDto updateDeviceInfo(upsertDeviceInfoDto)
+
 
 
+@deprecated
 
 ### Example
 ```dart
@@ -76,10 +81,10 @@ import 'package:openapi/api.dart';
 //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
 
 final api_instance = DeviceInfoApi();
-final updateDeviceInfoDto = UpdateDeviceInfoDto(); // UpdateDeviceInfoDto | 
+final upsertDeviceInfoDto = UpsertDeviceInfoDto(); // UpsertDeviceInfoDto | 
 
 try {
-    final result = api_instance.updateDeviceInfo(updateDeviceInfoDto);
+    final result = api_instance.updateDeviceInfo(upsertDeviceInfoDto);
     print(result);
 } catch (e) {
     print('Exception when calling DeviceInfoApi->updateDeviceInfo: $e\n');
@@ -90,7 +95,54 @@ try {
 
 Name | Type | Description  | Notes
 ------------- | ------------- | ------------- | -------------
- **updateDeviceInfoDto** | [**UpdateDeviceInfoDto**](UpdateDeviceInfoDto.md)|  | 
+ **upsertDeviceInfoDto** | [**UpsertDeviceInfoDto**](UpsertDeviceInfoDto.md)|  | 
+
+### Return type
+
+[**DeviceInfoResponseDto**](DeviceInfoResponseDto.md)
+
+### Authorization
+
+[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)
+
+# **upsertDeviceInfo**
+> DeviceInfoResponseDto upsertDeviceInfo(upsertDeviceInfoDto)
+
+
+
+### Example
+```dart
+import 'package:openapi/api.dart';
+// TODO Configure HTTP Bearer authorization: bearer
+// Case 1. Use String Token
+//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
+// Case 2. Use Function which generate token.
+// String yourTokenGeneratorFunction() { ... }
+//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
+
+final api_instance = DeviceInfoApi();
+final upsertDeviceInfoDto = UpsertDeviceInfoDto(); // UpsertDeviceInfoDto | 
+
+try {
+    final result = api_instance.upsertDeviceInfo(upsertDeviceInfoDto);
+    print(result);
+} catch (e) {
+    print('Exception when calling DeviceInfoApi->upsertDeviceInfo: $e\n');
+}
+```
+
+### Parameters
+
+Name | Type | Description  | Notes
+------------- | ------------- | ------------- | -------------
+ **upsertDeviceInfoDto** | [**UpsertDeviceInfoDto**](UpsertDeviceInfoDto.md)|  | 
 
 ### Return type
 

+ 0 - 17
mobile/openapi/doc/UpdateDeviceInfoDto.md

@@ -1,17 +0,0 @@
-# openapi.model.UpdateDeviceInfoDto
-
-## Load the model package
-```dart
-import 'package:openapi/api.dart';
-```
-
-## Properties
-Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
-**deviceType** | [**DeviceTypeEnum**](DeviceTypeEnum.md) |  | 
-**deviceId** | **String** |  | 
-**isAutoBackup** | **bool** |  | [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)
-
-

+ 1 - 1
mobile/openapi/doc/CreateDeviceInfoDto.md → mobile/openapi/doc/UpsertDeviceInfoDto.md

@@ -1,4 +1,4 @@
-# openapi.model.CreateDeviceInfoDto
+# openapi.model.UpsertDeviceInfoDto
 
 ## Load the model package
 ```dart

+ 1 - 2
mobile/openapi/lib/api.dart

@@ -56,7 +56,6 @@ part 'model/check_duplicate_asset_response_dto.dart';
 part 'model/check_existing_assets_dto.dart';
 part 'model/check_existing_assets_response_dto.dart';
 part 'model/create_album_dto.dart';
-part 'model/create_device_info_dto.dart';
 part 'model/create_profile_image_response_dto.dart';
 part 'model/create_tag_dto.dart';
 part 'model/create_user_dto.dart';
@@ -98,9 +97,9 @@ part 'model/thumbnail_format.dart';
 part 'model/time_group_enum.dart';
 part 'model/update_album_dto.dart';
 part 'model/update_asset_dto.dart';
-part 'model/update_device_info_dto.dart';
 part 'model/update_tag_dto.dart';
 part 'model/update_user_dto.dart';
+part 'model/upsert_device_info_dto.dart';
 part 'model/usage_by_user_dto.dart';
 part 'model/user_count_response_dto.dart';
 part 'model/user_response_dto.dart';

+ 79 - 14
mobile/openapi/lib/api/device_info_api.dart

@@ -16,16 +16,21 @@ class DeviceInfoApi {
 
   final ApiClient apiClient;
 
-  /// Performs an HTTP 'POST /device-info' operation and returns the [Response].
+  /// 
+  ///
+  /// @deprecated
+  ///
+  /// Note: This method returns the HTTP [Response].
+  ///
   /// Parameters:
   ///
-  /// * [CreateDeviceInfoDto] createDeviceInfoDto (required):
-  Future<Response> createDeviceInfoWithHttpInfo(CreateDeviceInfoDto createDeviceInfoDto,) async {
+  /// * [UpsertDeviceInfoDto] upsertDeviceInfoDto (required):
+  Future<Response> createDeviceInfoWithHttpInfo(UpsertDeviceInfoDto upsertDeviceInfoDto,) async {
     // ignore: prefer_const_declarations
     final path = r'/device-info';
 
     // ignore: prefer_final_locals
-    Object? postBody = createDeviceInfoDto;
+    Object? postBody = upsertDeviceInfoDto;
 
     final queryParams = <QueryParam>[];
     final headerParams = <String, String>{};
@@ -45,11 +50,15 @@ class DeviceInfoApi {
     );
   }
 
+  /// 
+  ///
+  /// @deprecated
+  ///
   /// Parameters:
   ///
-  /// * [CreateDeviceInfoDto] createDeviceInfoDto (required):
-  Future<DeviceInfoResponseDto?> createDeviceInfo(CreateDeviceInfoDto createDeviceInfoDto,) async {
-    final response = await createDeviceInfoWithHttpInfo(createDeviceInfoDto,);
+  /// * [UpsertDeviceInfoDto] upsertDeviceInfoDto (required):
+  Future<DeviceInfoResponseDto?> createDeviceInfo(UpsertDeviceInfoDto upsertDeviceInfoDto,) async {
+    final response = await createDeviceInfoWithHttpInfo(upsertDeviceInfoDto,);
     if (response.statusCode >= HttpStatus.badRequest) {
       throw ApiException(response.statusCode, await _decodeBodyBytes(response));
     }
@@ -63,16 +72,21 @@ class DeviceInfoApi {
     return null;
   }
 
-  /// Performs an HTTP 'PATCH /device-info' operation and returns the [Response].
+  /// 
+  ///
+  /// @deprecated
+  ///
+  /// Note: This method returns the HTTP [Response].
+  ///
   /// Parameters:
   ///
-  /// * [UpdateDeviceInfoDto] updateDeviceInfoDto (required):
-  Future<Response> updateDeviceInfoWithHttpInfo(UpdateDeviceInfoDto updateDeviceInfoDto,) async {
+  /// * [UpsertDeviceInfoDto] upsertDeviceInfoDto (required):
+  Future<Response> updateDeviceInfoWithHttpInfo(UpsertDeviceInfoDto upsertDeviceInfoDto,) async {
     // ignore: prefer_const_declarations
     final path = r'/device-info';
 
     // ignore: prefer_final_locals
-    Object? postBody = updateDeviceInfoDto;
+    Object? postBody = upsertDeviceInfoDto;
 
     final queryParams = <QueryParam>[];
     final headerParams = <String, String>{};
@@ -92,11 +106,62 @@ class DeviceInfoApi {
     );
   }
 
+  /// 
+  ///
+  /// @deprecated
+  ///
+  /// Parameters:
+  ///
+  /// * [UpsertDeviceInfoDto] upsertDeviceInfoDto (required):
+  Future<DeviceInfoResponseDto?> updateDeviceInfo(UpsertDeviceInfoDto upsertDeviceInfoDto,) async {
+    final response = await updateDeviceInfoWithHttpInfo(upsertDeviceInfoDto,);
+    if (response.statusCode >= HttpStatus.badRequest) {
+      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
+    }
+    // When a remote server returns no body with a status of 204, we shall not decode it.
+    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
+    // FormatException when trying to decode an empty string.
+    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
+      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'DeviceInfoResponseDto',) as DeviceInfoResponseDto;
+    
+    }
+    return null;
+  }
+
+  /// Performs an HTTP 'PUT /device-info' operation and returns the [Response].
+  /// Parameters:
+  ///
+  /// * [UpsertDeviceInfoDto] upsertDeviceInfoDto (required):
+  Future<Response> upsertDeviceInfoWithHttpInfo(UpsertDeviceInfoDto upsertDeviceInfoDto,) async {
+    // ignore: prefer_const_declarations
+    final path = r'/device-info';
+
+    // ignore: prefer_final_locals
+    Object? postBody = upsertDeviceInfoDto;
+
+    final queryParams = <QueryParam>[];
+    final headerParams = <String, String>{};
+    final formParams = <String, String>{};
+
+    const contentTypes = <String>['application/json'];
+
+
+    return apiClient.invokeAPI(
+      path,
+      'PUT',
+      queryParams,
+      postBody,
+      headerParams,
+      formParams,
+      contentTypes.isEmpty ? null : contentTypes.first,
+    );
+  }
+
   /// Parameters:
   ///
-  /// * [UpdateDeviceInfoDto] updateDeviceInfoDto (required):
-  Future<DeviceInfoResponseDto?> updateDeviceInfo(UpdateDeviceInfoDto updateDeviceInfoDto,) async {
-    final response = await updateDeviceInfoWithHttpInfo(updateDeviceInfoDto,);
+  /// * [UpsertDeviceInfoDto] upsertDeviceInfoDto (required):
+  Future<DeviceInfoResponseDto?> upsertDeviceInfo(UpsertDeviceInfoDto upsertDeviceInfoDto,) async {
+    final response = await upsertDeviceInfoWithHttpInfo(upsertDeviceInfoDto,);
     if (response.statusCode >= HttpStatus.badRequest) {
       throw ApiException(response.statusCode, await _decodeBodyBytes(response));
     }

+ 2 - 4
mobile/openapi/lib/api_client.dart

@@ -228,8 +228,6 @@ class ApiClient {
           return CheckExistingAssetsResponseDto.fromJson(value);
         case 'CreateAlbumDto':
           return CreateAlbumDto.fromJson(value);
-        case 'CreateDeviceInfoDto':
-          return CreateDeviceInfoDto.fromJson(value);
         case 'CreateProfileImageResponseDto':
           return CreateProfileImageResponseDto.fromJson(value);
         case 'CreateTagDto':
@@ -312,12 +310,12 @@ class ApiClient {
           return UpdateAlbumDto.fromJson(value);
         case 'UpdateAssetDto':
           return UpdateAssetDto.fromJson(value);
-        case 'UpdateDeviceInfoDto':
-          return UpdateDeviceInfoDto.fromJson(value);
         case 'UpdateTagDto':
           return UpdateTagDto.fromJson(value);
         case 'UpdateUserDto':
           return UpdateUserDto.fromJson(value);
+        case 'UpsertDeviceInfoDto':
+          return UpsertDeviceInfoDto.fromJson(value);
         case 'UsageByUserDto':
           return UsageByUserDto.fromJson(value);
         case 'UserCountResponseDto':

+ 0 - 136
mobile/openapi/lib/model/create_device_info_dto.dart

@@ -1,136 +0,0 @@
-//
-// AUTO-GENERATED FILE, DO NOT MODIFY!
-//
-// @dart=2.12
-
-// ignore_for_file: unused_element, unused_import
-// ignore_for_file: always_put_required_named_parameters_first
-// ignore_for_file: constant_identifier_names
-// ignore_for_file: lines_longer_than_80_chars
-
-part of openapi.api;
-
-class CreateDeviceInfoDto {
-  /// Returns a new [CreateDeviceInfoDto] instance.
-  CreateDeviceInfoDto({
-    required this.deviceType,
-    required this.deviceId,
-    this.isAutoBackup,
-  });
-
-  DeviceTypeEnum deviceType;
-
-  String deviceId;
-
-  ///
-  /// 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.
-  ///
-  bool? isAutoBackup;
-
-  @override
-  bool operator ==(Object other) => identical(this, other) || other is CreateDeviceInfoDto &&
-     other.deviceType == deviceType &&
-     other.deviceId == deviceId &&
-     other.isAutoBackup == isAutoBackup;
-
-  @override
-  int get hashCode =>
-    // ignore: unnecessary_parenthesis
-    (deviceType.hashCode) +
-    (deviceId.hashCode) +
-    (isAutoBackup == null ? 0 : isAutoBackup!.hashCode);
-
-  @override
-  String toString() => 'CreateDeviceInfoDto[deviceType=$deviceType, deviceId=$deviceId, isAutoBackup=$isAutoBackup]';
-
-  Map<String, dynamic> toJson() {
-    final _json = <String, dynamic>{};
-      _json[r'deviceType'] = deviceType;
-      _json[r'deviceId'] = deviceId;
-    if (isAutoBackup != null) {
-      _json[r'isAutoBackup'] = isAutoBackup;
-    } else {
-      _json[r'isAutoBackup'] = null;
-    }
-    return _json;
-  }
-
-  /// Returns a new [CreateDeviceInfoDto] instance and imports its values from
-  /// [value] if it's a [Map], null otherwise.
-  // ignore: prefer_constructors_over_static_methods
-  static CreateDeviceInfoDto? fromJson(dynamic value) {
-    if (value is Map) {
-      final json = value.cast<String, dynamic>();
-
-      // 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 "CreateDeviceInfoDto[$key]" is missing from JSON.');
-          assert(json[key] != null, 'Required key "CreateDeviceInfoDto[$key]" has a null value in JSON.');
-        });
-        return true;
-      }());
-
-      return CreateDeviceInfoDto(
-        deviceType: DeviceTypeEnum.fromJson(json[r'deviceType'])!,
-        deviceId: mapValueOfType<String>(json, r'deviceId')!,
-        isAutoBackup: mapValueOfType<bool>(json, r'isAutoBackup'),
-      );
-    }
-    return null;
-  }
-
-  static List<CreateDeviceInfoDto>? listFromJson(dynamic json, {bool growable = false,}) {
-    final result = <CreateDeviceInfoDto>[];
-    if (json is List && json.isNotEmpty) {
-      for (final row in json) {
-        final value = CreateDeviceInfoDto.fromJson(row);
-        if (value != null) {
-          result.add(value);
-        }
-      }
-    }
-    return result.toList(growable: growable);
-  }
-
-  static Map<String, CreateDeviceInfoDto> mapFromJson(dynamic json) {
-    final map = <String, CreateDeviceInfoDto>{};
-    if (json is Map && json.isNotEmpty) {
-      json = json.cast<String, dynamic>(); // ignore: parameter_assignments
-      for (final entry in json.entries) {
-        final value = CreateDeviceInfoDto.fromJson(entry.value);
-        if (value != null) {
-          map[entry.key] = value;
-        }
-      }
-    }
-    return map;
-  }
-
-  // maps a json object with a list of CreateDeviceInfoDto-objects as value to a dart map
-  static Map<String, List<CreateDeviceInfoDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
-    final map = <String, List<CreateDeviceInfoDto>>{};
-    if (json is Map && json.isNotEmpty) {
-      json = json.cast<String, dynamic>(); // ignore: parameter_assignments
-      for (final entry in json.entries) {
-        final value = CreateDeviceInfoDto.listFromJson(entry.value, growable: growable,);
-        if (value != null) {
-          map[entry.key] = value;
-        }
-      }
-    }
-    return map;
-  }
-
-  /// The list of required keys that must be present in a JSON.
-  static const requiredKeys = <String>{
-    'deviceType',
-    'deviceId',
-  };
-}
-

+ 20 - 20
mobile/openapi/lib/model/update_device_info_dto.dart → mobile/openapi/lib/model/upsert_device_info_dto.dart

@@ -10,9 +10,9 @@
 
 part of openapi.api;
 
-class UpdateDeviceInfoDto {
-  /// Returns a new [UpdateDeviceInfoDto] instance.
-  UpdateDeviceInfoDto({
+class UpsertDeviceInfoDto {
+  /// Returns a new [UpsertDeviceInfoDto] instance.
+  UpsertDeviceInfoDto({
     required this.deviceType,
     required this.deviceId,
     this.isAutoBackup,
@@ -31,7 +31,7 @@ class UpdateDeviceInfoDto {
   bool? isAutoBackup;
 
   @override
-  bool operator ==(Object other) => identical(this, other) || other is UpdateDeviceInfoDto &&
+  bool operator ==(Object other) => identical(this, other) || other is UpsertDeviceInfoDto &&
      other.deviceType == deviceType &&
      other.deviceId == deviceId &&
      other.isAutoBackup == isAutoBackup;
@@ -44,7 +44,7 @@ class UpdateDeviceInfoDto {
     (isAutoBackup == null ? 0 : isAutoBackup!.hashCode);
 
   @override
-  String toString() => 'UpdateDeviceInfoDto[deviceType=$deviceType, deviceId=$deviceId, isAutoBackup=$isAutoBackup]';
+  String toString() => 'UpsertDeviceInfoDto[deviceType=$deviceType, deviceId=$deviceId, isAutoBackup=$isAutoBackup]';
 
   Map<String, dynamic> toJson() {
     final _json = <String, dynamic>{};
@@ -58,10 +58,10 @@ class UpdateDeviceInfoDto {
     return _json;
   }
 
-  /// Returns a new [UpdateDeviceInfoDto] instance and imports its values from
+  /// Returns a new [UpsertDeviceInfoDto] instance and imports its values from
   /// [value] if it's a [Map], null otherwise.
   // ignore: prefer_constructors_over_static_methods
-  static UpdateDeviceInfoDto? fromJson(dynamic value) {
+  static UpsertDeviceInfoDto? fromJson(dynamic value) {
     if (value is Map) {
       final json = value.cast<String, dynamic>();
 
@@ -70,13 +70,13 @@ class UpdateDeviceInfoDto {
       // Note 2: this code is stripped in release mode!
       assert(() {
         requiredKeys.forEach((key) {
-          assert(json.containsKey(key), 'Required key "UpdateDeviceInfoDto[$key]" is missing from JSON.');
-          assert(json[key] != null, 'Required key "UpdateDeviceInfoDto[$key]" has a null value in JSON.');
+          assert(json.containsKey(key), 'Required key "UpsertDeviceInfoDto[$key]" is missing from JSON.');
+          assert(json[key] != null, 'Required key "UpsertDeviceInfoDto[$key]" has a null value in JSON.');
         });
         return true;
       }());
 
-      return UpdateDeviceInfoDto(
+      return UpsertDeviceInfoDto(
         deviceType: DeviceTypeEnum.fromJson(json[r'deviceType'])!,
         deviceId: mapValueOfType<String>(json, r'deviceId')!,
         isAutoBackup: mapValueOfType<bool>(json, r'isAutoBackup'),
@@ -85,11 +85,11 @@ class UpdateDeviceInfoDto {
     return null;
   }
 
-  static List<UpdateDeviceInfoDto>? listFromJson(dynamic json, {bool growable = false,}) {
-    final result = <UpdateDeviceInfoDto>[];
+  static List<UpsertDeviceInfoDto>? listFromJson(dynamic json, {bool growable = false,}) {
+    final result = <UpsertDeviceInfoDto>[];
     if (json is List && json.isNotEmpty) {
       for (final row in json) {
-        final value = UpdateDeviceInfoDto.fromJson(row);
+        final value = UpsertDeviceInfoDto.fromJson(row);
         if (value != null) {
           result.add(value);
         }
@@ -98,12 +98,12 @@ class UpdateDeviceInfoDto {
     return result.toList(growable: growable);
   }
 
-  static Map<String, UpdateDeviceInfoDto> mapFromJson(dynamic json) {
-    final map = <String, UpdateDeviceInfoDto>{};
+  static Map<String, UpsertDeviceInfoDto> mapFromJson(dynamic json) {
+    final map = <String, UpsertDeviceInfoDto>{};
     if (json is Map && json.isNotEmpty) {
       json = json.cast<String, dynamic>(); // ignore: parameter_assignments
       for (final entry in json.entries) {
-        final value = UpdateDeviceInfoDto.fromJson(entry.value);
+        final value = UpsertDeviceInfoDto.fromJson(entry.value);
         if (value != null) {
           map[entry.key] = value;
         }
@@ -112,13 +112,13 @@ class UpdateDeviceInfoDto {
     return map;
   }
 
-  // maps a json object with a list of UpdateDeviceInfoDto-objects as value to a dart map
-  static Map<String, List<UpdateDeviceInfoDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
-    final map = <String, List<UpdateDeviceInfoDto>>{};
+  // maps a json object with a list of UpsertDeviceInfoDto-objects as value to a dart map
+  static Map<String, List<UpsertDeviceInfoDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
+    final map = <String, List<UpsertDeviceInfoDto>>{};
     if (json is Map && json.isNotEmpty) {
       json = json.cast<String, dynamic>(); // ignore: parameter_assignments
       for (final entry in json.entries) {
-        final value = UpdateDeviceInfoDto.listFromJson(entry.value, growable: growable,);
+        final value = UpsertDeviceInfoDto.listFromJson(entry.value, growable: growable,);
         if (value != null) {
           map[entry.key] = value;
         }

+ 15 - 2
mobile/openapi/test/device_info_api_test.dart

@@ -17,15 +17,28 @@ void main() {
   // final instance = DeviceInfoApi();
 
   group('tests for DeviceInfoApi', () {
-    //Future<DeviceInfoResponseDto> createDeviceInfo(CreateDeviceInfoDto createDeviceInfoDto) async
+    // 
+    //
+    // @deprecated
+    //
+    //Future<DeviceInfoResponseDto> createDeviceInfo(UpsertDeviceInfoDto upsertDeviceInfoDto) async
     test('test createDeviceInfo', () async {
       // TODO
     });
 
-    //Future<DeviceInfoResponseDto> updateDeviceInfo(UpdateDeviceInfoDto updateDeviceInfoDto) async
+    // 
+    //
+    // @deprecated
+    //
+    //Future<DeviceInfoResponseDto> updateDeviceInfo(UpsertDeviceInfoDto upsertDeviceInfoDto) async
     test('test updateDeviceInfo', () async {
       // TODO
     });
 
+    //Future<DeviceInfoResponseDto> upsertDeviceInfo(UpsertDeviceInfoDto upsertDeviceInfoDto) async
+    test('test upsertDeviceInfo', () async {
+      // TODO
+    });
+
   });
 }

+ 0 - 37
mobile/openapi/test/update_device_info_dto_test.dart

@@ -1,37 +0,0 @@
-//
-// AUTO-GENERATED FILE, DO NOT MODIFY!
-//
-// @dart=2.12
-
-// ignore_for_file: unused_element, unused_import
-// ignore_for_file: always_put_required_named_parameters_first
-// ignore_for_file: constant_identifier_names
-// ignore_for_file: lines_longer_than_80_chars
-
-import 'package:openapi/api.dart';
-import 'package:test/test.dart';
-
-// tests for UpdateDeviceInfoDto
-void main() {
-  // final instance = UpdateDeviceInfoDto();
-
-  group('test UpdateDeviceInfoDto', () {
-    // DeviceTypeEnum deviceType
-    test('to test the property `deviceType`', () async {
-      // TODO
-    });
-
-    // String deviceId
-    test('to test the property `deviceId`', () async {
-      // TODO
-    });
-
-    // bool isAutoBackup
-    test('to test the property `isAutoBackup`', () async {
-      // TODO
-    });
-
-
-  });
-
-}

+ 3 - 3
mobile/openapi/test/create_device_info_dto_test.dart → mobile/openapi/test/upsert_device_info_dto_test.dart

@@ -11,11 +11,11 @@
 import 'package:openapi/api.dart';
 import 'package:test/test.dart';
 
-// tests for CreateDeviceInfoDto
+// tests for UpsertDeviceInfoDto
 void main() {
-  // final instance = CreateDeviceInfoDto();
+  // final instance = UpsertDeviceInfoDto();
 
-  group('test CreateDeviceInfoDto', () {
+  group('test UpsertDeviceInfoDto', () {
     // DeviceTypeEnum deviceType
     test('to test the property `deviceType`', () async {
       // TODO

+ 22 - 12
server/apps/immich/src/api-v1/device-info/device-info.controller.ts

@@ -1,11 +1,10 @@
-import { Controller, Post, Body, Patch, ValidationPipe } from '@nestjs/common';
+import { Body, Controller, Patch, Post, Put, ValidationPipe } from '@nestjs/common';
 import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
 import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
 import { Authenticated } from '../../decorators/authenticated.decorator';
 import { DeviceInfoService } from './device-info.service';
-import { CreateDeviceInfoDto } from './dto/create-device-info.dto';
-import { UpdateDeviceInfoDto } from './dto/update-device-info.dto';
-import { DeviceInfoResponseDto } from './response-dto/create-device-info-response.dto';
+import { UpsertDeviceInfoDto } from './dto/upsert-device-info.dto';
+import { DeviceInfoResponseDto, mapDeviceInfoResponse } from './response-dto/device-info-response.dto';
 
 @Authenticated()
 @ApiBearerAuth()
@@ -14,19 +13,30 @@ import { DeviceInfoResponseDto } from './response-dto/create-device-info-respons
 export class DeviceInfoController {
   constructor(private readonly deviceInfoService: DeviceInfoService) {}
 
+  /** @deprecated */
   @Post()
-  async createDeviceInfo(
-    @Body(ValidationPipe) createDeviceInfoDto: CreateDeviceInfoDto,
-    @GetAuthUser() authUser: AuthUserDto,
+  public async createDeviceInfo(
+    @GetAuthUser() user: AuthUserDto,
+    @Body(ValidationPipe) dto: UpsertDeviceInfoDto,
   ): Promise<DeviceInfoResponseDto> {
-    return this.deviceInfoService.create(createDeviceInfoDto, authUser);
+    return this.upsertDeviceInfo(user, dto);
   }
 
+  /** @deprecated */
   @Patch()
-  async updateDeviceInfo(
-    @Body(ValidationPipe) updateDeviceInfoDto: UpdateDeviceInfoDto,
-    @GetAuthUser() authUser: AuthUserDto,
+  public async updateDeviceInfo(
+    @GetAuthUser() user: AuthUserDto,
+    @Body(ValidationPipe) dto: UpsertDeviceInfoDto,
   ): Promise<DeviceInfoResponseDto> {
-    return this.deviceInfoService.update(authUser.id, updateDeviceInfoDto);
+    return this.upsertDeviceInfo(user, dto);
+  }
+
+  @Put()
+  public async upsertDeviceInfo(
+    @GetAuthUser() user: AuthUserDto,
+    @Body(ValidationPipe) dto: UpsertDeviceInfoDto,
+  ): Promise<DeviceInfoResponseDto> {
+    const deviceInfo = await this.deviceInfoService.upsert({ ...dto, userId: user.id });
+    return mapDeviceInfoResponse(deviceInfo);
   }
 }

+ 65 - 0
server/apps/immich/src/api-v1/device-info/device-info.service.spec.ts

@@ -0,0 +1,65 @@
+import { DeviceInfoEntity, DeviceType } from '@app/database/entities/device-info.entity';
+import { Repository } from 'typeorm';
+import { DeviceInfoService } from './device-info.service';
+
+const deviceId = 'device-123';
+const userId = 'user-123';
+
+describe('DeviceInfoService', () => {
+  let sut: DeviceInfoService;
+  let repositoryMock: jest.Mocked<Repository<DeviceInfoEntity>>;
+
+  beforeEach(async () => {
+    repositoryMock = {
+      findOne: jest.fn(),
+      save: jest.fn(),
+    } as unknown as jest.Mocked<Repository<DeviceInfoEntity>>;
+
+    sut = new DeviceInfoService(repositoryMock);
+  });
+
+  it('should be defined', () => {
+    expect(sut).toBeDefined();
+  });
+
+  describe('upsert', () => {
+    it('should create a new record', async () => {
+      const request = { deviceId, userId, deviceType: DeviceType.IOS } as DeviceInfoEntity;
+      const response = { ...request, id: 1 } as DeviceInfoEntity;
+
+      repositoryMock.findOne.mockResolvedValue(null);
+      repositoryMock.save.mockResolvedValue(response);
+
+      await expect(sut.upsert(request)).resolves.toEqual(response);
+
+      expect(repositoryMock.findOne).toHaveBeenCalledTimes(1);
+      expect(repositoryMock.save).toHaveBeenCalledTimes(1);
+    });
+
+    it('should update an existing record', async () => {
+      const request = { deviceId, userId, deviceType: DeviceType.IOS, isAutoBackup: true } as DeviceInfoEntity;
+      const response = { ...request, id: 1 } as DeviceInfoEntity;
+
+      repositoryMock.findOne.mockResolvedValue(response);
+      repositoryMock.save.mockResolvedValue(response);
+
+      await expect(sut.upsert(request)).resolves.toEqual(response);
+
+      expect(repositoryMock.findOne).toHaveBeenCalledTimes(1);
+      expect(repositoryMock.save).toHaveBeenCalledTimes(1);
+    });
+
+    it('should keep properties that were not updated', async () => {
+      const request = { deviceId, userId } as DeviceInfoEntity;
+      const response = { id: 1, isAutoBackup: true, deviceId, userId, deviceType: DeviceType.WEB } as DeviceInfoEntity;
+
+      repositoryMock.findOne.mockResolvedValue(response);
+      repositoryMock.save.mockResolvedValue(response);
+
+      await expect(sut.upsert(request)).resolves.toEqual(response);
+
+      expect(repositoryMock.findOne).toHaveBeenCalledTimes(1);
+      expect(repositoryMock.save).toHaveBeenCalledTimes(1);
+    });
+  });
+});

+ 14 - 55
server/apps/immich/src/api-v1/device-info/device-info.service.ts

@@ -1,70 +1,29 @@
-import { BadRequestException, Injectable, Logger, NotFoundException } from '@nestjs/common';
+import { DeviceInfoEntity } from '@app/database/entities/device-info.entity';
+import { Injectable } from '@nestjs/common';
 import { InjectRepository } from '@nestjs/typeorm';
 import { Repository } from 'typeorm';
-import { AuthUserDto } from '../../decorators/auth-user.decorator';
-import { CreateDeviceInfoDto } from './dto/create-device-info.dto';
-import { UpdateDeviceInfoDto } from './dto/update-device-info.dto';
-import { DeviceInfoEntity } from '@app/database/entities/device-info.entity';
-import { DeviceInfoResponseDto, mapDeviceInfoResponse } from './response-dto/create-device-info-response.dto';
+
+type EntityKeys = Pick<DeviceInfoEntity, 'deviceId' | 'userId'>;
+type Entity = EntityKeys & Partial<DeviceInfoEntity>;
 
 @Injectable()
 export class DeviceInfoService {
   constructor(
     @InjectRepository(DeviceInfoEntity)
-    private deviceRepository: Repository<DeviceInfoEntity>,
+    private repository: Repository<DeviceInfoEntity>,
   ) {}
 
-  async create(createDeviceInfoDto: CreateDeviceInfoDto, authUser: AuthUserDto): Promise<DeviceInfoResponseDto> {
-    const res = await this.deviceRepository.findOne({
-      where: {
-        deviceId: createDeviceInfoDto.deviceId,
-        userId: authUser.id,
-      },
-    });
+  public async upsert(entity: Entity): Promise<DeviceInfoEntity> {
+    const { deviceId, userId } = entity;
+    const exists = await this.repository.findOne({ where: { userId, deviceId } });
 
-    if (res) {
-      Logger.log('Device Info Exist', 'createDeviceInfo');
-      return mapDeviceInfoResponse(res);
+    if (!exists) {
+      return await this.repository.save(entity);
     }
 
-    const deviceInfo = new DeviceInfoEntity();
-    deviceInfo.deviceId = createDeviceInfoDto.deviceId;
-    deviceInfo.deviceType = createDeviceInfoDto.deviceType;
-    deviceInfo.userId = authUser.id;
-
-    const newDeviceInfo = await this.deviceRepository.save(deviceInfo);
-
-    return mapDeviceInfoResponse(newDeviceInfo);
-  }
-
-  async update(userId: string, updateDeviceInfoDto: UpdateDeviceInfoDto): Promise<DeviceInfoResponseDto> {
-    const deviceInfo = await this.deviceRepository.findOne({
-      where: { deviceId: updateDeviceInfoDto.deviceId, userId: userId },
-    });
+    exists.isAutoBackup = entity.isAutoBackup ?? exists.isAutoBackup;
+    exists.deviceType = entity.deviceType ?? exists.deviceType;
 
-    if (!deviceInfo) {
-      throw new NotFoundException('Device Not Found');
-    }
-
-    const res = await this.deviceRepository.update(
-      {
-        id: deviceInfo.id,
-      },
-      updateDeviceInfoDto,
-    );
-
-    if (res.affected == 1) {
-      const updatedDeviceInfo = await this.deviceRepository.findOne({
-        where: { deviceId: updateDeviceInfoDto.deviceId, userId: userId },
-      });
-
-      if (!updatedDeviceInfo) {
-        throw new NotFoundException('Device Not Found');
-      }
-
-      return mapDeviceInfoResponse(updatedDeviceInfo);
-    } else {
-      throw new BadRequestException('Bad Request');
-    }
+    return await this.repository.save(exists);
   }
 }

+ 0 - 15
server/apps/immich/src/api-v1/device-info/dto/update-device-info.dto.ts

@@ -1,15 +0,0 @@
-import { DeviceType } from '@app/database/entities/device-info.entity';
-import { ApiProperty } from '@nestjs/swagger';
-import { IsNotEmpty, IsOptional } from 'class-validator';
-
-export class UpdateDeviceInfoDto {
-  @IsNotEmpty()
-  deviceId!: string;
-
-  @IsNotEmpty()
-  @ApiProperty({ enumName: 'DeviceTypeEnum', enum: DeviceType })
-  deviceType!: DeviceType;
-
-  @IsOptional()
-  isAutoBackup?: boolean;
-}

+ 1 - 1
server/apps/immich/src/api-v1/device-info/dto/create-device-info.dto.ts → server/apps/immich/src/api-v1/device-info/dto/upsert-device-info.dto.ts

@@ -2,7 +2,7 @@ import { IsNotEmpty, IsOptional } from 'class-validator';
 import { DeviceType } from '@app/database/entities/device-info.entity';
 import { ApiProperty } from '@nestjs/swagger';
 
-export class CreateDeviceInfoDto {
+export class UpsertDeviceInfoDto {
   @IsNotEmpty()
   deviceId!: string;
 

+ 0 - 0
server/apps/immich/src/api-v1/device-info/response-dto/create-device-info-response.dto.ts → server/apps/immich/src/api-v1/device-info/response-dto/device-info-response.dto.ts


+ 41 - 21
server/immich-openapi-specs.json

@@ -1783,13 +1783,15 @@
     "/device-info": {
       "post": {
         "operationId": "createDeviceInfo",
+        "summary": "",
+        "description": "@deprecated",
         "parameters": [],
         "requestBody": {
           "required": true,
           "content": {
             "application/json": {
               "schema": {
-                "$ref": "#/components/schemas/CreateDeviceInfoDto"
+                "$ref": "#/components/schemas/UpsertDeviceInfoDto"
               }
             }
           }
@@ -1817,13 +1819,49 @@
       },
       "patch": {
         "operationId": "updateDeviceInfo",
+        "summary": "",
+        "description": "@deprecated",
         "parameters": [],
         "requestBody": {
           "required": true,
           "content": {
             "application/json": {
               "schema": {
-                "$ref": "#/components/schemas/UpdateDeviceInfoDto"
+                "$ref": "#/components/schemas/UpsertDeviceInfoDto"
+              }
+            }
+          }
+        },
+        "responses": {
+          "200": {
+            "description": "",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/DeviceInfoResponseDto"
+                }
+              }
+            }
+          }
+        },
+        "tags": [
+          "Device Info"
+        ],
+        "security": [
+          {
+            "bearer": []
+          }
+        ]
+      },
+      "put": {
+        "operationId": "upsertDeviceInfo",
+        "parameters": [],
+        "requestBody": {
+          "required": true,
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/UpsertDeviceInfoDto"
               }
             }
           }
@@ -3227,7 +3265,7 @@
           "WEB"
         ]
       },
-      "CreateDeviceInfoDto": {
+      "UpsertDeviceInfoDto": {
         "type": "object",
         "properties": {
           "deviceType": {
@@ -3276,24 +3314,6 @@
           "isAutoBackup"
         ]
       },
-      "UpdateDeviceInfoDto": {
-        "type": "object",
-        "properties": {
-          "deviceType": {
-            "$ref": "#/components/schemas/DeviceTypeEnum"
-          },
-          "deviceId": {
-            "type": "string"
-          },
-          "isAutoBackup": {
-            "type": "boolean"
-          }
-        },
-        "required": [
-          "deviceType",
-          "deviceId"
-        ]
-      },
       "ServerInfoResponseDto": {
         "type": "object",
         "properties": {

+ 135 - 83
web/src/api/open-api/api.ts

@@ -564,31 +564,6 @@ export interface CreateAlbumDto {
      */
     'assetIds'?: Array<string>;
 }
-/**
- * 
- * @export
- * @interface CreateDeviceInfoDto
- */
-export interface CreateDeviceInfoDto {
-    /**
-     * 
-     * @type {DeviceTypeEnum}
-     * @memberof CreateDeviceInfoDto
-     */
-    'deviceType': DeviceTypeEnum;
-    /**
-     * 
-     * @type {string}
-     * @memberof CreateDeviceInfoDto
-     */
-    'deviceId': string;
-    /**
-     * 
-     * @type {boolean}
-     * @memberof CreateDeviceInfoDto
-     */
-    'isAutoBackup'?: boolean;
-}
 /**
  * 
  * @export
@@ -1629,31 +1604,6 @@ export interface UpdateAssetDto {
      */
     'isFavorite'?: boolean;
 }
-/**
- * 
- * @export
- * @interface UpdateDeviceInfoDto
- */
-export interface UpdateDeviceInfoDto {
-    /**
-     * 
-     * @type {DeviceTypeEnum}
-     * @memberof UpdateDeviceInfoDto
-     */
-    'deviceType': DeviceTypeEnum;
-    /**
-     * 
-     * @type {string}
-     * @memberof UpdateDeviceInfoDto
-     */
-    'deviceId': string;
-    /**
-     * 
-     * @type {boolean}
-     * @memberof UpdateDeviceInfoDto
-     */
-    'isAutoBackup'?: boolean;
-}
 /**
  * 
  * @export
@@ -1722,6 +1672,31 @@ export interface UpdateUserDto {
      */
     'profileImagePath'?: string;
 }
+/**
+ * 
+ * @export
+ * @interface UpsertDeviceInfoDto
+ */
+export interface UpsertDeviceInfoDto {
+    /**
+     * 
+     * @type {DeviceTypeEnum}
+     * @memberof UpsertDeviceInfoDto
+     */
+    'deviceType': DeviceTypeEnum;
+    /**
+     * 
+     * @type {string}
+     * @memberof UpsertDeviceInfoDto
+     */
+    'deviceId': string;
+    /**
+     * 
+     * @type {boolean}
+     * @memberof UpsertDeviceInfoDto
+     */
+    'isAutoBackup'?: boolean;
+}
 /**
  * 
  * @export
@@ -4321,14 +4296,15 @@ export class AuthenticationApi extends BaseAPI {
 export const DeviceInfoApiAxiosParamCreator = function (configuration?: Configuration) {
     return {
         /**
-         * 
-         * @param {CreateDeviceInfoDto} createDeviceInfoDto 
+         * @deprecated
+         * @summary 
+         * @param {UpsertDeviceInfoDto} upsertDeviceInfoDto 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        createDeviceInfo: async (createDeviceInfoDto: CreateDeviceInfoDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
-            // verify required parameter 'createDeviceInfoDto' is not null or undefined
-            assertParamExists('createDeviceInfo', 'createDeviceInfoDto', createDeviceInfoDto)
+        createDeviceInfo: async (upsertDeviceInfoDto: UpsertDeviceInfoDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            // verify required parameter 'upsertDeviceInfoDto' is not null or undefined
+            assertParamExists('createDeviceInfo', 'upsertDeviceInfoDto', upsertDeviceInfoDto)
             const localVarPath = `/device-info`;
             // use dummy base URL string because the URL constructor only accepts absolute URLs.
             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
@@ -4352,7 +4328,7 @@ export const DeviceInfoApiAxiosParamCreator = function (configuration?: Configur
             setSearchParams(localVarUrlObj, localVarQueryParameter);
             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
-            localVarRequestOptions.data = serializeDataIfNeeded(createDeviceInfoDto, localVarRequestOptions, configuration)
+            localVarRequestOptions.data = serializeDataIfNeeded(upsertDeviceInfoDto, localVarRequestOptions, configuration)
 
             return {
                 url: toPathString(localVarUrlObj),
@@ -4360,14 +4336,15 @@ export const DeviceInfoApiAxiosParamCreator = function (configuration?: Configur
             };
         },
         /**
-         * 
-         * @param {UpdateDeviceInfoDto} updateDeviceInfoDto 
+         * @deprecated
+         * @summary 
+         * @param {UpsertDeviceInfoDto} upsertDeviceInfoDto 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        updateDeviceInfo: async (updateDeviceInfoDto: UpdateDeviceInfoDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
-            // verify required parameter 'updateDeviceInfoDto' is not null or undefined
-            assertParamExists('updateDeviceInfo', 'updateDeviceInfoDto', updateDeviceInfoDto)
+        updateDeviceInfo: async (upsertDeviceInfoDto: UpsertDeviceInfoDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            // verify required parameter 'upsertDeviceInfoDto' is not null or undefined
+            assertParamExists('updateDeviceInfo', 'upsertDeviceInfoDto', upsertDeviceInfoDto)
             const localVarPath = `/device-info`;
             // use dummy base URL string because the URL constructor only accepts absolute URLs.
             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
@@ -4391,7 +4368,46 @@ export const DeviceInfoApiAxiosParamCreator = function (configuration?: Configur
             setSearchParams(localVarUrlObj, localVarQueryParameter);
             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
-            localVarRequestOptions.data = serializeDataIfNeeded(updateDeviceInfoDto, localVarRequestOptions, configuration)
+            localVarRequestOptions.data = serializeDataIfNeeded(upsertDeviceInfoDto, localVarRequestOptions, configuration)
+
+            return {
+                url: toPathString(localVarUrlObj),
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @param {UpsertDeviceInfoDto} upsertDeviceInfoDto 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        upsertDeviceInfo: async (upsertDeviceInfoDto: UpsertDeviceInfoDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            // verify required parameter 'upsertDeviceInfoDto' is not null or undefined
+            assertParamExists('upsertDeviceInfo', 'upsertDeviceInfoDto', upsertDeviceInfoDto)
+            const localVarPath = `/device-info`;
+            // 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 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(upsertDeviceInfoDto, localVarRequestOptions, configuration)
 
             return {
                 url: toPathString(localVarUrlObj),
@@ -4409,23 +4425,35 @@ export const DeviceInfoApiFp = function(configuration?: Configuration) {
     const localVarAxiosParamCreator = DeviceInfoApiAxiosParamCreator(configuration)
     return {
         /**
-         * 
-         * @param {CreateDeviceInfoDto} createDeviceInfoDto 
+         * @deprecated
+         * @summary 
+         * @param {UpsertDeviceInfoDto} upsertDeviceInfoDto 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async createDeviceInfo(upsertDeviceInfoDto: UpsertDeviceInfoDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<DeviceInfoResponseDto>> {
+            const localVarAxiosArgs = await localVarAxiosParamCreator.createDeviceInfo(upsertDeviceInfoDto, options);
+            return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
+        },
+        /**
+         * @deprecated
+         * @summary 
+         * @param {UpsertDeviceInfoDto} upsertDeviceInfoDto 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async createDeviceInfo(createDeviceInfoDto: CreateDeviceInfoDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<DeviceInfoResponseDto>> {
-            const localVarAxiosArgs = await localVarAxiosParamCreator.createDeviceInfo(createDeviceInfoDto, options);
+        async updateDeviceInfo(upsertDeviceInfoDto: UpsertDeviceInfoDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<DeviceInfoResponseDto>> {
+            const localVarAxiosArgs = await localVarAxiosParamCreator.updateDeviceInfo(upsertDeviceInfoDto, options);
             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
         },
         /**
          * 
-         * @param {UpdateDeviceInfoDto} updateDeviceInfoDto 
+         * @param {UpsertDeviceInfoDto} upsertDeviceInfoDto 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async updateDeviceInfo(updateDeviceInfoDto: UpdateDeviceInfoDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<DeviceInfoResponseDto>> {
-            const localVarAxiosArgs = await localVarAxiosParamCreator.updateDeviceInfo(updateDeviceInfoDto, options);
+        async upsertDeviceInfo(upsertDeviceInfoDto: UpsertDeviceInfoDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<DeviceInfoResponseDto>> {
+            const localVarAxiosArgs = await localVarAxiosParamCreator.upsertDeviceInfo(upsertDeviceInfoDto, options);
             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
         },
     }
@@ -4439,22 +4467,33 @@ export const DeviceInfoApiFactory = function (configuration?: Configuration, bas
     const localVarFp = DeviceInfoApiFp(configuration)
     return {
         /**
-         * 
-         * @param {CreateDeviceInfoDto} createDeviceInfoDto 
+         * @deprecated
+         * @summary 
+         * @param {UpsertDeviceInfoDto} upsertDeviceInfoDto 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        createDeviceInfo(createDeviceInfoDto: CreateDeviceInfoDto, options?: any): AxiosPromise<DeviceInfoResponseDto> {
-            return localVarFp.createDeviceInfo(createDeviceInfoDto, options).then((request) => request(axios, basePath));
+        createDeviceInfo(upsertDeviceInfoDto: UpsertDeviceInfoDto, options?: any): AxiosPromise<DeviceInfoResponseDto> {
+            return localVarFp.createDeviceInfo(upsertDeviceInfoDto, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * @deprecated
+         * @summary 
+         * @param {UpsertDeviceInfoDto} upsertDeviceInfoDto 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        updateDeviceInfo(upsertDeviceInfoDto: UpsertDeviceInfoDto, options?: any): AxiosPromise<DeviceInfoResponseDto> {
+            return localVarFp.updateDeviceInfo(upsertDeviceInfoDto, options).then((request) => request(axios, basePath));
         },
         /**
          * 
-         * @param {UpdateDeviceInfoDto} updateDeviceInfoDto 
+         * @param {UpsertDeviceInfoDto} upsertDeviceInfoDto 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        updateDeviceInfo(updateDeviceInfoDto: UpdateDeviceInfoDto, options?: any): AxiosPromise<DeviceInfoResponseDto> {
-            return localVarFp.updateDeviceInfo(updateDeviceInfoDto, options).then((request) => request(axios, basePath));
+        upsertDeviceInfo(upsertDeviceInfoDto: UpsertDeviceInfoDto, options?: any): AxiosPromise<DeviceInfoResponseDto> {
+            return localVarFp.upsertDeviceInfo(upsertDeviceInfoDto, options).then((request) => request(axios, basePath));
         },
     };
 };
@@ -4467,25 +4506,38 @@ export const DeviceInfoApiFactory = function (configuration?: Configuration, bas
  */
 export class DeviceInfoApi extends BaseAPI {
     /**
-     * 
-     * @param {CreateDeviceInfoDto} createDeviceInfoDto 
+     * @deprecated
+     * @summary 
+     * @param {UpsertDeviceInfoDto} upsertDeviceInfoDto 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof DeviceInfoApi
+     */
+    public createDeviceInfo(upsertDeviceInfoDto: UpsertDeviceInfoDto, options?: AxiosRequestConfig) {
+        return DeviceInfoApiFp(this.configuration).createDeviceInfo(upsertDeviceInfoDto, options).then((request) => request(this.axios, this.basePath));
+    }
+
+    /**
+     * @deprecated
+     * @summary 
+     * @param {UpsertDeviceInfoDto} upsertDeviceInfoDto 
      * @param {*} [options] Override http request option.
      * @throws {RequiredError}
      * @memberof DeviceInfoApi
      */
-    public createDeviceInfo(createDeviceInfoDto: CreateDeviceInfoDto, options?: AxiosRequestConfig) {
-        return DeviceInfoApiFp(this.configuration).createDeviceInfo(createDeviceInfoDto, options).then((request) => request(this.axios, this.basePath));
+    public updateDeviceInfo(upsertDeviceInfoDto: UpsertDeviceInfoDto, options?: AxiosRequestConfig) {
+        return DeviceInfoApiFp(this.configuration).updateDeviceInfo(upsertDeviceInfoDto, options).then((request) => request(this.axios, this.basePath));
     }
 
     /**
      * 
-     * @param {UpdateDeviceInfoDto} updateDeviceInfoDto 
+     * @param {UpsertDeviceInfoDto} upsertDeviceInfoDto 
      * @param {*} [options] Override http request option.
      * @throws {RequiredError}
      * @memberof DeviceInfoApi
      */
-    public updateDeviceInfo(updateDeviceInfoDto: UpdateDeviceInfoDto, options?: AxiosRequestConfig) {
-        return DeviceInfoApiFp(this.configuration).updateDeviceInfo(updateDeviceInfoDto, options).then((request) => request(this.axios, this.basePath));
+    public upsertDeviceInfo(upsertDeviceInfoDto: UpsertDeviceInfoDto, options?: AxiosRequestConfig) {
+        return DeviceInfoApiFp(this.configuration).upsertDeviceInfo(upsertDeviceInfoDto, options).then((request) => request(this.axios, this.basePath));
     }
 }