mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	feat(server/web): jobs clear button + queue status (#2144)
* feat(server/web): jobs clear button + queue status * adjust design and colors * Adjust some styling * show status next to buttons instead of on top * Update rounded corner for badge --------- Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
		
							
								
								
									
										6
									
								
								mobile/openapi/.openapi-generator/FILES
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								mobile/openapi/.openapi-generator/FILES
									
									
									
										generated
									
									
									
								
							| @@ -53,6 +53,7 @@ doc/JobCommand.md | |||||||
| doc/JobCommandDto.md | doc/JobCommandDto.md | ||||||
| doc/JobCountsDto.md | doc/JobCountsDto.md | ||||||
| doc/JobName.md | doc/JobName.md | ||||||
|  | doc/JobStatusDto.md | ||||||
| doc/LoginCredentialDto.md | doc/LoginCredentialDto.md | ||||||
| doc/LoginResponseDto.md | doc/LoginResponseDto.md | ||||||
| doc/LogoutResponseDto.md | doc/LogoutResponseDto.md | ||||||
| @@ -60,6 +61,7 @@ doc/OAuthApi.md | |||||||
| doc/OAuthCallbackDto.md | doc/OAuthCallbackDto.md | ||||||
| doc/OAuthConfigDto.md | doc/OAuthConfigDto.md | ||||||
| doc/OAuthConfigResponseDto.md | doc/OAuthConfigResponseDto.md | ||||||
|  | doc/QueueStatusDto.md | ||||||
| doc/RemoveAssetsDto.md | doc/RemoveAssetsDto.md | ||||||
| doc/SearchAlbumResponseDto.md | doc/SearchAlbumResponseDto.md | ||||||
| doc/SearchApi.md | doc/SearchApi.md | ||||||
| @@ -170,12 +172,14 @@ lib/model/job_command.dart | |||||||
| lib/model/job_command_dto.dart | lib/model/job_command_dto.dart | ||||||
| lib/model/job_counts_dto.dart | lib/model/job_counts_dto.dart | ||||||
| lib/model/job_name.dart | lib/model/job_name.dart | ||||||
|  | lib/model/job_status_dto.dart | ||||||
| lib/model/login_credential_dto.dart | lib/model/login_credential_dto.dart | ||||||
| lib/model/login_response_dto.dart | lib/model/login_response_dto.dart | ||||||
| lib/model/logout_response_dto.dart | lib/model/logout_response_dto.dart | ||||||
| lib/model/o_auth_callback_dto.dart | lib/model/o_auth_callback_dto.dart | ||||||
| lib/model/o_auth_config_dto.dart | lib/model/o_auth_config_dto.dart | ||||||
| lib/model/o_auth_config_response_dto.dart | lib/model/o_auth_config_response_dto.dart | ||||||
|  | lib/model/queue_status_dto.dart | ||||||
| lib/model/remove_assets_dto.dart | lib/model/remove_assets_dto.dart | ||||||
| lib/model/search_album_response_dto.dart | lib/model/search_album_response_dto.dart | ||||||
| lib/model/search_asset_dto.dart | lib/model/search_asset_dto.dart | ||||||
| @@ -264,6 +268,7 @@ test/job_command_dto_test.dart | |||||||
| test/job_command_test.dart | test/job_command_test.dart | ||||||
| test/job_counts_dto_test.dart | test/job_counts_dto_test.dart | ||||||
| test/job_name_test.dart | test/job_name_test.dart | ||||||
|  | test/job_status_dto_test.dart | ||||||
| test/login_credential_dto_test.dart | test/login_credential_dto_test.dart | ||||||
| test/login_response_dto_test.dart | test/login_response_dto_test.dart | ||||||
| test/logout_response_dto_test.dart | test/logout_response_dto_test.dart | ||||||
| @@ -271,6 +276,7 @@ test/o_auth_api_test.dart | |||||||
| test/o_auth_callback_dto_test.dart | test/o_auth_callback_dto_test.dart | ||||||
| test/o_auth_config_dto_test.dart | test/o_auth_config_dto_test.dart | ||||||
| test/o_auth_config_response_dto_test.dart | test/o_auth_config_response_dto_test.dart | ||||||
|  | test/queue_status_dto_test.dart | ||||||
| test/remove_assets_dto_test.dart | test/remove_assets_dto_test.dart | ||||||
| test/search_album_response_dto_test.dart | test/search_album_response_dto_test.dart | ||||||
| test/search_api_test.dart | test/search_api_test.dart | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							| @@ -200,12 +200,14 @@ Class | Method | HTTP request | Description | |||||||
|  - [JobCommandDto](doc//JobCommandDto.md) |  - [JobCommandDto](doc//JobCommandDto.md) | ||||||
|  - [JobCountsDto](doc//JobCountsDto.md) |  - [JobCountsDto](doc//JobCountsDto.md) | ||||||
|  - [JobName](doc//JobName.md) |  - [JobName](doc//JobName.md) | ||||||
|  |  - [JobStatusDto](doc//JobStatusDto.md) | ||||||
|  - [LoginCredentialDto](doc//LoginCredentialDto.md) |  - [LoginCredentialDto](doc//LoginCredentialDto.md) | ||||||
|  - [LoginResponseDto](doc//LoginResponseDto.md) |  - [LoginResponseDto](doc//LoginResponseDto.md) | ||||||
|  - [LogoutResponseDto](doc//LogoutResponseDto.md) |  - [LogoutResponseDto](doc//LogoutResponseDto.md) | ||||||
|  - [OAuthCallbackDto](doc//OAuthCallbackDto.md) |  - [OAuthCallbackDto](doc//OAuthCallbackDto.md) | ||||||
|  - [OAuthConfigDto](doc//OAuthConfigDto.md) |  - [OAuthConfigDto](doc//OAuthConfigDto.md) | ||||||
|  - [OAuthConfigResponseDto](doc//OAuthConfigResponseDto.md) |  - [OAuthConfigResponseDto](doc//OAuthConfigResponseDto.md) | ||||||
|  |  - [QueueStatusDto](doc//QueueStatusDto.md) | ||||||
|  - [RemoveAssetsDto](doc//RemoveAssetsDto.md) |  - [RemoveAssetsDto](doc//RemoveAssetsDto.md) | ||||||
|  - [SearchAlbumResponseDto](doc//SearchAlbumResponseDto.md) |  - [SearchAlbumResponseDto](doc//SearchAlbumResponseDto.md) | ||||||
|  - [SearchAssetDto](doc//SearchAssetDto.md) |  - [SearchAssetDto](doc//SearchAssetDto.md) | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								mobile/openapi/doc/AllJobStatusResponseDto.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										16
									
								
								mobile/openapi/doc/AllJobStatusResponseDto.md
									
									
									
										generated
									
									
									
								
							| @@ -8,14 +8,14 @@ import 'package:openapi/api.dart'; | |||||||
| ## Properties | ## Properties | ||||||
| Name | Type | Description | Notes | Name | Type | Description | Notes | ||||||
| ------------ | ------------- | ------------- | ------------- | ------------ | ------------- | ------------- | ------------- | ||||||
| **thumbnailGenerationQueue** | [**JobCountsDto**](JobCountsDto.md) |  |  | **thumbnailGenerationQueue** | [**JobStatusDto**](JobStatusDto.md) |  |  | ||||||
| **metadataExtractionQueue** | [**JobCountsDto**](JobCountsDto.md) |  |  | **metadataExtractionQueue** | [**JobStatusDto**](JobStatusDto.md) |  |  | ||||||
| **videoConversionQueue** | [**JobCountsDto**](JobCountsDto.md) |  |  | **videoConversionQueue** | [**JobStatusDto**](JobStatusDto.md) |  |  | ||||||
| **objectTaggingQueue** | [**JobCountsDto**](JobCountsDto.md) |  |  | **objectTaggingQueue** | [**JobStatusDto**](JobStatusDto.md) |  |  | ||||||
| **clipEncodingQueue** | [**JobCountsDto**](JobCountsDto.md) |  |  | **clipEncodingQueue** | [**JobStatusDto**](JobStatusDto.md) |  |  | ||||||
| **storageTemplateMigrationQueue** | [**JobCountsDto**](JobCountsDto.md) |  |  | **storageTemplateMigrationQueue** | [**JobStatusDto**](JobStatusDto.md) |  |  | ||||||
| **backgroundTaskQueue** | [**JobCountsDto**](JobCountsDto.md) |  |  | **backgroundTaskQueue** | [**JobStatusDto**](JobStatusDto.md) |  |  | ||||||
| **searchQueue** | [**JobCountsDto**](JobCountsDto.md) |  |  | **searchQueue** | [**JobStatusDto**](JobStatusDto.md) |  |  | ||||||
| 
 | 
 | ||||||
| [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | ||||||
| 
 | 
 | ||||||
|   | |||||||
							
								
								
									
										9
									
								
								mobile/openapi/doc/JobApi.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										9
									
								
								mobile/openapi/doc/JobApi.md
									
									
									
										generated
									
									
									
								
							| @@ -63,7 +63,7 @@ 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) | [[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) | ||||||
| 
 | 
 | ||||||
| # **sendJobCommand** | # **sendJobCommand** | ||||||
| > sendJobCommand(jobId, jobCommandDto) | > JobStatusDto sendJobCommand(jobId, jobCommandDto) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @@ -88,7 +88,8 @@ final jobId = ; // JobName | | |||||||
| final jobCommandDto = JobCommandDto(); // JobCommandDto |  | final jobCommandDto = JobCommandDto(); // JobCommandDto |  | ||||||
| 
 | 
 | ||||||
| try { | try { | ||||||
|     api_instance.sendJobCommand(jobId, jobCommandDto); |     final result = api_instance.sendJobCommand(jobId, jobCommandDto); | ||||||
|  |     print(result); | ||||||
| } catch (e) { | } catch (e) { | ||||||
|     print('Exception when calling JobApi->sendJobCommand: $e\n'); |     print('Exception when calling JobApi->sendJobCommand: $e\n'); | ||||||
| } | } | ||||||
| @@ -103,7 +104,7 @@ Name | Type | Description  | Notes | |||||||
| 
 | 
 | ||||||
| ### Return type | ### Return type | ||||||
| 
 | 
 | ||||||
| void (empty response body) | [**JobStatusDto**](JobStatusDto.md) | ||||||
| 
 | 
 | ||||||
| ### Authorization | ### Authorization | ||||||
| 
 | 
 | ||||||
| @@ -112,7 +113,7 @@ void (empty response body) | |||||||
| ### HTTP request headers | ### HTTP request headers | ||||||
| 
 | 
 | ||||||
|  - **Content-Type**: application/json |  - **Content-Type**: application/json | ||||||
|  - **Accept**: 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) | [[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) | ||||||
| 
 | 
 | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								mobile/openapi/doc/JobStatusDto.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								mobile/openapi/doc/JobStatusDto.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | # openapi.model.JobStatusDto | ||||||
|  | 
 | ||||||
|  | ## Load the model package | ||||||
|  | ```dart | ||||||
|  | import 'package:openapi/api.dart'; | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Properties | ||||||
|  | Name | Type | Description | Notes | ||||||
|  | ------------ | ------------- | ------------- | ------------- | ||||||
|  | **jobCounts** | [**JobCountsDto**](JobCountsDto.md) |  |  | ||||||
|  | **queueStatus** | [**QueueStatusDto**](QueueStatusDto.md) |  |  | ||||||
|  | 
 | ||||||
|  | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
							
								
								
									
										16
									
								
								mobile/openapi/doc/QueueStatusDto.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								mobile/openapi/doc/QueueStatusDto.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | # openapi.model.QueueStatusDto | ||||||
|  | 
 | ||||||
|  | ## Load the model package | ||||||
|  | ```dart | ||||||
|  | import 'package:openapi/api.dart'; | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Properties | ||||||
|  | Name | Type | Description | Notes | ||||||
|  | ------------ | ------------- | ------------- | ------------- | ||||||
|  | **isActive** | **bool** |  |  | ||||||
|  | **isPaused** | **bool** |  |  | ||||||
|  | 
 | ||||||
|  | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
							
								
								
									
										2
									
								
								mobile/openapi/lib/api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								mobile/openapi/lib/api.dart
									
									
									
										generated
									
									
									
								
							| @@ -86,12 +86,14 @@ part 'model/job_command.dart'; | |||||||
| part 'model/job_command_dto.dart'; | part 'model/job_command_dto.dart'; | ||||||
| part 'model/job_counts_dto.dart'; | part 'model/job_counts_dto.dart'; | ||||||
| part 'model/job_name.dart'; | part 'model/job_name.dart'; | ||||||
|  | part 'model/job_status_dto.dart'; | ||||||
| part 'model/login_credential_dto.dart'; | part 'model/login_credential_dto.dart'; | ||||||
| part 'model/login_response_dto.dart'; | part 'model/login_response_dto.dart'; | ||||||
| part 'model/logout_response_dto.dart'; | part 'model/logout_response_dto.dart'; | ||||||
| part 'model/o_auth_callback_dto.dart'; | part 'model/o_auth_callback_dto.dart'; | ||||||
| part 'model/o_auth_config_dto.dart'; | part 'model/o_auth_config_dto.dart'; | ||||||
| part 'model/o_auth_config_response_dto.dart'; | part 'model/o_auth_config_response_dto.dart'; | ||||||
|  | part 'model/queue_status_dto.dart'; | ||||||
| part 'model/remove_assets_dto.dart'; | part 'model/remove_assets_dto.dart'; | ||||||
| part 'model/search_album_response_dto.dart'; | part 'model/search_album_response_dto.dart'; | ||||||
| part 'model/search_asset_dto.dart'; | part 'model/search_asset_dto.dart'; | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								mobile/openapi/lib/api/job_api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								mobile/openapi/lib/api/job_api.dart
									
									
									
										generated
									
									
									
								
							| @@ -102,10 +102,18 @@ class JobApi { | |||||||
|   /// * [JobName] jobId (required): |   /// * [JobName] jobId (required): | ||||||
|   /// |   /// | ||||||
|   /// * [JobCommandDto] jobCommandDto (required): |   /// * [JobCommandDto] jobCommandDto (required): | ||||||
|   Future<void> sendJobCommand(JobName jobId, JobCommandDto jobCommandDto,) async { |   Future<JobStatusDto?> sendJobCommand(JobName jobId, JobCommandDto jobCommandDto,) async { | ||||||
|     final response = await sendJobCommandWithHttpInfo(jobId, jobCommandDto,); |     final response = await sendJobCommandWithHttpInfo(jobId, jobCommandDto,); | ||||||
|     if (response.statusCode >= HttpStatus.badRequest) { |     if (response.statusCode >= HttpStatus.badRequest) { | ||||||
|       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); |       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), 'JobStatusDto',) as JobStatusDto; | ||||||
|  |      | ||||||
|  |     } | ||||||
|  |     return null; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								mobile/openapi/lib/api_client.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								mobile/openapi/lib/api_client.dart
									
									
									
										generated
									
									
									
								
							| @@ -280,6 +280,8 @@ class ApiClient { | |||||||
|           return JobCountsDto.fromJson(value); |           return JobCountsDto.fromJson(value); | ||||||
|         case 'JobName': |         case 'JobName': | ||||||
|           return JobNameTypeTransformer().decode(value); |           return JobNameTypeTransformer().decode(value); | ||||||
|  |         case 'JobStatusDto': | ||||||
|  |           return JobStatusDto.fromJson(value); | ||||||
|         case 'LoginCredentialDto': |         case 'LoginCredentialDto': | ||||||
|           return LoginCredentialDto.fromJson(value); |           return LoginCredentialDto.fromJson(value); | ||||||
|         case 'LoginResponseDto': |         case 'LoginResponseDto': | ||||||
| @@ -292,6 +294,8 @@ class ApiClient { | |||||||
|           return OAuthConfigDto.fromJson(value); |           return OAuthConfigDto.fromJson(value); | ||||||
|         case 'OAuthConfigResponseDto': |         case 'OAuthConfigResponseDto': | ||||||
|           return OAuthConfigResponseDto.fromJson(value); |           return OAuthConfigResponseDto.fromJson(value); | ||||||
|  |         case 'QueueStatusDto': | ||||||
|  |           return QueueStatusDto.fromJson(value); | ||||||
|         case 'RemoveAssetsDto': |         case 'RemoveAssetsDto': | ||||||
|           return RemoveAssetsDto.fromJson(value); |           return RemoveAssetsDto.fromJson(value); | ||||||
|         case 'SearchAlbumResponseDto': |         case 'SearchAlbumResponseDto': | ||||||
|   | |||||||
| @@ -23,21 +23,21 @@ class AllJobStatusResponseDto { | |||||||
|     required this.searchQueue, |     required this.searchQueue, | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   JobCountsDto thumbnailGenerationQueue; |   JobStatusDto thumbnailGenerationQueue; | ||||||
| 
 | 
 | ||||||
|   JobCountsDto metadataExtractionQueue; |   JobStatusDto metadataExtractionQueue; | ||||||
| 
 | 
 | ||||||
|   JobCountsDto videoConversionQueue; |   JobStatusDto videoConversionQueue; | ||||||
| 
 | 
 | ||||||
|   JobCountsDto objectTaggingQueue; |   JobStatusDto objectTaggingQueue; | ||||||
| 
 | 
 | ||||||
|   JobCountsDto clipEncodingQueue; |   JobStatusDto clipEncodingQueue; | ||||||
| 
 | 
 | ||||||
|   JobCountsDto storageTemplateMigrationQueue; |   JobStatusDto storageTemplateMigrationQueue; | ||||||
| 
 | 
 | ||||||
|   JobCountsDto backgroundTaskQueue; |   JobStatusDto backgroundTaskQueue; | ||||||
| 
 | 
 | ||||||
|   JobCountsDto searchQueue; |   JobStatusDto searchQueue; | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   bool operator ==(Object other) => identical(this, other) || other is AllJobStatusResponseDto && |   bool operator ==(Object other) => identical(this, other) || other is AllJobStatusResponseDto && | ||||||
| @@ -97,14 +97,14 @@ class AllJobStatusResponseDto { | |||||||
|       }()); |       }()); | ||||||
| 
 | 
 | ||||||
|       return AllJobStatusResponseDto( |       return AllJobStatusResponseDto( | ||||||
|         thumbnailGenerationQueue: JobCountsDto.fromJson(json[r'thumbnail-generation-queue'])!, |         thumbnailGenerationQueue: JobStatusDto.fromJson(json[r'thumbnail-generation-queue'])!, | ||||||
|         metadataExtractionQueue: JobCountsDto.fromJson(json[r'metadata-extraction-queue'])!, |         metadataExtractionQueue: JobStatusDto.fromJson(json[r'metadata-extraction-queue'])!, | ||||||
|         videoConversionQueue: JobCountsDto.fromJson(json[r'video-conversion-queue'])!, |         videoConversionQueue: JobStatusDto.fromJson(json[r'video-conversion-queue'])!, | ||||||
|         objectTaggingQueue: JobCountsDto.fromJson(json[r'object-tagging-queue'])!, |         objectTaggingQueue: JobStatusDto.fromJson(json[r'object-tagging-queue'])!, | ||||||
|         clipEncodingQueue: JobCountsDto.fromJson(json[r'clip-encoding-queue'])!, |         clipEncodingQueue: JobStatusDto.fromJson(json[r'clip-encoding-queue'])!, | ||||||
|         storageTemplateMigrationQueue: JobCountsDto.fromJson(json[r'storage-template-migration-queue'])!, |         storageTemplateMigrationQueue: JobStatusDto.fromJson(json[r'storage-template-migration-queue'])!, | ||||||
|         backgroundTaskQueue: JobCountsDto.fromJson(json[r'background-task-queue'])!, |         backgroundTaskQueue: JobStatusDto.fromJson(json[r'background-task-queue'])!, | ||||||
|         searchQueue: JobCountsDto.fromJson(json[r'search-queue'])!, |         searchQueue: JobStatusDto.fromJson(json[r'search-queue'])!, | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|     return null; |     return null; | ||||||
|   | |||||||
							
								
								
									
										119
									
								
								mobile/openapi/lib/model/job_status_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								mobile/openapi/lib/model/job_status_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | |||||||
|  | // | ||||||
|  | // 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 JobStatusDto { | ||||||
|  |   /// Returns a new [JobStatusDto] instance. | ||||||
|  |   JobStatusDto({ | ||||||
|  |     required this.jobCounts, | ||||||
|  |     required this.queueStatus, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   JobCountsDto jobCounts; | ||||||
|  | 
 | ||||||
|  |   QueueStatusDto queueStatus; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   bool operator ==(Object other) => identical(this, other) || other is JobStatusDto && | ||||||
|  |      other.jobCounts == jobCounts && | ||||||
|  |      other.queueStatus == queueStatus; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   int get hashCode => | ||||||
|  |     // ignore: unnecessary_parenthesis | ||||||
|  |     (jobCounts.hashCode) + | ||||||
|  |     (queueStatus.hashCode); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() => 'JobStatusDto[jobCounts=$jobCounts, queueStatus=$queueStatus]'; | ||||||
|  | 
 | ||||||
|  |   Map<String, dynamic> toJson() { | ||||||
|  |     final json = <String, dynamic>{}; | ||||||
|  |       json[r'jobCounts'] = this.jobCounts; | ||||||
|  |       json[r'queueStatus'] = this.queueStatus; | ||||||
|  |     return json; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Returns a new [JobStatusDto] instance and imports its values from | ||||||
|  |   /// [value] if it's a [Map], null otherwise. | ||||||
|  |   // ignore: prefer_constructors_over_static_methods | ||||||
|  |   static JobStatusDto? 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 "JobStatusDto[$key]" is missing from JSON.'); | ||||||
|  |           assert(json[key] != null, 'Required key "JobStatusDto[$key]" has a null value in JSON.'); | ||||||
|  |         }); | ||||||
|  |         return true; | ||||||
|  |       }()); | ||||||
|  | 
 | ||||||
|  |       return JobStatusDto( | ||||||
|  |         jobCounts: JobCountsDto.fromJson(json[r'jobCounts'])!, | ||||||
|  |         queueStatus: QueueStatusDto.fromJson(json[r'queueStatus'])!, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static List<JobStatusDto>? listFromJson(dynamic json, {bool growable = false,}) { | ||||||
|  |     final result = <JobStatusDto>[]; | ||||||
|  |     if (json is List && json.isNotEmpty) { | ||||||
|  |       for (final row in json) { | ||||||
|  |         final value = JobStatusDto.fromJson(row); | ||||||
|  |         if (value != null) { | ||||||
|  |           result.add(value); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return result.toList(growable: growable); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static Map<String, JobStatusDto> mapFromJson(dynamic json) { | ||||||
|  |     final map = <String, JobStatusDto>{}; | ||||||
|  |     if (json is Map && json.isNotEmpty) { | ||||||
|  |       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||||
|  |       for (final entry in json.entries) { | ||||||
|  |         final value = JobStatusDto.fromJson(entry.value); | ||||||
|  |         if (value != null) { | ||||||
|  |           map[entry.key] = value; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return map; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // maps a json object with a list of JobStatusDto-objects as value to a dart map | ||||||
|  |   static Map<String, List<JobStatusDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||||
|  |     final map = <String, List<JobStatusDto>>{}; | ||||||
|  |     if (json is Map && json.isNotEmpty) { | ||||||
|  |       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||||
|  |       for (final entry in json.entries) { | ||||||
|  |         final value = JobStatusDto.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>{ | ||||||
|  |     'jobCounts', | ||||||
|  |     'queueStatus', | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
							
								
								
									
										119
									
								
								mobile/openapi/lib/model/queue_status_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								mobile/openapi/lib/model/queue_status_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | |||||||
|  | // | ||||||
|  | // 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 QueueStatusDto { | ||||||
|  |   /// Returns a new [QueueStatusDto] instance. | ||||||
|  |   QueueStatusDto({ | ||||||
|  |     required this.isActive, | ||||||
|  |     required this.isPaused, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   bool isActive; | ||||||
|  | 
 | ||||||
|  |   bool isPaused; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   bool operator ==(Object other) => identical(this, other) || other is QueueStatusDto && | ||||||
|  |      other.isActive == isActive && | ||||||
|  |      other.isPaused == isPaused; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   int get hashCode => | ||||||
|  |     // ignore: unnecessary_parenthesis | ||||||
|  |     (isActive.hashCode) + | ||||||
|  |     (isPaused.hashCode); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() => 'QueueStatusDto[isActive=$isActive, isPaused=$isPaused]'; | ||||||
|  | 
 | ||||||
|  |   Map<String, dynamic> toJson() { | ||||||
|  |     final json = <String, dynamic>{}; | ||||||
|  |       json[r'isActive'] = this.isActive; | ||||||
|  |       json[r'isPaused'] = this.isPaused; | ||||||
|  |     return json; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Returns a new [QueueStatusDto] instance and imports its values from | ||||||
|  |   /// [value] if it's a [Map], null otherwise. | ||||||
|  |   // ignore: prefer_constructors_over_static_methods | ||||||
|  |   static QueueStatusDto? 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 "QueueStatusDto[$key]" is missing from JSON.'); | ||||||
|  |           assert(json[key] != null, 'Required key "QueueStatusDto[$key]" has a null value in JSON.'); | ||||||
|  |         }); | ||||||
|  |         return true; | ||||||
|  |       }()); | ||||||
|  | 
 | ||||||
|  |       return QueueStatusDto( | ||||||
|  |         isActive: mapValueOfType<bool>(json, r'isActive')!, | ||||||
|  |         isPaused: mapValueOfType<bool>(json, r'isPaused')!, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static List<QueueStatusDto>? listFromJson(dynamic json, {bool growable = false,}) { | ||||||
|  |     final result = <QueueStatusDto>[]; | ||||||
|  |     if (json is List && json.isNotEmpty) { | ||||||
|  |       for (final row in json) { | ||||||
|  |         final value = QueueStatusDto.fromJson(row); | ||||||
|  |         if (value != null) { | ||||||
|  |           result.add(value); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return result.toList(growable: growable); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static Map<String, QueueStatusDto> mapFromJson(dynamic json) { | ||||||
|  |     final map = <String, QueueStatusDto>{}; | ||||||
|  |     if (json is Map && json.isNotEmpty) { | ||||||
|  |       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||||
|  |       for (final entry in json.entries) { | ||||||
|  |         final value = QueueStatusDto.fromJson(entry.value); | ||||||
|  |         if (value != null) { | ||||||
|  |           map[entry.key] = value; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return map; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // maps a json object with a list of QueueStatusDto-objects as value to a dart map | ||||||
|  |   static Map<String, List<QueueStatusDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||||
|  |     final map = <String, List<QueueStatusDto>>{}; | ||||||
|  |     if (json is Map && json.isNotEmpty) { | ||||||
|  |       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||||
|  |       for (final entry in json.entries) { | ||||||
|  |         final value = QueueStatusDto.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>{ | ||||||
|  |     'isActive', | ||||||
|  |     'isPaused', | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| @@ -16,42 +16,42 @@ void main() { | |||||||
|   // final instance = AllJobStatusResponseDto(); |   // final instance = AllJobStatusResponseDto(); | ||||||
| 
 | 
 | ||||||
|   group('test AllJobStatusResponseDto', () { |   group('test AllJobStatusResponseDto', () { | ||||||
|     // JobCountsDto thumbnailGenerationQueue |     // JobStatusDto thumbnailGenerationQueue | ||||||
|     test('to test the property `thumbnailGenerationQueue`', () async { |     test('to test the property `thumbnailGenerationQueue`', () async { | ||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     // JobCountsDto metadataExtractionQueue |     // JobStatusDto metadataExtractionQueue | ||||||
|     test('to test the property `metadataExtractionQueue`', () async { |     test('to test the property `metadataExtractionQueue`', () async { | ||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     // JobCountsDto videoConversionQueue |     // JobStatusDto videoConversionQueue | ||||||
|     test('to test the property `videoConversionQueue`', () async { |     test('to test the property `videoConversionQueue`', () async { | ||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     // JobCountsDto objectTaggingQueue |     // JobStatusDto objectTaggingQueue | ||||||
|     test('to test the property `objectTaggingQueue`', () async { |     test('to test the property `objectTaggingQueue`', () async { | ||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     // JobCountsDto clipEncodingQueue |     // JobStatusDto clipEncodingQueue | ||||||
|     test('to test the property `clipEncodingQueue`', () async { |     test('to test the property `clipEncodingQueue`', () async { | ||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     // JobCountsDto storageTemplateMigrationQueue |     // JobStatusDto storageTemplateMigrationQueue | ||||||
|     test('to test the property `storageTemplateMigrationQueue`', () async { |     test('to test the property `storageTemplateMigrationQueue`', () async { | ||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     // JobCountsDto backgroundTaskQueue |     // JobStatusDto backgroundTaskQueue | ||||||
|     test('to test the property `backgroundTaskQueue`', () async { |     test('to test the property `backgroundTaskQueue`', () async { | ||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     // JobCountsDto searchQueue |     // JobStatusDto searchQueue | ||||||
|     test('to test the property `searchQueue`', () async { |     test('to test the property `searchQueue`', () async { | ||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								mobile/openapi/test/job_api_test.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								mobile/openapi/test/job_api_test.dart
									
									
									
										generated
									
									
									
								
							| @@ -26,7 +26,7 @@ void main() { | |||||||
| 
 | 
 | ||||||
|     //  |     //  | ||||||
|     // |     // | ||||||
|     //Future sendJobCommand(JobName jobId, JobCommandDto jobCommandDto) async |     //Future<JobStatusDto> sendJobCommand(JobName jobId, JobCommandDto jobCommandDto) async | ||||||
|     test('test sendJobCommand', () async { |     test('test sendJobCommand', () async { | ||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
|   | |||||||
							
								
								
									
										32
									
								
								mobile/openapi/test/job_status_dto_test.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								mobile/openapi/test/job_status_dto_test.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | // | ||||||
|  | // 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 JobStatusDto | ||||||
|  | void main() { | ||||||
|  |   // final instance = JobStatusDto(); | ||||||
|  | 
 | ||||||
|  |   group('test JobStatusDto', () { | ||||||
|  |     // JobCountsDto jobCounts | ||||||
|  |     test('to test the property `jobCounts`', () async { | ||||||
|  |       // TODO | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // QueueStatusDto queueStatus | ||||||
|  |     test('to test the property `queueStatus`', () async { | ||||||
|  |       // TODO | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								mobile/openapi/test/queue_status_dto_test.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								mobile/openapi/test/queue_status_dto_test.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | // | ||||||
|  | // 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 QueueStatusDto | ||||||
|  | void main() { | ||||||
|  |   // final instance = QueueStatusDto(); | ||||||
|  | 
 | ||||||
|  |   group('test QueueStatusDto', () { | ||||||
|  |     // bool isActive | ||||||
|  |     test('to test the property `isActive`', () async { | ||||||
|  |       // TODO | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // bool isPaused | ||||||
|  |     test('to test the property `isPaused`', () async { | ||||||
|  |       // TODO | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| import { AllJobStatusResponseDto, JobCommandDto, JobIdDto, JobService } from '@app/domain'; | import { AllJobStatusResponseDto, JobCommandDto, JobStatusDto, JobIdDto, JobService } from '@app/domain'; | ||||||
| import { Body, Controller, Get, Param, Put, UsePipes, ValidationPipe } from '@nestjs/common'; | import { Body, Controller, Get, Param, Put, UsePipes, ValidationPipe } from '@nestjs/common'; | ||||||
| import { ApiTags } from '@nestjs/swagger'; | import { ApiTags } from '@nestjs/swagger'; | ||||||
| import { Authenticated } from '../decorators/authenticated.decorator'; | import { Authenticated } from '../decorators/authenticated.decorator'; | ||||||
| @@ -16,7 +16,8 @@ export class JobController { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   @Put('/:jobId') |   @Put('/:jobId') | ||||||
|   sendJobCommand(@Param() { jobId }: JobIdDto, @Body() dto: JobCommandDto): Promise<void> { |   async sendJobCommand(@Param() { jobId }: JobIdDto, @Body() dto: JobCommandDto): Promise<JobStatusDto> { | ||||||
|     return this.service.handleCommand(jobId, dto); |     await this.service.handleCommand(jobId, dto); | ||||||
|  |     return await this.service.getJobStatus(jobId); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -541,7 +541,14 @@ | |||||||
|         }, |         }, | ||||||
|         "responses": { |         "responses": { | ||||||
|           "200": { |           "200": { | ||||||
|             "description": "" |             "description": "", | ||||||
|  |             "content": { | ||||||
|  |               "application/json": { | ||||||
|  |                 "schema": { | ||||||
|  |                   "$ref": "#/components/schemas/JobStatusDto" | ||||||
|  |                 } | ||||||
|  |               } | ||||||
|  |             } | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         "tags": [ |         "tags": [ | ||||||
| @@ -4088,32 +4095,62 @@ | |||||||
|           "paused" |           "paused" | ||||||
|         ] |         ] | ||||||
|       }, |       }, | ||||||
|  |       "QueueStatusDto": { | ||||||
|  |         "type": "object", | ||||||
|  |         "properties": { | ||||||
|  |           "isActive": { | ||||||
|  |             "type": "boolean" | ||||||
|  |           }, | ||||||
|  |           "isPaused": { | ||||||
|  |             "type": "boolean" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "required": [ | ||||||
|  |           "isActive", | ||||||
|  |           "isPaused" | ||||||
|  |         ] | ||||||
|  |       }, | ||||||
|  |       "JobStatusDto": { | ||||||
|  |         "type": "object", | ||||||
|  |         "properties": { | ||||||
|  |           "jobCounts": { | ||||||
|  |             "$ref": "#/components/schemas/JobCountsDto" | ||||||
|  |           }, | ||||||
|  |           "queueStatus": { | ||||||
|  |             "$ref": "#/components/schemas/QueueStatusDto" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "required": [ | ||||||
|  |           "jobCounts", | ||||||
|  |           "queueStatus" | ||||||
|  |         ] | ||||||
|  |       }, | ||||||
|       "AllJobStatusResponseDto": { |       "AllJobStatusResponseDto": { | ||||||
|         "type": "object", |         "type": "object", | ||||||
|         "properties": { |         "properties": { | ||||||
|           "thumbnail-generation-queue": { |           "thumbnail-generation-queue": { | ||||||
|             "$ref": "#/components/schemas/JobCountsDto" |             "$ref": "#/components/schemas/JobStatusDto" | ||||||
|           }, |           }, | ||||||
|           "metadata-extraction-queue": { |           "metadata-extraction-queue": { | ||||||
|             "$ref": "#/components/schemas/JobCountsDto" |             "$ref": "#/components/schemas/JobStatusDto" | ||||||
|           }, |           }, | ||||||
|           "video-conversion-queue": { |           "video-conversion-queue": { | ||||||
|             "$ref": "#/components/schemas/JobCountsDto" |             "$ref": "#/components/schemas/JobStatusDto" | ||||||
|           }, |           }, | ||||||
|           "object-tagging-queue": { |           "object-tagging-queue": { | ||||||
|             "$ref": "#/components/schemas/JobCountsDto" |             "$ref": "#/components/schemas/JobStatusDto" | ||||||
|           }, |           }, | ||||||
|           "clip-encoding-queue": { |           "clip-encoding-queue": { | ||||||
|             "$ref": "#/components/schemas/JobCountsDto" |             "$ref": "#/components/schemas/JobStatusDto" | ||||||
|           }, |           }, | ||||||
|           "storage-template-migration-queue": { |           "storage-template-migration-queue": { | ||||||
|             "$ref": "#/components/schemas/JobCountsDto" |             "$ref": "#/components/schemas/JobStatusDto" | ||||||
|           }, |           }, | ||||||
|           "background-task-queue": { |           "background-task-queue": { | ||||||
|             "$ref": "#/components/schemas/JobCountsDto" |             "$ref": "#/components/schemas/JobStatusDto" | ||||||
|           }, |           }, | ||||||
|           "search-queue": { |           "search-queue": { | ||||||
|             "$ref": "#/components/schemas/JobCountsDto" |             "$ref": "#/components/schemas/JobStatusDto" | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         "required": [ |         "required": [ | ||||||
|   | |||||||
| @@ -18,6 +18,11 @@ export interface JobCounts { | |||||||
|   paused: number; |   paused: number; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export interface QueueStatus { | ||||||
|  |   isActive: boolean; | ||||||
|  |   isPaused: boolean; | ||||||
|  | } | ||||||
|  |  | ||||||
| export type JobItem = | export type JobItem = | ||||||
|   // Asset Upload |   // Asset Upload | ||||||
|   | { name: JobName.ASSET_UPLOADED; data: IAssetUploadedJob } |   | { name: JobName.ASSET_UPLOADED; data: IAssetUploadedJob } | ||||||
| @@ -73,6 +78,6 @@ export interface IJobRepository { | |||||||
|   pause(name: QueueName): Promise<void>; |   pause(name: QueueName): Promise<void>; | ||||||
|   resume(name: QueueName): Promise<void>; |   resume(name: QueueName): Promise<void>; | ||||||
|   empty(name: QueueName): Promise<void>; |   empty(name: QueueName): Promise<void>; | ||||||
|   isActive(name: QueueName): Promise<boolean>; |   getQueueStatus(name: QueueName): Promise<QueueStatus>; | ||||||
|   getJobCounts(name: QueueName): Promise<JobCounts>; |   getJobCounts(name: QueueName): Promise<JobCounts>; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -25,72 +25,35 @@ describe(JobService.name, () => { | |||||||
|         waiting: 1, |         waiting: 1, | ||||||
|         paused: 1, |         paused: 1, | ||||||
|       }); |       }); | ||||||
|  |       jobMock.getQueueStatus.mockResolvedValue({ | ||||||
|  |         isActive: true, | ||||||
|  |         isPaused: true, | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |       const expectedJobStatus = { | ||||||
|  |         jobCounts: { | ||||||
|  |           active: 1, | ||||||
|  |           completed: 1, | ||||||
|  |           delayed: 1, | ||||||
|  |           failed: 1, | ||||||
|  |           waiting: 1, | ||||||
|  |           paused: 1, | ||||||
|  |         }, | ||||||
|  |         queueStatus: { | ||||||
|  |           isActive: true, | ||||||
|  |           isPaused: true, | ||||||
|  |         }, | ||||||
|  |       }; | ||||||
|  |  | ||||||
|       await expect(sut.getAllJobsStatus()).resolves.toEqual({ |       await expect(sut.getAllJobsStatus()).resolves.toEqual({ | ||||||
|         'background-task-queue': { |         'background-task-queue': expectedJobStatus, | ||||||
|           active: 1, |         'clip-encoding-queue': expectedJobStatus, | ||||||
|           completed: 1, |         'metadata-extraction-queue': expectedJobStatus, | ||||||
|           delayed: 1, |         'object-tagging-queue': expectedJobStatus, | ||||||
|           failed: 1, |         'search-queue': expectedJobStatus, | ||||||
|           waiting: 1, |         'storage-template-migration-queue': expectedJobStatus, | ||||||
|           paused: 1, |         'thumbnail-generation-queue': expectedJobStatus, | ||||||
|         }, |         'video-conversion-queue': expectedJobStatus, | ||||||
|         'clip-encoding-queue': { |  | ||||||
|           active: 1, |  | ||||||
|           completed: 1, |  | ||||||
|           delayed: 1, |  | ||||||
|           failed: 1, |  | ||||||
|           waiting: 1, |  | ||||||
|           paused: 1, |  | ||||||
|         }, |  | ||||||
|         'metadata-extraction-queue': { |  | ||||||
|           active: 1, |  | ||||||
|           completed: 1, |  | ||||||
|           delayed: 1, |  | ||||||
|           failed: 1, |  | ||||||
|           waiting: 1, |  | ||||||
|           paused: 1, |  | ||||||
|         }, |  | ||||||
|         'object-tagging-queue': { |  | ||||||
|           active: 1, |  | ||||||
|           completed: 1, |  | ||||||
|           delayed: 1, |  | ||||||
|           failed: 1, |  | ||||||
|           waiting: 1, |  | ||||||
|           paused: 1, |  | ||||||
|         }, |  | ||||||
|         'search-queue': { |  | ||||||
|           active: 1, |  | ||||||
|           completed: 1, |  | ||||||
|           delayed: 1, |  | ||||||
|           failed: 1, |  | ||||||
|           waiting: 1, |  | ||||||
|           paused: 1, |  | ||||||
|         }, |  | ||||||
|         'storage-template-migration-queue': { |  | ||||||
|           active: 1, |  | ||||||
|           completed: 1, |  | ||||||
|           delayed: 1, |  | ||||||
|           failed: 1, |  | ||||||
|           waiting: 1, |  | ||||||
|           paused: 1, |  | ||||||
|         }, |  | ||||||
|         'thumbnail-generation-queue': { |  | ||||||
|           active: 1, |  | ||||||
|           completed: 1, |  | ||||||
|           delayed: 1, |  | ||||||
|           failed: 1, |  | ||||||
|           waiting: 1, |  | ||||||
|           paused: 1, |  | ||||||
|         }, |  | ||||||
|         'video-conversion-queue': { |  | ||||||
|           active: 1, |  | ||||||
|           completed: 1, |  | ||||||
|           delayed: 1, |  | ||||||
|           failed: 1, |  | ||||||
|           waiting: 1, |  | ||||||
|           paused: 1, |  | ||||||
|         }, |  | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
| @@ -115,7 +78,7 @@ describe(JobService.name, () => { | |||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     it('should not start a job that is already running', async () => { |     it('should not start a job that is already running', async () => { | ||||||
|       jobMock.isActive.mockResolvedValue(true); |       jobMock.getQueueStatus.mockResolvedValue({ isActive: true, isPaused: false }); | ||||||
|  |  | ||||||
|       await expect( |       await expect( | ||||||
|         sut.handleCommand(QueueName.VIDEO_CONVERSION, { command: JobCommand.START, force: false }), |         sut.handleCommand(QueueName.VIDEO_CONVERSION, { command: JobCommand.START, force: false }), | ||||||
| @@ -125,7 +88,7 @@ describe(JobService.name, () => { | |||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     it('should handle a start video conversion command', async () => { |     it('should handle a start video conversion command', async () => { | ||||||
|       jobMock.isActive.mockResolvedValue(false); |       jobMock.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); | ||||||
|  |  | ||||||
|       await sut.handleCommand(QueueName.VIDEO_CONVERSION, { command: JobCommand.START, force: false }); |       await sut.handleCommand(QueueName.VIDEO_CONVERSION, { command: JobCommand.START, force: false }); | ||||||
|  |  | ||||||
| @@ -133,7 +96,7 @@ describe(JobService.name, () => { | |||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     it('should handle a start storage template migration command', async () => { |     it('should handle a start storage template migration command', async () => { | ||||||
|       jobMock.isActive.mockResolvedValue(false); |       jobMock.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); | ||||||
|  |  | ||||||
|       await sut.handleCommand(QueueName.STORAGE_TEMPLATE_MIGRATION, { command: JobCommand.START, force: false }); |       await sut.handleCommand(QueueName.STORAGE_TEMPLATE_MIGRATION, { command: JobCommand.START, force: false }); | ||||||
|  |  | ||||||
| @@ -141,7 +104,7 @@ describe(JobService.name, () => { | |||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     it('should handle a start object tagging command', async () => { |     it('should handle a start object tagging command', async () => { | ||||||
|       jobMock.isActive.mockResolvedValue(false); |       jobMock.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); | ||||||
|  |  | ||||||
|       await sut.handleCommand(QueueName.OBJECT_TAGGING, { command: JobCommand.START, force: false }); |       await sut.handleCommand(QueueName.OBJECT_TAGGING, { command: JobCommand.START, force: false }); | ||||||
|  |  | ||||||
| @@ -149,7 +112,7 @@ describe(JobService.name, () => { | |||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     it('should handle a start clip encoding command', async () => { |     it('should handle a start clip encoding command', async () => { | ||||||
|       jobMock.isActive.mockResolvedValue(false); |       jobMock.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); | ||||||
|  |  | ||||||
|       await sut.handleCommand(QueueName.CLIP_ENCODING, { command: JobCommand.START, force: false }); |       await sut.handleCommand(QueueName.CLIP_ENCODING, { command: JobCommand.START, force: false }); | ||||||
|  |  | ||||||
| @@ -157,7 +120,7 @@ describe(JobService.name, () => { | |||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     it('should handle a start metadata extraction command', async () => { |     it('should handle a start metadata extraction command', async () => { | ||||||
|       jobMock.isActive.mockResolvedValue(false); |       jobMock.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); | ||||||
|  |  | ||||||
|       await sut.handleCommand(QueueName.METADATA_EXTRACTION, { command: JobCommand.START, force: false }); |       await sut.handleCommand(QueueName.METADATA_EXTRACTION, { command: JobCommand.START, force: false }); | ||||||
|  |  | ||||||
| @@ -165,7 +128,7 @@ describe(JobService.name, () => { | |||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     it('should handle a start thumbnail generation command', async () => { |     it('should handle a start thumbnail generation command', async () => { | ||||||
|       jobMock.isActive.mockResolvedValue(false); |       jobMock.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); | ||||||
|  |  | ||||||
|       await sut.handleCommand(QueueName.THUMBNAIL_GENERATION, { command: JobCommand.START, force: false }); |       await sut.handleCommand(QueueName.THUMBNAIL_GENERATION, { command: JobCommand.START, force: false }); | ||||||
|  |  | ||||||
| @@ -173,7 +136,7 @@ describe(JobService.name, () => { | |||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     it('should throw a bad request when an invalid queue is used', async () => { |     it('should throw a bad request when an invalid queue is used', async () => { | ||||||
|       jobMock.isActive.mockResolvedValue(false); |       jobMock.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); | ||||||
|  |  | ||||||
|       await expect( |       await expect( | ||||||
|         sut.handleCommand(QueueName.BACKGROUND_TASK, { command: JobCommand.START, force: false }), |         sut.handleCommand(QueueName.BACKGROUND_TASK, { command: JobCommand.START, force: false }), | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ import { assertMachineLearningEnabled } from '../domain.constant'; | |||||||
| import { JobCommandDto } from './dto'; | import { JobCommandDto } from './dto'; | ||||||
| import { JobCommand, JobName, QueueName } from './job.constants'; | import { JobCommand, JobName, QueueName } from './job.constants'; | ||||||
| import { IJobRepository } from './job.repository'; | import { IJobRepository } from './job.repository'; | ||||||
| import { AllJobStatusResponseDto } from './response-dto'; | import { AllJobStatusResponseDto, JobStatusDto } from './response-dto'; | ||||||
|  |  | ||||||
| @Injectable() | @Injectable() | ||||||
| export class JobService { | export class JobService { | ||||||
| @@ -29,16 +29,25 @@ export class JobService { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   async getJobStatus(queueName: QueueName): Promise<JobStatusDto> { | ||||||
|  |     const [jobCounts, queueStatus] = await Promise.all([ | ||||||
|  |       this.jobRepository.getJobCounts(queueName), | ||||||
|  |       this.jobRepository.getQueueStatus(queueName), | ||||||
|  |     ]); | ||||||
|  |  | ||||||
|  |     return { jobCounts, queueStatus }; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   async getAllJobsStatus(): Promise<AllJobStatusResponseDto> { |   async getAllJobsStatus(): Promise<AllJobStatusResponseDto> { | ||||||
|     const response = new AllJobStatusResponseDto(); |     const response = new AllJobStatusResponseDto(); | ||||||
|     for (const queueName of Object.values(QueueName)) { |     for (const queueName of Object.values(QueueName)) { | ||||||
|       response[queueName] = await this.jobRepository.getJobCounts(queueName); |       response[queueName] = await this.getJobStatus(queueName); | ||||||
|     } |     } | ||||||
|     return response; |     return response; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private async start(name: QueueName, { force }: JobCommandDto): Promise<void> { |   private async start(name: QueueName, { force }: JobCommandDto): Promise<void> { | ||||||
|     const isActive = await this.jobRepository.isActive(name); |     const { isActive } = await this.jobRepository.getQueueStatus(name); | ||||||
|     if (isActive) { |     if (isActive) { | ||||||
|       throw new BadRequestException(`Job is already running`); |       throw new BadRequestException(`Job is already running`); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -16,28 +16,41 @@ export class JobCountsDto { | |||||||
|   paused!: number; |   paused!: number; | ||||||
| } | } | ||||||
|  |  | ||||||
| export class AllJobStatusResponseDto implements Record<QueueName, JobCountsDto> { | export class QueueStatusDto { | ||||||
|   @ApiProperty({ type: JobCountsDto }) |   isActive!: boolean; | ||||||
|   [QueueName.THUMBNAIL_GENERATION]!: JobCountsDto; |   isPaused!: boolean; | ||||||
|  | } | ||||||
|   @ApiProperty({ type: JobCountsDto }) |  | ||||||
|   [QueueName.METADATA_EXTRACTION]!: JobCountsDto; | export class JobStatusDto { | ||||||
|  |   @ApiProperty({ type: JobCountsDto }) | ||||||
|   @ApiProperty({ type: JobCountsDto }) |   jobCounts!: JobCountsDto; | ||||||
|   [QueueName.VIDEO_CONVERSION]!: JobCountsDto; |  | ||||||
|  |   @ApiProperty({ type: QueueStatusDto }) | ||||||
|   @ApiProperty({ type: JobCountsDto }) |   queueStatus!: QueueStatusDto; | ||||||
|   [QueueName.OBJECT_TAGGING]!: JobCountsDto; | } | ||||||
|  |  | ||||||
|   @ApiProperty({ type: JobCountsDto }) | export class AllJobStatusResponseDto implements Record<QueueName, JobStatusDto> { | ||||||
|   [QueueName.CLIP_ENCODING]!: JobCountsDto; |   @ApiProperty({ type: JobStatusDto }) | ||||||
|  |   [QueueName.THUMBNAIL_GENERATION]!: JobStatusDto; | ||||||
|   @ApiProperty({ type: JobCountsDto }) |  | ||||||
|   [QueueName.STORAGE_TEMPLATE_MIGRATION]!: JobCountsDto; |   @ApiProperty({ type: JobStatusDto }) | ||||||
|  |   [QueueName.METADATA_EXTRACTION]!: JobStatusDto; | ||||||
|   @ApiProperty({ type: JobCountsDto }) |  | ||||||
|   [QueueName.BACKGROUND_TASK]!: JobCountsDto; |   @ApiProperty({ type: JobStatusDto }) | ||||||
|  |   [QueueName.VIDEO_CONVERSION]!: JobStatusDto; | ||||||
|   @ApiProperty({ type: JobCountsDto }) |  | ||||||
|   [QueueName.SEARCH]!: JobCountsDto; |   @ApiProperty({ type: JobStatusDto }) | ||||||
|  |   [QueueName.OBJECT_TAGGING]!: JobStatusDto; | ||||||
|  |  | ||||||
|  |   @ApiProperty({ type: JobStatusDto }) | ||||||
|  |   [QueueName.CLIP_ENCODING]!: JobStatusDto; | ||||||
|  |  | ||||||
|  |   @ApiProperty({ type: JobStatusDto }) | ||||||
|  |   [QueueName.STORAGE_TEMPLATE_MIGRATION]!: JobStatusDto; | ||||||
|  |  | ||||||
|  |   @ApiProperty({ type: JobStatusDto }) | ||||||
|  |   [QueueName.BACKGROUND_TASK]!: JobStatusDto; | ||||||
|  |  | ||||||
|  |   @ApiProperty({ type: JobStatusDto }) | ||||||
|  |   [QueueName.SEARCH]!: JobStatusDto; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ export const newJobRepositoryMock = (): jest.Mocked<IJobRepository> => { | |||||||
|     pause: jest.fn(), |     pause: jest.fn(), | ||||||
|     resume: jest.fn(), |     resume: jest.fn(), | ||||||
|     queue: jest.fn().mockImplementation(() => Promise.resolve()), |     queue: jest.fn().mockImplementation(() => Promise.resolve()), | ||||||
|     isActive: jest.fn(), |     getQueueStatus: jest.fn(), | ||||||
|     getJobCounts: jest.fn(), |     getJobCounts: jest.fn(), | ||||||
|   }; |   }; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ import { | |||||||
|   JobItem, |   JobItem, | ||||||
|   JobName, |   JobName, | ||||||
|   QueueName, |   QueueName, | ||||||
|  |   QueueStatus, | ||||||
| } from '@app/domain'; | } from '@app/domain'; | ||||||
| import { InjectQueue } from '@nestjs/bull'; | import { InjectQueue } from '@nestjs/bull'; | ||||||
| import { Logger } from '@nestjs/common'; | import { Logger } from '@nestjs/common'; | ||||||
| @@ -36,9 +37,13 @@ export class JobRepository implements IJobRepository { | |||||||
|     @InjectQueue(QueueName.SEARCH) private searchIndex: Queue, |     @InjectQueue(QueueName.SEARCH) private searchIndex: Queue, | ||||||
|   ) {} |   ) {} | ||||||
|  |  | ||||||
|   async isActive(name: QueueName): Promise<boolean> { |   async getQueueStatus(name: QueueName): Promise<QueueStatus> { | ||||||
|     const counts = await this.getJobCounts(name); |     const queue = this.queueMap[name]; | ||||||
|     return !!counts.active; |  | ||||||
|  |     return { | ||||||
|  |       isActive: !!(await queue.getActiveCount()), | ||||||
|  |       isPaused: await queue.isPaused(), | ||||||
|  |     }; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   pause(name: QueueName) { |   pause(name: QueueName) { | ||||||
|   | |||||||
							
								
								
									
										74
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										74
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							| @@ -291,52 +291,52 @@ export interface AlbumResponseDto { | |||||||
| export interface AllJobStatusResponseDto { | export interface AllJobStatusResponseDto { | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @type {JobCountsDto} |      * @type {JobStatusDto} | ||||||
|      * @memberof AllJobStatusResponseDto |      * @memberof AllJobStatusResponseDto | ||||||
|      */ |      */ | ||||||
|     'thumbnail-generation-queue': JobCountsDto; |     'thumbnail-generation-queue': JobStatusDto; | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @type {JobCountsDto} |      * @type {JobStatusDto} | ||||||
|      * @memberof AllJobStatusResponseDto |      * @memberof AllJobStatusResponseDto | ||||||
|      */ |      */ | ||||||
|     'metadata-extraction-queue': JobCountsDto; |     'metadata-extraction-queue': JobStatusDto; | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @type {JobCountsDto} |      * @type {JobStatusDto} | ||||||
|      * @memberof AllJobStatusResponseDto |      * @memberof AllJobStatusResponseDto | ||||||
|      */ |      */ | ||||||
|     'video-conversion-queue': JobCountsDto; |     'video-conversion-queue': JobStatusDto; | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @type {JobCountsDto} |      * @type {JobStatusDto} | ||||||
|      * @memberof AllJobStatusResponseDto |      * @memberof AllJobStatusResponseDto | ||||||
|      */ |      */ | ||||||
|     'object-tagging-queue': JobCountsDto; |     'object-tagging-queue': JobStatusDto; | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @type {JobCountsDto} |      * @type {JobStatusDto} | ||||||
|      * @memberof AllJobStatusResponseDto |      * @memberof AllJobStatusResponseDto | ||||||
|      */ |      */ | ||||||
|     'clip-encoding-queue': JobCountsDto; |     'clip-encoding-queue': JobStatusDto; | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @type {JobCountsDto} |      * @type {JobStatusDto} | ||||||
|      * @memberof AllJobStatusResponseDto |      * @memberof AllJobStatusResponseDto | ||||||
|      */ |      */ | ||||||
|     'storage-template-migration-queue': JobCountsDto; |     'storage-template-migration-queue': JobStatusDto; | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @type {JobCountsDto} |      * @type {JobStatusDto} | ||||||
|      * @memberof AllJobStatusResponseDto |      * @memberof AllJobStatusResponseDto | ||||||
|      */ |      */ | ||||||
|     'background-task-queue': JobCountsDto; |     'background-task-queue': JobStatusDto; | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @type {JobCountsDto} |      * @type {JobStatusDto} | ||||||
|      * @memberof AllJobStatusResponseDto |      * @memberof AllJobStatusResponseDto | ||||||
|      */ |      */ | ||||||
|     'search-queue': JobCountsDto; |     'search-queue': JobStatusDto; | ||||||
| } | } | ||||||
| /** | /** | ||||||
|  *  |  *  | ||||||
| @@ -1311,6 +1311,25 @@ export const JobName = { | |||||||
| export type JobName = typeof JobName[keyof typeof JobName]; | export type JobName = typeof JobName[keyof typeof JobName]; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  *  | ||||||
|  |  * @export | ||||||
|  |  * @interface JobStatusDto | ||||||
|  |  */ | ||||||
|  | export interface JobStatusDto { | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {JobCountsDto} | ||||||
|  |      * @memberof JobStatusDto | ||||||
|  |      */ | ||||||
|  |     'jobCounts': JobCountsDto; | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {QueueStatusDto} | ||||||
|  |      * @memberof JobStatusDto | ||||||
|  |      */ | ||||||
|  |     'queueStatus': QueueStatusDto; | ||||||
|  | } | ||||||
| /** | /** | ||||||
|  *  |  *  | ||||||
|  * @export |  * @export | ||||||
| @@ -1467,6 +1486,25 @@ export interface OAuthConfigResponseDto { | |||||||
|      */ |      */ | ||||||
|     'autoLaunch'?: boolean; |     'autoLaunch'?: boolean; | ||||||
| } | } | ||||||
|  | /** | ||||||
|  |  *  | ||||||
|  |  * @export | ||||||
|  |  * @interface QueueStatusDto | ||||||
|  |  */ | ||||||
|  | export interface QueueStatusDto { | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {boolean} | ||||||
|  |      * @memberof QueueStatusDto | ||||||
|  |      */ | ||||||
|  |     'isActive': boolean; | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {boolean} | ||||||
|  |      * @memberof QueueStatusDto | ||||||
|  |      */ | ||||||
|  |     'isPaused': boolean; | ||||||
|  | } | ||||||
| /** | /** | ||||||
|  *  |  *  | ||||||
|  * @export |  * @export | ||||||
| @@ -6270,7 +6308,7 @@ export const JobApiFp = function(configuration?: Configuration) { | |||||||
|          * @param {*} [options] Override http request option. |          * @param {*} [options] Override http request option. | ||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         async sendJobCommand(jobId: JobName, jobCommandDto: JobCommandDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> { |         async sendJobCommand(jobId: JobName, jobCommandDto: JobCommandDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<JobStatusDto>> { | ||||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.sendJobCommand(jobId, jobCommandDto, options); |             const localVarAxiosArgs = await localVarAxiosParamCreator.sendJobCommand(jobId, jobCommandDto, options); | ||||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); |             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||||
|         }, |         }, | ||||||
| @@ -6299,7 +6337,7 @@ export const JobApiFactory = function (configuration?: Configuration, basePath?: | |||||||
|          * @param {*} [options] Override http request option. |          * @param {*} [options] Override http request option. | ||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         sendJobCommand(jobId: JobName, jobCommandDto: JobCommandDto, options?: any): AxiosPromise<void> { |         sendJobCommand(jobId: JobName, jobCommandDto: JobCommandDto, options?: any): AxiosPromise<JobStatusDto> { | ||||||
|             return localVarFp.sendJobCommand(jobId, jobCommandDto, options).then((request) => request(axios, basePath)); |             return localVarFp.sendJobCommand(jobId, jobCommandDto, options).then((request) => request(axios, basePath)); | ||||||
|         }, |         }, | ||||||
|     }; |     }; | ||||||
|   | |||||||
| @@ -109,8 +109,4 @@ input:focus-visible { | |||||||
| 		display: none; | 		display: none; | ||||||
| 		scrollbar-width: none; | 		scrollbar-width: none; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	.job-play-button { |  | ||||||
| 		@apply h-full flex flex-col place-items-center place-content-center px-8 text-gray-600 transition-all hover:bg-immich-primary hover:text-white dark:text-gray-200 dark:hover:bg-immich-dark-primary text-sm dark:hover:text-black gap-2; |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,21 @@ | |||||||
|  | <script lang="ts" context="module"> | ||||||
|  | 	export type Colors = 'light-gray' | 'gray'; | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <script lang="ts"> | ||||||
|  | 	export let color: Colors; | ||||||
|  |  | ||||||
|  | 	const colorClasses: Record<Colors, string> = { | ||||||
|  | 		'light-gray': 'bg-gray-300/90 dark:bg-gray-600/90', | ||||||
|  | 		gray: 'bg-gray-300 dark:bg-gray-600' | ||||||
|  | 	}; | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <button | ||||||
|  | 	class="h-full flex gap-2 flex-col place-items-center place-content-center px-8 text-gray-600 transition-colors hover:bg-immich-primary hover:text-white dark:text-gray-200 dark:hover:bg-immich-dark-primary text-xs dark:hover:text-black {colorClasses[ | ||||||
|  | 		color | ||||||
|  | 	]}" | ||||||
|  | 	on:click | ||||||
|  | > | ||||||
|  | 	<slot /> | ||||||
|  | </button> | ||||||
| @@ -0,0 +1,16 @@ | |||||||
|  | <script lang="ts" context="module"> | ||||||
|  | 	export type Color = 'success' | 'warning'; | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <script lang="ts"> | ||||||
|  | 	export let color: Color; | ||||||
|  |  | ||||||
|  | 	const colorClasses: Record<Color, string> = { | ||||||
|  | 		success: 'bg-green-500/70 text-gray-900 dark:bg-green-700/90 dark:text-gray-100', | ||||||
|  | 		warning: 'bg-orange-400/70 text-gray-900 dark:bg-orange-900 dark:text-gray-100' | ||||||
|  | 	}; | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <div class="w-full text-center text-sm p-2 {colorClasses[color]}"> | ||||||
|  | 	<slot /> | ||||||
|  | </div> | ||||||
| @@ -4,40 +4,49 @@ | |||||||
| 	import Pause from 'svelte-material-icons/Pause.svelte'; | 	import Pause from 'svelte-material-icons/Pause.svelte'; | ||||||
| 	import FastForward from 'svelte-material-icons/FastForward.svelte'; | 	import FastForward from 'svelte-material-icons/FastForward.svelte'; | ||||||
| 	import AllInclusive from 'svelte-material-icons/AllInclusive.svelte'; | 	import AllInclusive from 'svelte-material-icons/AllInclusive.svelte'; | ||||||
|  | 	import Close from 'svelte-material-icons/Close.svelte'; | ||||||
| 	import { locale } from '$lib/stores/preferences.store'; | 	import { locale } from '$lib/stores/preferences.store'; | ||||||
| 	import { createEventDispatcher } from 'svelte'; | 	import { createEventDispatcher } from 'svelte'; | ||||||
| 	import { JobCommand, JobCommandDto, JobCountsDto } from '@api'; | 	import { JobCommand, JobCommandDto, JobCountsDto, QueueStatusDto } from '@api'; | ||||||
| 	import Badge from '$lib/components/elements/badge.svelte'; | 	import Badge from '$lib/components/elements/badge.svelte'; | ||||||
|  | 	import JobTileButton from './job-tile-button.svelte'; | ||||||
|  | 	import JobTileStatus from './job-tile-status.svelte'; | ||||||
|  |  | ||||||
| 	export let title: string; | 	export let title: string; | ||||||
| 	export let subtitle: string | undefined = undefined; | 	export let subtitle: string | undefined = undefined; | ||||||
| 	export let jobCounts: JobCountsDto; | 	export let jobCounts: JobCountsDto; | ||||||
|  | 	export let queueStatus: QueueStatusDto; | ||||||
| 	export let allowForceCommand = true; | 	export let allowForceCommand = true; | ||||||
|  |  | ||||||
| 	$: isRunning = jobCounts.active > 0 || jobCounts.waiting > 0; | 	$: waitingCount = jobCounts.waiting + jobCounts.paused + jobCounts.delayed; | ||||||
| 	$: waitingCount = jobCounts.waiting + jobCounts.paused; | 	$: isIdle = !queueStatus.isActive && !queueStatus.isPaused; | ||||||
| 	$: isPause = jobCounts.paused > 0; |  | ||||||
|  |  | ||||||
| 	const dispatch = createEventDispatcher<{ command: JobCommandDto }>(); | 	const dispatch = createEventDispatcher<{ command: JobCommandDto }>(); | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <div | <div class="flex bg-gray-100 dark:bg-immich-dark-gray rounded-3xl overflow-hidden"> | ||||||
| 	class="flex justify-between rounded-3xl bg-gray-100 dark:bg-immich-dark-gray transition-all | 	<div class="flex flex-col w-full"> | ||||||
|   {isRunning ? 'dark:bg-immich-primary/30 bg-immich-primary/20' : ''}  | 		{#if queueStatus.isPaused} | ||||||
|   {isPause ? 'dark:bg-yellow-100/30 bg-yellow-500/20' : ''}" | 			<JobTileStatus color="warning">Paused</JobTileStatus> | ||||||
| > | 		{:else if queueStatus.isActive} | ||||||
| 	<div id="job-info" class="w-full p-9"> | 			<JobTileStatus color="success">Active</JobTileStatus> | ||||||
| 		<div class="flex flex-col gap-2 "> | 		{/if} | ||||||
|  | 		<div class="flex flex-col gap-2 p-9"> | ||||||
| 			<div | 			<div | ||||||
| 				class="flex items-center gap-4 text-xl font-semibold text-immich-primary dark:text-immich-dark-primary" | 				class="flex items-center gap-4 text-xl font-semibold text-immich-primary dark:text-immich-dark-primary" | ||||||
| 			> | 			> | ||||||
| 				<span>{title.toUpperCase()}</span> | 				<span>{title.toUpperCase()}</span> | ||||||
| 				<div class="flex gap-2"> | 				<div class="flex gap-2"> | ||||||
| 					{#if jobCounts.failed > 0} | 					{#if jobCounts.failed > 0} | ||||||
| 						<Badge color="danger"> | 						<Badge color="primary"> | ||||||
| 							{jobCounts.failed.toLocaleString($locale)} failed | 							{jobCounts.failed.toLocaleString($locale)} failed | ||||||
| 						</Badge> | 						</Badge> | ||||||
| 					{/if} | 					{/if} | ||||||
|  | 					{#if jobCounts.delayed > 0} | ||||||
|  | 						<Badge color="secondary"> | ||||||
|  | 							{jobCounts.delayed.toLocaleString($locale)} delayed | ||||||
|  | 						</Badge> | ||||||
|  | 					{/if} | ||||||
| 				</div> | 				</div> | ||||||
| 			</div> | 			</div> | ||||||
|  |  | ||||||
| @@ -69,43 +78,54 @@ | |||||||
| 			</div> | 			</div> | ||||||
| 		</div> | 		</div> | ||||||
| 	</div> | 	</div> | ||||||
| 	<div id="job-action" class="flex flex-col rounded-r-3xl w-32 overflow-hidden"> | 	<div class="flex flex-col w-32 overflow-hidden"> | ||||||
| 		{#if isRunning} | 		{#if !isIdle} | ||||||
| 			<button | 			{#if waitingCount > 0} | ||||||
| 				class="job-play-button bg-gray-300/90 dark:bg-gray-600/90" | 				<JobTileButton | ||||||
| 				on:click={() => dispatch('command', { command: JobCommand.Pause, force: false })} | 					color="gray" | ||||||
| 			> | 					on:click={() => dispatch('command', { command: JobCommand.Empty, force: false })} | ||||||
| 				<Pause size="48" /> PAUSE | 				> | ||||||
| 			</button> | 					<Close size="24" /> CLEAR | ||||||
| 		{:else if jobCounts.paused > 0} | 				</JobTileButton> | ||||||
| 			<button | 			{/if} | ||||||
| 				class="job-play-button bg-gray-300 dark:bg-gray-600/90" | 			{#if queueStatus.isPaused} | ||||||
| 				on:click={() => dispatch('command', { command: JobCommand.Resume, force: false })} | 				<JobTileButton | ||||||
| 			> | 					color="light-gray" | ||||||
| 				<span class=" {isPause ? 'animate-pulse' : ''}"> | 					on:click={() => dispatch('command', { command: JobCommand.Resume, force: false })} | ||||||
| 					<FastForward size="48" /> RESUME | 				> | ||||||
| 				</span> | 					{@const size = waitingCount > 0 ? '24' : '48'} | ||||||
| 			</button> |  | ||||||
|  | 					<!-- size property is not reactive, so have to use width and height --> | ||||||
|  | 					<FastForward width={size} height={size} /> RESUME | ||||||
|  | 				</JobTileButton> | ||||||
|  | 			{:else} | ||||||
|  | 				<JobTileButton | ||||||
|  | 					color="light-gray" | ||||||
|  | 					on:click={() => dispatch('command', { command: JobCommand.Pause, force: false })} | ||||||
|  | 				> | ||||||
|  | 					<Pause size="24" /> PAUSE | ||||||
|  | 				</JobTileButton> | ||||||
|  | 			{/if} | ||||||
| 		{:else if allowForceCommand} | 		{:else if allowForceCommand} | ||||||
| 			<button | 			<JobTileButton | ||||||
| 				class="job-play-button bg-gray-300 dark:bg-gray-600" | 				color="gray" | ||||||
| 				on:click={() => dispatch('command', { command: JobCommand.Start, force: true })} | 				on:click={() => dispatch('command', { command: JobCommand.Start, force: true })} | ||||||
| 			> | 			> | ||||||
| 				<AllInclusive size="18" /> ALL | 				<AllInclusive size="24" /> ALL | ||||||
| 			</button> | 			</JobTileButton> | ||||||
| 			<button | 			<JobTileButton | ||||||
| 				class="job-play-button bg-gray-300/90 dark:bg-gray-600/90" | 				color="light-gray" | ||||||
| 				on:click={() => dispatch('command', { command: JobCommand.Start, force: false })} | 				on:click={() => dispatch('command', { command: JobCommand.Start, force: false })} | ||||||
| 			> | 			> | ||||||
| 				<SelectionSearch size="18" /> MISSING | 				<SelectionSearch size="24" /> MISSING | ||||||
| 			</button> | 			</JobTileButton> | ||||||
| 		{:else} | 		{:else} | ||||||
| 			<button | 			<JobTileButton | ||||||
| 				class="job-play-button bg-gray-300/90 dark:bg-gray-600/90" | 				color="light-gray" | ||||||
| 				on:click={() => dispatch('command', { command: JobCommand.Start, force: false })} | 				on:click={() => dispatch('command', { command: JobCommand.Start, force: false })} | ||||||
| 			> | 			> | ||||||
| 				<Play size="48" /> START | 				<Play size="48" /> START | ||||||
| 			</button> | 			</JobTileButton> | ||||||
| 		{/if} | 		{/if} | ||||||
| 	</div> | 	</div> | ||||||
| </div> | </div> | ||||||
|   | |||||||
| @@ -1,4 +1,8 @@ | |||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
|  | 	import { | ||||||
|  | 		notificationController, | ||||||
|  | 		NotificationType | ||||||
|  | 	} from '$lib/components/shared-components/notification/notification'; | ||||||
| 	import { handleError } from '$lib/utils/handle-error'; | 	import { handleError } from '$lib/utils/handle-error'; | ||||||
| 	import { AllJobStatusResponseDto, api, JobCommand, JobCommandDto, JobName } from '@api'; | 	import { AllJobStatusResponseDto, api, JobCommand, JobCommandDto, JobName } from '@api'; | ||||||
| 	import type { ComponentType } from 'svelte'; | 	import type { ComponentType } from 'svelte'; | ||||||
| @@ -49,21 +53,15 @@ | |||||||
| 		const title = jobDetails[jobId]?.title; | 		const title = jobDetails[jobId]?.title; | ||||||
|  |  | ||||||
| 		try { | 		try { | ||||||
| 			await api.jobApi.sendJobCommand(jobId, jobCommand); | 			const { data } = await api.jobApi.sendJobCommand(jobId, jobCommand); | ||||||
|  | 			jobs[jobId] = data; | ||||||
|  |  | ||||||
| 			// TODO: Return actual job status from server and use that. |  | ||||||
| 			switch (jobCommand.command) { | 			switch (jobCommand.command) { | ||||||
| 				case JobCommand.Start: | 				case JobCommand.Empty: | ||||||
| 					jobs[jobId].active += 1; | 					notificationController.show({ | ||||||
| 					break; | 						message: `Cleared jobs for: ${title}`, | ||||||
| 				case JobCommand.Resume: | 						type: NotificationType.Info | ||||||
| 					jobs[jobId].active += 1; | 					}); | ||||||
| 					jobs[jobId].paused = 0; |  | ||||||
| 					break; |  | ||||||
| 				case JobCommand.Pause: |  | ||||||
| 					jobs[jobId].paused += 1; |  | ||||||
| 					jobs[jobId].active = 0; |  | ||||||
| 					jobs[jobId].waiting = 0; |  | ||||||
| 					break; | 					break; | ||||||
| 			} | 			} | ||||||
| 		} catch (error) { | 		} catch (error) { | ||||||
| @@ -74,12 +72,14 @@ | |||||||
|  |  | ||||||
| <div class="flex flex-col gap-7"> | <div class="flex flex-col gap-7"> | ||||||
| 	{#each jobDetailsArray as [jobName, { title, subtitle, allowForceCommand, component }]} | 	{#each jobDetailsArray as [jobName, { title, subtitle, allowForceCommand, component }]} | ||||||
|  | 		{@const { jobCounts, queueStatus } = jobs[jobName]} | ||||||
| 		<JobTile | 		<JobTile | ||||||
| 			{title} | 			{title} | ||||||
| 			{subtitle} | 			{subtitle} | ||||||
| 			{allowForceCommand} | 			{allowForceCommand} | ||||||
|  | 			{jobCounts} | ||||||
|  | 			{queueStatus} | ||||||
| 			on:command={({ detail }) => runJob(jobName, detail)} | 			on:command={({ detail }) => runJob(jobName, detail)} | ||||||
| 			jobCounts={jobs[jobName]} |  | ||||||
| 		> | 		> | ||||||
| 			<svelte:component this={component} /> | 			<svelte:component this={component} /> | ||||||
| 		</JobTile> | 		</JobTile> | ||||||
|   | |||||||
| @@ -1,27 +1,25 @@ | |||||||
| <script lang="ts" context="module"> | <script lang="ts" context="module"> | ||||||
| 	export type BadgeColor = 'primary' | 'dark' | 'warning' | 'success' | 'danger'; | 	export type Color = 'primary' | 'secondary'; | ||||||
| 	export type BadgeRounded = false | true | 'full'; | 	export type Rounded = false | true | 'full'; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| 	export let color: BadgeColor = 'primary'; | 	export let color: Color = 'primary'; | ||||||
| 	export let rounded: BadgeRounded = true; | 	export let rounded: Rounded = true; | ||||||
|  |  | ||||||
| 	const colorClasses: { [Key in BadgeColor]: string } = { | 	const colorClasses: Record<Color, string> = { | ||||||
| 		primary: | 		primary: | ||||||
| 			'text-gray-100 dark:text-immich-dark-gray bg-immich-primary dark:bg-immich-dark-primary', | 			'text-gray-100 dark:text-immich-dark-gray bg-immich-primary dark:bg-immich-dark-primary', | ||||||
| 		dark: 'text-neutral-50 dark:text-neutral-50 bg-neutral-900 dark:bg-neutral-900', | 		secondary: | ||||||
| 		warning: 'text-yellow-900 bg-yellow-200', | 			'text-immich-dark-bg dark:text-immich-gray dark:bg-gray-600 bg-gray-300 dark:text-immich-gray' | ||||||
| 		success: 'text-green-900 bg-green-200', |  | ||||||
| 		danger: 'text-red-900 bg-red-200' |  | ||||||
| 	}; | 	}; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <span | <span | ||||||
| 	class="inline-block h-min whitespace-nowrap px-[0.65em] pt-[0.35em] pb-[0.25em] text-center align-baseline text-[0.65em] font-bold leading-none {colorClasses[ | 	class="inline-block h-min whitespace-nowrap px-4 pt-[0.55em] pb-[0.55em] text-center align-baseline text-xs leading-none {colorClasses[ | ||||||
| 		color | 		color | ||||||
| 	]}" | 	]}" | ||||||
| 	class:rounded={rounded === true} | 	class:rounded-md={rounded === true} | ||||||
| 	class:rounded-full={rounded === 'full'} | 	class:rounded-full={rounded === 'full'} | ||||||
| > | > | ||||||
| 	<slot /> | 	<slot /> | ||||||
|   | |||||||
| @@ -5,9 +5,10 @@ | |||||||
| 	import type { PageData } from './$types'; | 	import type { PageData } from './$types'; | ||||||
|  |  | ||||||
| 	export let data: PageData; | 	export let data: PageData; | ||||||
| 	let jobs = data.jobs; |  | ||||||
| 	let timer: NodeJS.Timer; | 	let timer: NodeJS.Timer; | ||||||
|  |  | ||||||
|  | 	$: jobs = data.jobs; | ||||||
|  |  | ||||||
| 	const load = async () => { | 	const load = async () => { | ||||||
| 		const { data } = await api.jobApi.getAllJobsStatus(); | 		const { data } = await api.jobApi.getAllJobsStatus(); | ||||||
| 		jobs = data; | 		jobs = data; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user