diff --git a/src/main/java/com/magamochi/content/controller/ContentController.java b/src/main/java/com/magamochi/content/controller/ContentController.java index 68d25fa..1399669 100644 --- a/src/main/java/com/magamochi/content/controller/ContentController.java +++ b/src/main/java/com/magamochi/content/controller/ContentController.java @@ -1,11 +1,7 @@ package com.magamochi.content.controller; import com.magamochi.common.model.dto.DefaultResponseDTO; -import com.magamochi.content.model.dto.FileImportRequestDTO; -import com.magamochi.content.model.dto.MangaContentDTO; -import com.magamochi.content.model.dto.MangaContentImagesDTO; -import com.magamochi.content.model.dto.PresignedImportRequestDTO; -import com.magamochi.content.model.dto.PresignedImportResponseDTO; +import com.magamochi.content.model.dto.*; import com.magamochi.content.model.enumeration.ContentArchiveFileType; import com.magamochi.content.service.ContentDownloadService; import com.magamochi.content.service.ContentImportService; @@ -107,4 +103,14 @@ public class ContentController { @RequestBody PresignedImportRequestDTO request) { return DefaultResponseDTO.ok(contentImportService.requestPresignedImport(request)); } + + @Operation( + summary = "Get a list of manga import jobs", + description = "Returns a list of manga import jobs.", + tags = {"Content"}, + operationId = "getMangaImportJobs") + @GetMapping(value = "/import/jobs") + public DefaultResponseDTO> requestPresignedImport() { + return DefaultResponseDTO.ok(contentImportService.getImportJobs()); + } } diff --git a/src/main/java/com/magamochi/content/model/dto/MangaImportJobDTO.java b/src/main/java/com/magamochi/content/model/dto/MangaImportJobDTO.java new file mode 100644 index 0000000..4928186 --- /dev/null +++ b/src/main/java/com/magamochi/content/model/dto/MangaImportJobDTO.java @@ -0,0 +1,31 @@ +package com.magamochi.content.model.dto; + +import com.magamochi.content.model.entity.MangaImportJob; +import com.magamochi.content.model.enumeration.ImportJobStatus; +import java.time.Instant; + +public record MangaImportJobDTO( + Long id, + ImportJobStatus status, + Long malId, + Long aniListId, + String filename, + String s3Key, + Instant createdAt, + Instant updatedAt, + String errorMessage, + String errorStackTrace) { + public static MangaImportJobDTO from(MangaImportJob mangaImportJob) { + return new MangaImportJobDTO( + mangaImportJob.getId(), + mangaImportJob.getStatus(), + mangaImportJob.getMalId(), + mangaImportJob.getAniListId(), + mangaImportJob.getOriginalFilename(), + mangaImportJob.getS3FileKey(), + mangaImportJob.getCreatedAt(), + mangaImportJob.getUpdatedAt(), + mangaImportJob.getErrorMessage(), + mangaImportJob.getErrorStacktrace()); + } +} diff --git a/src/main/java/com/magamochi/content/model/entity/MangaImportJob.java b/src/main/java/com/magamochi/content/model/entity/MangaImportJob.java index 0e4f885..b2831ec 100644 --- a/src/main/java/com/magamochi/content/model/entity/MangaImportJob.java +++ b/src/main/java/com/magamochi/content/model/entity/MangaImportJob.java @@ -30,6 +30,10 @@ public class MangaImportJob { @Enumerated(EnumType.STRING) private ImportJobStatus status; + private String errorMessage; + + private String errorStacktrace; + @CreationTimestamp private Instant createdAt; @UpdateTimestamp private Instant updatedAt; diff --git a/src/main/java/com/magamochi/content/queue/consumer/FileImportConsumer.java b/src/main/java/com/magamochi/content/queue/consumer/FileImportConsumer.java index 55de27e..1f85bff 100644 --- a/src/main/java/com/magamochi/content/queue/consumer/FileImportConsumer.java +++ b/src/main/java/com/magamochi/content/queue/consumer/FileImportConsumer.java @@ -2,8 +2,11 @@ package com.magamochi.content.queue.consumer; import static java.util.Objects.nonNull; +import com.magamochi.content.model.enumeration.ImportJobStatus; import com.magamochi.content.queue.command.FileImportCommand; import com.magamochi.content.service.ContentImportService; +import java.io.PrintWriter; +import java.io.StringWriter; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.amqp.rabbit.annotation.RabbitListener; @@ -24,14 +27,16 @@ public class FileImportConsumer { if (nonNull(command.mangaImportJobId())) { contentImportService.updateJobStatus( - command.mangaImportJobId(), - com.magamochi.content.model.enumeration.ImportJobStatus.SUCCESS); + command.mangaImportJobId(), ImportJobStatus.SUCCESS, null, null); } } catch (Exception e) { if (nonNull(command.mangaImportJobId())) { + var sw = new StringWriter(); + var pw = new PrintWriter(sw); + e.printStackTrace(pw); + contentImportService.updateJobStatus( - command.mangaImportJobId(), - com.magamochi.content.model.enumeration.ImportJobStatus.FAILED); + command.mangaImportJobId(), ImportJobStatus.FAILED, e.getMessage(), sw.toString()); } throw e; diff --git a/src/main/java/com/magamochi/content/service/ContentImportService.java b/src/main/java/com/magamochi/content/service/ContentImportService.java index cce72a5..155b4e6 100644 --- a/src/main/java/com/magamochi/content/service/ContentImportService.java +++ b/src/main/java/com/magamochi/content/service/ContentImportService.java @@ -8,6 +8,7 @@ import com.magamochi.catalog.service.MangaContentProviderService; import com.magamochi.catalog.service.MangaResolutionService; import com.magamochi.common.exception.UnprocessableException; import com.magamochi.common.model.enumeration.ContentType; +import com.magamochi.content.model.dto.MangaImportJobDTO; import com.magamochi.content.model.dto.PresignedImportRequestDTO; import com.magamochi.content.model.dto.PresignedImportResponseDTO; import com.magamochi.content.model.entity.MangaContent; @@ -119,12 +120,15 @@ public class ContentImportService { } @Transactional(propagation = Propagation.REQUIRES_NEW) - public void updateJobStatus(Long jobId, ImportJobStatus status) { + public void updateJobStatus( + Long jobId, ImportJobStatus status, String errorMessage, String errorStacktrace) { mangaImportJobRepository .findById(jobId) .ifPresent( job -> { job.setStatus(status); + job.setErrorMessage(errorMessage); + job.setErrorStacktrace(errorStacktrace); mangaImportJobRepository.save(job); }); } @@ -202,4 +206,8 @@ public class ContentImportService { .build()); } } + + public List getImportJobs() { + return mangaImportJobRepository.findAll().stream().map(MangaImportJobDTO::from).toList(); + } } diff --git a/src/main/java/com/magamochi/content/task/PendingImportScannerTask.java b/src/main/java/com/magamochi/content/task/PendingImportScannerTask.java index 554e713..0eac633 100644 --- a/src/main/java/com/magamochi/content/task/PendingImportScannerTask.java +++ b/src/main/java/com/magamochi/content/task/PendingImportScannerTask.java @@ -8,6 +8,8 @@ import com.magamochi.content.queue.command.FileImportCommand; import com.magamochi.content.queue.producer.FileImportProducer; import com.magamochi.image.service.S3Service; import com.magamochi.ingestion.service.ContentProviderService; +import java.io.PrintWriter; +import java.io.StringWriter; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.scheduling.annotation.Scheduled; @@ -47,8 +49,14 @@ public class PendingImportScannerTask { fileImportProducer.sendFileImportCommand( new FileImportCommand(mangaContentProvider.getId(), job.getS3FileKey(), job.getId())); } catch (Exception e) { + var sw = new StringWriter(); + var pw = new PrintWriter(sw); + e.printStackTrace(pw); + log.error("Failed to enqueue job {}", job.getId(), e); job.setStatus(ImportJobStatus.FAILED); + job.setErrorMessage(e.getMessage()); + job.setErrorStacktrace(sw.toString()); mangaImportJobRepository.save(job); } } diff --git a/src/main/java/com/magamochi/ingestion/model/dto/ContentProviderListDTO.java b/src/main/java/com/magamochi/ingestion/model/dto/ContentProviderListDTO.java index b78d549..a09cab0 100644 --- a/src/main/java/com/magamochi/ingestion/model/dto/ContentProviderListDTO.java +++ b/src/main/java/com/magamochi/ingestion/model/dto/ContentProviderListDTO.java @@ -11,9 +11,13 @@ public record ContentProviderListDTO(@NotNull List providers contentProviders.stream().map(ContentProviderDTO::from).toList()); } - public record ContentProviderDTO(long id, @NotBlank String name) { + public record ContentProviderDTO(long id, @NotBlank String name, String url, boolean active) { public static ContentProviderDTO from(ContentProvider contentProvider) { - return new ContentProviderDTO(contentProvider.getId(), contentProvider.getName()); + return new ContentProviderDTO( + contentProvider.getId(), + contentProvider.getName(), + contentProvider.getUrl(), + contentProvider.isActive()); } } } diff --git a/src/main/java/com/magamochi/ingestion/model/entity/ContentProvider.java b/src/main/java/com/magamochi/ingestion/model/entity/ContentProvider.java index 26ab447..267bf17 100644 --- a/src/main/java/com/magamochi/ingestion/model/entity/ContentProvider.java +++ b/src/main/java/com/magamochi/ingestion/model/entity/ContentProvider.java @@ -22,6 +22,8 @@ public class ContentProvider { private String name; + private String url; + private boolean active; private Boolean supportsContentFetch; diff --git a/src/main/resources/db/migration/V0008__MANGA_IMPORT_JOB.sql b/src/main/resources/db/migration/V0008__MANGA_IMPORT_JOB.sql new file mode 100644 index 0000000..cd916ad --- /dev/null +++ b/src/main/resources/db/migration/V0008__MANGA_IMPORT_JOB.sql @@ -0,0 +1,3 @@ +ALTER TABLE manga_import_job + ADD COLUMN error_message VARCHAR, + ADD COLUMN error_stacktrace VARCHAR;