diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index aee173efb8..e69de29bb2 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,9 +0,0 @@ -/ui-packages/ @paulovmr - -/runtime-tools-quarkus-extension-parent/ @paulovmr - -/kogito-quarkus-serverless-workflow-devui-parent/ @paulovmr - -/management-console/ @paulovmr - -/task-console/ @paulovmr \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit index 763c895725..6700f51282 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,2 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" - -cd ui-packages && npx lint-staged diff --git a/README.md b/README.md index a2a0f74fea..45cf6629c2 100644 --- a/README.md +++ b/README.md @@ -15,57 +15,6 @@ git clone git@github.com:kiegroup/kogito-apps.git > ``` > git clone https://github.com/kiegroup/kogito-apps.git > ``` - -- Install Node and NPM package manager - -See detailed instructions [here](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) for your OS. - -- Install [pnpm](https://pnpm.io/) -```bash -cd kogito-apps/ui-packages -npm install -g pnpm -``` - -- Install projects dependencies using pnpm -```bash -cd kogito-apps/ui-packages -pnpm install -``` - -- Build with pnpm: -```bash -cd kogito-apps/ui-packages -pnpm run init - -#prod -pnpm run build:prod - -# dev -pnpm run build # skips integration tests and production packing -``` - -> Final artifacts will be on `packages/*/dist` directories. - -## Management Console - -For detailed instructions on how to develop and run the Management Console, please check instructions on the specific -[README](./ui-packages/packages/management-console/README.md) file. - -## `ui-packages` dependencies - -`ui-packages` are managed with [pnpm Workspaces](https://pnpm.io/workspaces). Dependencies shared between packages are listed in the top-level [`package.json`](./ui-packages/package.json). - -A `locktt` npm script relying on [lock-treatment-tool](https://github.com/Ginxo/lock-treatment-tool) is available to allow the usage of a private npm registry during building. - -`locktt` replaces the host from [`ui-packages/pnpm-lock`](./ui-packages/pnpm-lock) resolved field with the custom registry. It is set to run just before the execution of `pnpm install`. See [`ui-packages/pom.xml`](./ui-packages/pom.xml) for further details. - -## Skipping frontend build - -To skip the frontend build when running maven, simply execute Maven with the following parameters - -```bash -mvn clean install -Dskip.ui.build -Dskip.ui.deps -``` ## Getting Help ### Issues @@ -77,4 +26,4 @@ mvn clean install -Dskip.ui.build -Dskip.ui.deps ### Requests - Do you have a feature/enhancement request? - - Please open a new thread in the [Kogito stream](https://kie.zulipchat.com/#narrow/stream/232676-kogito) of the KIE Zulip chat to start a discussion there. + - Please open a new thread in the [Kogito stream](https://kie.zulipchat.com/#narrow/stream/232676-kogito) of the KIE Zulip chat to start a discussion there. \ No newline at end of file diff --git a/apps-integration-tests/integration-tests-trusty-audit/README.md b/apps-integration-tests/integration-tests-trusty-audit/README.md deleted file mode 100644 index 7bade54b10..0000000000 --- a/apps-integration-tests/integration-tests-trusty-audit/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# Trusty AI Console - end to end tests - -This project runs Trusty AI Console and verifies it via Cypress E2E test suite which is located in [UI Packages](../../ui-packages/packages/trusty/it-tests). - -## Requirements - -- docker version >= 19.03.12 -- java version >= 11 -- maven version >= 3.6.3 - -Note: also previous versions of `docker` might work, but they were not tested. -Note: see [Manage Docker as a non-root user](https://docs.docker.com/engine/install/linux-postinstall/) - -## Perform e2e test suite - -The e2e test suite is performed during a build of whole project. - -Go to the root folder of this project (kogito-apps) and run `mvn install` to build and performed all tests. - -Note: We recommend running the e2e test suite after the `kogito-apps-ui-packages` module is built in your environment. -Otherwise, be sure that your environment is clean and contains all necessary tools. - -In case that you want to run only the E2E test be sure that docker contains all images which are mentioned in the pom.xml file. - -``` -docker images -``` - -Use `mvn install -pl :integration-tests-trusty-audit -am` if you miss any docker image. -Go to this folder (integration-tests-trusty-audit) and perform `mvn install` directly in this module. - -In case that you want to run just services (without test suite) to investigate problems then perform `mvn docker:run` when you interrupt this execution all services are stopped and removed. See [DMP docs](https://dmp.fabric8.io/#maven-goals). - -## Clear docker - -Remove obsolete image: - -``` -docker rmi $(docker images 'org.kie.kogito/integration-tests-trusty-service-quarkus' -a -q) -``` - -Or clear all docker images: - -``` -docker system prune --all -docker volume prune -docker network rm trusty-nw -``` - -Sometimes it seems that docker-maven-plugin does not stop all images so call - -``` -docker rm -fv $(docker ps -aq) -``` -It is an issue of DMP [see](https://github.com/fabric8io/docker-maven-plugin/issues/552) \ No newline at end of file diff --git a/apps-integration-tests/integration-tests-trusty-audit/pom.xml b/apps-integration-tests/integration-tests-trusty-audit/pom.xml deleted file mode 100644 index 61e2d26cdd..0000000000 --- a/apps-integration-tests/integration-tests-trusty-audit/pom.xml +++ /dev/null @@ -1,348 +0,0 @@ - - - 4.0.0 - - org.kie.kogito - apps-integration-tests - 999-SNAPSHOT - - - integration-tests-trusty-audit - Kogito Apps :: Integration Tests :: Trusty Audit UI - pom - - - to-be-changed-in-submodules - 0.40.3 - - - - - - org.kie.kogito - explainability-service-messaging - ${project.version} - pom - provided - - - * - * - - - - - org.kie.kogito - integration-tests-trusty-service-quarkus - ${project.version} - pom - provided - - - * - * - - - - - org.kie.kogito - trusty-service-infinispan - ${project.version} - pom - provided - - - * - * - - - - - org.kie.kogito - trusty-ui - ${project.version} - pom - provided - - - * - * - - - - - - - - - com.github.eirslett - frontend-maven-plugin - ${version.frontend.plugin} - - ${project.root.dir}/ui-packages - - - - - - pnpm e2e test - - pnpm - - integration-test - - ${skip.ui.it} - run test:e2e:trusty - - - - - - io.fabric8 - docker-maven-plugin - ${version.dmp.fabric8} - - true - - - zookeeper - wurstmeister/zookeeper:latest - - - 2181:2181 - - - /tmp/logs - - - custom - trusty-nw - - - %a - ${project.build.directory}/zookeeper.log - - - - - kafka - ${container.image.wurstmeister.kafka} - - - zookeeper - - - 9092:9092 - - 9093:9093 - - - /tmp/logs - INSIDE://kafka-1:9093,OUTSIDE://localhost:9092 - INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT - INSIDE://0.0.0.0:9093,OUTSIDE://0.0.0.0:9092 - zookeeper-1:2181 - INSIDE - - - custom - trusty-nw - - - %a - ${project.build.directory}/kafka.log - - - - - infinispan - ${container.image.infinispan} - - - 11222:11222 - - -c infinispan-demo.xml - - custom - trusty-nw - - - - ./src/test/resources/infinispan/infinispan.xml:/opt/infinispan/server/conf/infinispan-demo.xml:z - - - - %a - ${project.build.directory}/infinispan.log - - - - - http://localhost:11222/ - 200..401 - GET - - - - - - kogito-app - ${container.image.integration-tests-trusty-service-quarkus} - - - - kafka - - - 8080:8080 - - - kafka-1:9093 - http://integration-tests-trusty-service-quarkus-1:8080 - - - custom - trusty-nw - - - %a - ${project.build.directory}/kogito-app.log - - - - - explainability - ${container.image.explainability-service-messaging} - - - kafka - kogito-app - - - 1336:8080 - - - kafka-1:9093 - - - custom - trusty-nw - - - %a - ${project.build.directory}/explainability.log - - - - - http://localhost:1336/q/health/started - 200 - GET - - - - - - trusty - ${container.image.trusty-service-infinispan} - - - - kafka - infinispan - - - 1337:8080 - - - kafka-1:9093 - server-1:11222 - "false" - "true" - - - custom - trusty-nw - - - %a - ${project.build.directory}/trusty.log - - - - - trusty-ui - ${container.image.trusty-ui} - - - kafka - - - 1338:8080 - - - http://localhost:1337 - - - custom - trusty-nw - - - %a - ${project.build.directory}/trusty-ui.log - - - - - http://localhost:1338/q/health/started - GET - 200 - - - - - - - - - start - pre-integration-test - - start - - - - stop - post-integration-test - - stop - - - - - - - \ No newline at end of file diff --git a/apps-integration-tests/integration-tests-trusty-audit/src/test/resources/infinispan/infinispan.xml b/apps-integration-tests/integration-tests-trusty-audit/src/test/resources/infinispan/infinispan.xml deleted file mode 100644 index 1201a8dc47..0000000000 --- a/apps-integration-tests/integration-tests-trusty-audit/src/test/resources/infinispan/infinispan.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/apps-integration-tests/pom.xml b/apps-integration-tests/pom.xml index 724fe26259..610c6427f6 100644 --- a/apps-integration-tests/pom.xml +++ b/apps-integration-tests/pom.xml @@ -37,7 +37,6 @@ integration-tests-data-index-service integration-tests-jobs-service - integration-tests-trusty-audit integration-tests-trusty-service diff --git a/data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/model/ProcessDefinitionEntity.java b/data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/model/ProcessDefinitionEntity.java index dd2db761a6..63e965a2a0 100644 --- a/data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/model/ProcessDefinitionEntity.java +++ b/data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/model/ProcessDefinitionEntity.java @@ -205,10 +205,7 @@ public String toString() { ", version='" + version + '\'' + ", name='" + name + '\'' + ", type='" + type + '\'' + - ", roles=" + roles + - ", addons=" + addons + ", endpoint='" + endpoint + '\'' + - ", nodes='" + nodes + '\'' + '}'; } } diff --git a/data-index/kogito-addons-quarkus-data-index/kogito-addons-quarkus-data-index-infinispan/integration-tests-sw/pom.xml b/data-index/kogito-addons-quarkus-data-index/kogito-addons-quarkus-data-index-infinispan/integration-tests-sw/pom.xml index 120bc1ac7c..a0ac7ca881 100644 --- a/data-index/kogito-addons-quarkus-data-index/kogito-addons-quarkus-data-index-infinispan/integration-tests-sw/pom.xml +++ b/data-index/kogito-addons-quarkus-data-index/kogito-addons-quarkus-data-index-infinispan/integration-tests-sw/pom.xml @@ -51,24 +51,6 @@ org.kie kie-addons-quarkus-persistence-infinispan - - org.apache.kie.sonataflow - sonataflow-quarkus-devui - - - - org.apache.kie.sonataflow - sonataflow-quarkus-devui-deployment - ${project.version} - pom - test - - - * - * - - - io.quarkus quarkus-junit5 diff --git a/jobs-service/jobs-service-common/pom.xml b/jobs-service/jobs-service-common/pom.xml index 7823ce6e52..cdfc82a41a 100644 --- a/jobs-service/jobs-service-common/pom.xml +++ b/jobs-service/jobs-service-common/pom.xml @@ -120,11 +120,6 @@ quarkus-smallrye-reactive-messaging - - io.smallrye.reactive - smallrye-reactive-messaging-in-memory - - org.kie.kogito diff --git a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/job/DelegateJob.java b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/job/DelegateJob.java index 20796efa24..d76f163c6d 100644 --- a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/job/DelegateJob.java +++ b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/job/DelegateJob.java @@ -18,19 +18,26 @@ */ package org.kie.kogito.jobs.service.job; -import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; import org.kie.kogito.jobs.service.exception.JobExecutionException; +import org.kie.kogito.jobs.service.executor.JobExecutor; import org.kie.kogito.jobs.service.executor.JobExecutorResolver; +import org.kie.kogito.jobs.service.model.JobDetails; import org.kie.kogito.jobs.service.model.JobDetailsContext; import org.kie.kogito.jobs.service.model.JobExecutionResponse; -import org.kie.kogito.jobs.service.stream.JobEventPublisher; +import org.kie.kogito.jobs.service.scheduler.ReactiveJobScheduler; +import org.kie.kogito.jobs.service.utils.ErrorHandling; import org.kie.kogito.timer.Job; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.infrastructure.Infrastructure; +import static java.util.Objects.requireNonNull; +import static mutiny.zero.flow.adapters.AdaptersToFlow.publisher; + /** * The job that delegates the execution to the {@link JobExecutorResolver} with the {@link JobDetailsContext}. */ @@ -40,35 +47,45 @@ public class DelegateJob implements Job { private final JobExecutorResolver jobExecutorResolver; - private final JobEventPublisher jobEventPublisher; + ReactiveJobScheduler scheduler; - public DelegateJob(JobExecutorResolver executorResolver, JobEventPublisher jobEventPublisher) { + public DelegateJob(JobExecutorResolver executorResolver, ReactiveJobScheduler scheduler) { this.jobExecutorResolver = executorResolver; - this.jobEventPublisher = jobEventPublisher; + this.scheduler = scheduler; } @Override public void execute(JobDetailsContext ctx) { - LOGGER.info("Executing for context {}", ctx.getJobDetails()); - Optional.ofNullable(ctx) - .map(JobDetailsContext::getJobDetails) - .map(jobExecutorResolver::get) - .map(executor -> executor.execute(ctx.getJobDetails())) - .orElseThrow(() -> new IllegalStateException("JobDetails cannot be null from context " + ctx)) - .onItem().invoke(jobEventPublisher::publishJobSuccess) - .onFailure(JobExecutionException.class).invoke(ex -> { + final AtomicReference executionResponse = new AtomicReference<>(); + final JobDetails jobDetails = requireNonNull(ctx.getJobDetails(), () -> String.format("JobDetails cannot be null for context: %s", ctx)); + final JobExecutor executor = requireNonNull(jobExecutorResolver.get(jobDetails), () -> String.format("No JobExecutor was found for jobDetails: %s", jobDetails)); + LOGGER.info("Executing job for context: {}", jobDetails); + executor.execute(jobDetails) + .flatMap(response -> { + executionResponse.set(response); + return handleJobExecutionSuccess(response); + }) + .onFailure(JobExecutionException.class).recoverWithUni(ex -> { String jobId = ((JobExecutionException) ex).getJobId(); - LOGGER.error("Error executing job {}", jobId, ex); - jobEventPublisher.publishJobError(JobExecutionResponse.builder() + executionResponse.set(JobExecutionResponse.builder() .message(ex.getMessage()) .now() .jobId(jobId) .build()); + return handleJobExecutionError(executionResponse.get()); }) - //to avoid blocking IO pool from eventloop since execute() is blocking currently - //might consider execute() method to be non-blocking/async + // avoid blocking IO pool from the event-loop since alternative EmbeddedJobExecutor is blocking. .runSubscriptionOn(Infrastructure.getDefaultWorkerPool()) - .subscribe().with(response -> LOGGER.info("Executed successfully with response {}", response)); + .subscribe().with(ignore -> LOGGER.info("Job execution response processing has finished: {}", executionResponse.get())); + } + + public Uni handleJobExecutionSuccess(JobExecutionResponse response) { + LOGGER.debug("Job execution success response received: {}", response); + return Uni.createFrom().publisher(publisher(ErrorHandling.skipErrorPublisherBuilder(scheduler::handleJobExecutionSuccess, response).buildRs())); + } + public Uni handleJobExecutionError(JobExecutionResponse response) { + LOGGER.error("Job execution error response received: {}", response); + return Uni.createFrom().publisher(publisher(ErrorHandling.skipErrorPublisherBuilder(scheduler::handleJobExecutionError, response).buildRs())); } } diff --git a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/management/JobServiceLeaderHealthCheck.java b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/management/JobServiceLeaderHealthCheck.java index 13af8fdcd8..57b7856675 100644 --- a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/management/JobServiceLeaderHealthCheck.java +++ b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/management/JobServiceLeaderHealthCheck.java @@ -32,7 +32,7 @@ @ApplicationScoped public class JobServiceLeaderHealthCheck implements HealthCheck { - private AtomicBoolean enabled = new AtomicBoolean(false); + private final AtomicBoolean enabled = new AtomicBoolean(false); protected void onMessagingStatusChange(@Observes MessagingChangeEvent event) { this.enabled.set(event.isEnabled()); diff --git a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/repository/ReactiveJobRepository.java b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/repository/ReactiveJobRepository.java index b96c021240..0e85226114 100644 --- a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/repository/ReactiveJobRepository.java +++ b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/repository/ReactiveJobRepository.java @@ -27,6 +27,42 @@ public interface ReactiveJobRepository { + enum SortTermField { + FIRE_TIME, + CREATED, + ID + } + + class SortTerm { + private final SortTermField field; + private final boolean asc; + + private SortTerm(SortTermField field, boolean asc) { + this.field = field; + this.asc = asc; + } + + public SortTermField getField() { + return field; + } + + public boolean isAsc() { + return asc; + } + + public static SortTerm byFireTime(boolean asc) { + return new SortTerm(SortTermField.FIRE_TIME, asc); + } + + public static SortTerm byCreated(boolean asc) { + return new SortTerm(SortTermField.CREATED, asc); + } + + public static SortTerm byId(boolean asc) { + return new SortTerm(SortTermField.ID, asc); + } + } + CompletionStage save(JobDetails job); CompletionStage merge(String id, JobDetails job); @@ -39,9 +75,8 @@ public interface ReactiveJobRepository { CompletionStage delete(JobDetails job); - PublisherBuilder findByStatus(JobStatus... status); - - PublisherBuilder findAll(); - - PublisherBuilder findByStatusBetweenDatesOrderByPriority(ZonedDateTime from, ZonedDateTime to, JobStatus... status); + PublisherBuilder findByStatusBetweenDates(ZonedDateTime fromFireTime, + ZonedDateTime toFireTime, + JobStatus[] status, + SortTerm[] orderBy); } diff --git a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/repository/impl/BaseReactiveJobRepository.java b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/repository/impl/BaseReactiveJobRepository.java index 2271f46dc5..2827d1538f 100644 --- a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/repository/impl/BaseReactiveJobRepository.java +++ b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/repository/impl/BaseReactiveJobRepository.java @@ -18,17 +18,13 @@ */ package org.kie.kogito.jobs.service.repository.impl; -import java.util.Arrays; -import java.util.Objects; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.function.Supplier; import org.apache.commons.lang3.StringUtils; -import org.eclipse.microprofile.reactive.streams.operators.PublisherBuilder; import org.kie.kogito.jobs.service.model.JobDetails; -import org.kie.kogito.jobs.service.model.JobStatus; import org.kie.kogito.jobs.service.repository.ReactiveJobRepository; import org.kie.kogito.jobs.service.stream.JobEventPublisher; @@ -52,13 +48,6 @@ public CompletionStage runAsync(Supplier function) { return future; } - @Override - public PublisherBuilder findByStatus(JobStatus... status) { - return findAll() - .filter(job -> Objects.nonNull(job.getStatus())) - .filter(job -> Arrays.stream(status).anyMatch(job.getStatus()::equals)); - } - @Override public CompletionStage save(JobDetails job) { return doSave(job) diff --git a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/repository/impl/InMemoryJobRepository.java b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/repository/impl/InMemoryJobRepository.java index 5b61e1f403..fa11409c64 100644 --- a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/repository/impl/InMemoryJobRepository.java +++ b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/repository/impl/InMemoryJobRepository.java @@ -20,12 +20,11 @@ import java.time.ZonedDateTime; import java.util.Comparator; +import java.util.Date; +import java.util.List; import java.util.Map; -import java.util.Objects; -import java.util.Optional; import java.util.concurrent.CompletionStage; import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; import java.util.stream.Stream; import org.eclipse.microprofile.reactive.streams.operators.PublisherBuilder; @@ -42,6 +41,8 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +import static org.kie.kogito.jobs.service.utils.ModelUtil.jobWithCreatedAndLastUpdate; + @DefaultBean @ApplicationScoped public class InMemoryJobRepository extends BaseReactiveJobRepository implements ReactiveJobRepository { @@ -60,8 +61,10 @@ public InMemoryJobRepository(Vertx vertx, JobEventPublisher jobEventPublisher) { @Override public CompletionStage doSave(JobDetails job) { return runAsync(() -> { - jobMap.put(job.getId(), job); - return job; + boolean isNew = !jobMap.containsKey(job.getId()); + JobDetails timeStampedJob = jobWithCreatedAndLastUpdate(isNew, job); + jobMap.put(timeStampedJob.getId(), timeStampedJob); + return timeStampedJob; }); } @@ -81,20 +84,61 @@ public CompletionStage delete(String key) { } @Override - public PublisherBuilder findAll() { - return ReactiveStreams.fromIterable(jobMap.values()); + public PublisherBuilder findByStatusBetweenDates(ZonedDateTime fromFireTime, + ZonedDateTime toFireTime, + JobStatus[] status, + SortTerm[] orderBy) { + Stream unsortedResult = jobMap.values() + .stream() + .filter(j -> matchStatusFilter(j, status)) + .filter(j -> matchFireTimeFilter(j, fromFireTime, toFireTime)); + List result = orderBy == null || orderBy.length == 0 ? unsortedResult.toList() : unsortedResult.sorted(orderByComparator(orderBy)).toList(); + return ReactiveStreams.fromIterable(result); } - @Override - public PublisherBuilder findByStatusBetweenDatesOrderByPriority(ZonedDateTime from, ZonedDateTime to, JobStatus... status) { - return ReactiveStreams.fromIterable( - jobMap.values() - .stream() - .filter(j -> Optional.ofNullable(j.getStatus()) - .filter(s -> Objects.nonNull(status)) - .map(s -> Stream.of(status).anyMatch(s::equals)).orElse(true)) - .filter(j -> DateUtil.fromDate(j.getTrigger().hasNextFireTime()).isAfter(from) && DateUtil.fromDate(j.getTrigger().hasNextFireTime()).isBefore(to)) - .sorted(Comparator.comparing(JobDetails::getPriority).reversed()) - .collect(Collectors.toList())); + private static boolean matchStatusFilter(JobDetails job, JobStatus[] status) { + if (status == null || status.length == 0) { + return true; + } + return Stream.of(status).anyMatch(s -> job.getStatus() == s); } + + private static boolean matchFireTimeFilter(JobDetails job, ZonedDateTime fromFireTime, ZonedDateTime toFireTime) { + ZonedDateTime fireTime = DateUtil.fromDate(job.getTrigger().hasNextFireTime()); + return (fireTime.isEqual(fromFireTime) || fireTime.isAfter(fromFireTime)) && + (fireTime.isEqual(toFireTime) || fireTime.isBefore(toFireTime)); + } + + private static Comparator orderByComparator(SortTerm[] orderBy) { + Comparator comparator = createOrderByFieldComparator(orderBy[0]); + for (int i = 1; i < orderBy.length; i++) { + comparator = comparator.thenComparing(createOrderByFieldComparator(orderBy[i])); + } + return comparator; + } + + private static Comparator createOrderByFieldComparator(SortTerm field) { + Comparator comparator; + switch (field.getField()) { + case FIRE_TIME: + comparator = Comparator.comparingLong(jobDetails -> { + Date nextFireTime = jobDetails.getTrigger().hasNextFireTime(); + return nextFireTime != null ? nextFireTime.getTime() : Long.MIN_VALUE; + }); + break; + case CREATED: + comparator = Comparator.comparingLong(jobDetails -> { + ZonedDateTime created = jobDetails.getCreated(); + return created != null ? created.toInstant().toEpochMilli() : Long.MIN_VALUE; + }); + break; + case ID: + comparator = Comparator.comparing(JobDetails::getId); + break; + default: + throw new IllegalArgumentException("No comparator is defined for field: " + field.getField()); + } + return field.isAsc() ? comparator : comparator.reversed(); + } + } diff --git a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/repository/marshaller/JobDetailsMarshaller.java b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/repository/marshaller/JobDetailsMarshaller.java index 6772a50918..ea4a82a418 100644 --- a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/repository/marshaller/JobDetailsMarshaller.java +++ b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/repository/marshaller/JobDetailsMarshaller.java @@ -82,6 +82,7 @@ private static class JobDetailsAccessor { private Map trigger; private Long executionTimeout; private String executionTimeoutUnit; + private Date created; public JobDetailsAccessor() { } @@ -99,6 +100,7 @@ public JobDetailsAccessor(JobDetails jobDetails, RecipientMarshaller recipientMa this.trigger = Optional.ofNullable(jobDetails.getTrigger()).map(t -> triggerMarshaller.marshall(t).getMap()).orElse(null); this.executionTimeout = jobDetails.getExecutionTimeout(); this.executionTimeoutUnit = Optional.ofNullable(jobDetails.getExecutionTimeoutUnit()).map(Enum::name).orElse(null); + this.created = Optional.ofNullable(jobDetails.getCreated()).map(u -> Date.from(u.toInstant())).orElse(null); } public JobDetails to(RecipientMarshaller recipientMarshaller, TriggerMarshaller triggerMarshaller) { @@ -115,6 +117,7 @@ public JobDetails to(RecipientMarshaller recipientMarshaller, TriggerMarshaller .trigger(Optional.ofNullable(this.trigger).map(t -> triggerMarshaller.unmarshall(new JsonObject(t))).orElse(null)) .executionTimeout(this.executionTimeout) .executionTimeoutUnit(Optional.ofNullable(this.executionTimeoutUnit).map(ChronoUnit::valueOf).orElse(null)) + .created(Optional.ofNullable(this.created).map(t -> ZonedDateTime.ofInstant(t.toInstant(), DEFAULT_ZONE)).orElse(null)) .build(); } @@ -213,5 +216,13 @@ public String getExecutionTimeoutUnit() { public void setExecutionTimeoutUnit(String executionTimeoutUnit) { this.executionTimeoutUnit = executionTimeoutUnit; } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } } } diff --git a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/BaseTimerJobScheduler.java b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/BaseTimerJobScheduler.java index aa2b39a824..ac080cdb82 100644 --- a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/BaseTimerJobScheduler.java +++ b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/BaseTimerJobScheduler.java @@ -21,6 +21,8 @@ import java.time.Duration; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Collection; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -29,7 +31,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; -import org.apache.commons.lang3.tuple.Pair; import org.eclipse.microprofile.reactive.streams.operators.PublisherBuilder; import org.eclipse.microprofile.reactive.streams.operators.ReactiveStreams; import org.kie.kogito.jobs.service.exception.InvalidScheduleTimeException; @@ -50,6 +51,8 @@ import io.smallrye.mutiny.Uni; import static mutiny.zero.flow.adapters.AdaptersToFlow.publisher; +import static org.kie.kogito.jobs.service.utils.ModelUtil.jobWithStatus; +import static org.kie.kogito.jobs.service.utils.ModelUtil.jobWithStatusAndHandle; /** * Base reactive Job Scheduler that performs the fundamental operations and let to the concrete classes to @@ -67,7 +70,13 @@ public abstract class BaseTimerJobScheduler implements ReactiveJobScheduler { * Flag to allow and force a job with expirationTime in the past to be executed immediately. If false an * exception will be thrown. */ - Optional forceExecuteExpiredJobs; + boolean forceExecuteExpiredJobs; + + /** + * Flag to allow that jobs that might have overdue during an eventual service shutdown should be fired at the + * next service start. + */ + boolean forceExecuteExpiredJobsOnServiceStart; /** * The current chunk size in minutes the scheduler handles, it is used to keep a limit number of jobs scheduled @@ -77,42 +86,84 @@ public abstract class BaseTimerJobScheduler implements ReactiveJobScheduler { private ReactiveJobRepository jobRepository; - private final Map schedulerControl; + private final Map schedulerControl; + + protected static class SchedulerControlRecord { + private final String jobId; + private final long handleId; + private final ZonedDateTime scheduledTime; + + public SchedulerControlRecord(String jobId, long handleId, ZonedDateTime scheduledTime) { + this.jobId = jobId; + this.handleId = handleId; + this.scheduledTime = scheduledTime; + } + + public String getJobId() { + return jobId; + } + + public long getHandleId() { + return handleId; + } + + public ZonedDateTime getScheduledTime() { + return scheduledTime; + } + } protected BaseTimerJobScheduler() { - this(null, 0, 0, 0, null); + this(null, 0, 0, 0, true, true); } protected BaseTimerJobScheduler(ReactiveJobRepository jobRepository, long backoffRetryMillis, long maxIntervalLimitToRetryMillis, long schedulerChunkInMinutes, - Boolean forceExecuteExpiredJobs) { + boolean forceExecuteExpiredJobs, + boolean forceExecuteExpiredJobsOnServiceStart) { this.jobRepository = jobRepository; this.backoffRetryMillis = backoffRetryMillis; this.maxIntervalLimitToRetryMillis = maxIntervalLimitToRetryMillis; this.schedulerControl = new ConcurrentHashMap<>(); this.schedulerChunkInMinutes = schedulerChunkInMinutes; - this.forceExecuteExpiredJobs = Optional.ofNullable(forceExecuteExpiredJobs); + this.forceExecuteExpiredJobs = forceExecuteExpiredJobs; + this.forceExecuteExpiredJobsOnServiceStart = forceExecuteExpiredJobsOnServiceStart; } + /** + * Executed from the API to reflect client invocations. + */ @Override public Publisher schedule(JobDetails job) { - LOGGER.debug("Scheduling {}", job); + LOGGER.debug("Scheduling job: {}", job); return ReactiveStreams - //check if the job is already scheduled and persisted .fromCompletionStage(jobRepository.exists(job.getId())) .flatMap(exists -> Boolean.TRUE.equals(exists) - ? handleExistingJob(job).map(existingJob -> Pair.of(exists, existingJob)) - : ReactiveStreams.of(Pair.of(exists, job))) - .flatMap(pair -> isOnCurrentSchedulerChunk(job) - //in case the job is on the current bulk, proceed with scheduling process - ? doJobScheduling(job, pair.getLeft()) - //in case the job is not on the current bulk, just save it to be scheduled later + ? handleExistingJob(job) + : ReactiveStreams.of(job)) + .flatMap(handled -> isOnCurrentSchedulerChunk(job) + // in case the job is on the current bulk, proceed with scheduling process. + ? doJobScheduling(job) + // in case the job is not on the current bulk, just save it to be scheduled later. : ReactiveStreams.fromCompletionStage(jobRepository.save(jobWithStatus(job, JobStatus.SCHEDULED)))) .buildRs(); } + /** + * Internal use, executed by the periodic loader only. Jobs processed by this method belongs to the current chunk. + */ + @Override + public Publisher internalSchedule(JobDetails job, boolean onServiceStart) { + LOGGER.debug("Internal Scheduling, onServiceStart: {}, job: {}", onServiceStart, job); + return ReactiveStreams + .fromCompletionStage(jobRepository.exists(job.getId())) + .flatMap(exists -> Boolean.TRUE.equals(exists) + ? handleInternalSchedule(job, onServiceStart) + : handleInternalScheduleDeletedJob(job)) + .buildRs(); + } + @Override public PublisherBuilder reschedule(String id, Trigger trigger) { return ReactiveStreams.fromCompletionStageNullable(jobRepository.merge(id, JobDetails.builder().trigger(trigger).build())) @@ -121,21 +172,10 @@ public PublisherBuilder reschedule(String id, Trigger trigger) { .flatMapRsPublisher(j -> j); } - private JobDetails jobWithStatus(JobDetails job, JobStatus status) { - return JobDetails.builder().of(job).status(status).build(); - } - - private JobDetails jobWithStatusAndHandle(JobDetails job, JobStatus status, ManageableJobHandle handle) { - return JobDetails.builder().of(job).status(status).scheduledId(String.valueOf(handle.getId())).build(); - } - /** * Performs the given job scheduling process on the scheduler, after all the validations already made. - * - * @param job to be scheduled - * @return */ - private PublisherBuilder doJobScheduling(JobDetails job, boolean exists) { + private PublisherBuilder doJobScheduling(JobDetails job) { return ReactiveStreams.of(job) //calculate the delay (when the job should be executed) .map(current -> job.getTrigger().hasNextFireTime()) @@ -144,62 +184,88 @@ private PublisherBuilder doJobScheduling(JobDetails job, boolean exi .peek(delay -> Optional .of(delay.isNegative()) .filter(Boolean.FALSE::equals) - .orElseThrow(() -> new InvalidScheduleTimeException("The expirationTime should be greater than current " + - "time"))) - // new jobs in current bulk must be stored in the repository before we proceed to schedule, the same as - // way as we do with new jobs that aren't. In this way we provide the same pattern for both cases. - // https://issues.redhat.com/browse/KOGITO-8513 - .flatMap(delay -> !exists - ? ReactiveStreams.fromCompletionStage(jobRepository.save(jobWithStatus(job, JobStatus.SCHEDULED))) - : ReactiveStreams.fromCompletionStage(CompletableFuture.completedFuture(job))) - //schedule the job on the scheduler - .flatMap(j -> scheduleRegistering(job, Optional.empty())) + .orElseThrow(() -> new InvalidScheduleTimeException( + String.format("The expirationTime: %s, for job: %s should be greater than current time: %s.", + job.getTrigger().hasNextFireTime(), job.getId(), ZonedDateTime.now())))) + .flatMap(delay -> ReactiveStreams.fromCompletionStage(jobRepository.save(jobWithStatus(job, JobStatus.SCHEDULED)))) + //schedule the job in the scheduler + .flatMap(j -> scheduleRegistering(job, job.getTrigger())) .map(handle -> jobWithStatusAndHandle(job, JobStatus.SCHEDULED, handle)) .map(scheduledJob -> jobRepository.save(scheduledJob)) .flatMapCompletionStage(p -> p); } /** - * Check if it should be scheduled (on the current chunk) or saved to be scheduled later. - * - * @return + * Check if the job should be scheduled on the current chunk or saved to be scheduled later. */ private boolean isOnCurrentSchedulerChunk(JobDetails job) { return DateUtil.fromDate(job.getTrigger().hasNextFireTime()).isBefore(DateUtil.now().plusMinutes(schedulerChunkInMinutes)); } private PublisherBuilder handleExistingJob(JobDetails job) { - //always returns true, canceling in case the job is already schedule return ReactiveStreams.fromCompletionStage(jobRepository.get(job.getId())) - //handle scheduled and retry cases .flatMap( - j -> { - switch (j.getStatus()) { + currentJob -> { + switch (currentJob.getStatus()) { case SCHEDULED: - return handleExpirationTime(j) - .map(scheduled -> jobWithStatus(scheduled, JobStatus.CANCELED)) - .map(CompletableFuture::completedFuture) - .flatMapCompletionStage(this::cancel) - .map(deleted -> j); case RETRY: - return handleRetry(CompletableFuture.completedFuture(j)) - .flatMap(retryJob -> ReactiveStreams.empty()); + // cancel the job. + return ReactiveStreams.fromCompletionStage( + cancel(CompletableFuture.completedFuture(jobWithStatus(currentJob, JobStatus.CANCELED)))); default: - //empty to break the stream processing + // uncommon, break the stream processing return ReactiveStreams.empty(); } }) .onErrorResumeWith(t -> ReactiveStreams.empty()); } + private PublisherBuilder handleInternalSchedule(JobDetails job, boolean onStart) { + unregisterScheduledJob(job); + switch (job.getStatus()) { + case SCHEDULED: + Duration delay = calculateRawDelay(DateUtil.fromDate(job.getTrigger().hasNextFireTime())); + if (delay.isNegative() && onStart && !forceExecuteExpiredJobsOnServiceStart) { + return ReactiveStreams.fromCompletionStage(handleExpiredJob(job)); + } else { + // other cases of potential overdue are because of slow processing of the jobs service, or the user + // configured to fire overdue triggers at service startup. Always schedule. + PublisherBuilder preSchedule; + if (job.getScheduledId() != null) { + // cancel the existing timer if any. + preSchedule = ReactiveStreams.fromPublisher(doCancel(job)).flatMap(jobHandle -> ReactiveStreams.of(job)); + } else { + preSchedule = ReactiveStreams.of(job); + } + return preSchedule.flatMap(j -> scheduleRegistering(job, job.getTrigger())) + .map(handle -> jobWithStatusAndHandle(job, JobStatus.SCHEDULED, handle)) + .map(scheduledJob -> jobRepository.save(scheduledJob)) + .flatMapCompletionStage(p -> p); + } + case RETRY: + return handleRetry(CompletableFuture.completedFuture(job)); + default: + // by definition there are no more cases, only SCHEDULED and RETRY cases are picked by the loader. + return ReactiveStreams.of(job); + } + } + + private PublisherBuilder handleInternalScheduleDeletedJob(JobDetails job) { + LOGGER.warn("Job was removed from database: {}.", job); + return ReactiveStreams.of(job); + } + private Duration calculateDelay(ZonedDateTime expirationTime) { - //in case forceExecuteExpiredJobs is true, execute the job immediately (1ms) - return Optional.of(Duration.between(DateUtil.now(), expirationTime)) - .filter(d -> !d.isNegative()) - .orElse(forceExecuteExpiredJobs - .filter(Boolean.TRUE::equals) - .map(f -> Duration.ofSeconds(1)) - .orElse(Duration.ofSeconds(-1))); + Duration delay = Duration.between(DateUtil.now(), expirationTime); + if (!delay.isNegative()) { + return delay; + } + //in case forceExecuteExpiredJobs is true, execute the job immediately. + return forceExecuteExpiredJobs ? Duration.ofSeconds(1) : Duration.ofSeconds(-1); + } + + private Duration calculateRawDelay(ZonedDateTime expirationTime) { + return Duration.between(DateUtil.now(), expirationTime); } public PublisherBuilder handleJobExecutionSuccess(JobDetails futureJob) { @@ -212,7 +278,7 @@ public PublisherBuilder handleJobExecutionSuccess(JobDetails futureJ .flatMap(job -> Optional .ofNullable(job.getTrigger()) .filter(trigger -> Objects.nonNull(trigger.hasNextFireTime())) - .map(time -> doJobScheduling(job, true)) + .map(time -> doJobScheduling(job)) //in case the job should not be executed anymore (there is no nextFireTime) .orElseGet(() -> ReactiveStreams.of(jobWithStatus(job, JobStatus.EXECUTED)))) //final state EXECUTED, removing the job, it is not kept on the repository @@ -269,10 +335,10 @@ private PublisherBuilder handleRetry(CompletionStage fut .flatMap(scheduledJob -> handleExpirationTime(scheduledJob) .map(JobDetails::getStatus) .filter(s -> !JobStatus.ERROR.equals(s)) - .map(s -> scheduleRegistering(scheduledJob, Optional.of(getRetryTrigger()))) + .map(s -> scheduleRegistering(scheduledJob, getRetryTrigger())) .flatMap(p -> p) - .map(scheduleId -> JobDetails.builder() - .of(jobWithStatusAndHandle(scheduledJob, JobStatus.RETRY, scheduleId)) + .map(registeredJobHandle -> JobDetails.builder() + .of(jobWithStatusAndHandle(scheduledJob, JobStatus.RETRY, registeredJobHandle)) .incrementRetries() .build()) .map(jobRepository::save) @@ -298,21 +364,25 @@ private CompletionStage handleExpiredJob(JobDetails scheduledJob) { .orElse(null); } - private PublisherBuilder scheduleRegistering(JobDetails job, Optional trigger) { + private PublisherBuilder scheduleRegistering(JobDetails job, Trigger trigger) { return doSchedule(job, trigger) .peek(registerScheduledJob(job)); } - private Consumer registerScheduledJob(JobDetails job) { - return s -> schedulerControl.put(job.getId(), DateUtil.now()); + protected Consumer registerScheduledJob(JobDetails job) { + return handle -> schedulerControl.put(job.getId(), new SchedulerControlRecord(job.getId(), handle.getId(), DateUtil.now())); } - public abstract PublisherBuilder doSchedule(JobDetails job, Optional trigger); + public abstract PublisherBuilder doSchedule(JobDetails job, Trigger trigger); - private ZonedDateTime unregisterScheduledJob(JobDetails job) { + protected SchedulerControlRecord unregisterScheduledJob(JobDetails job) { return schedulerControl.remove(job.getId()); } + protected Collection getScheduledJobs() { + return new ArrayList<>(schedulerControl.values()); + } + public CompletionStage cancel(CompletionStage futureJob) { return Uni.createFrom().completionStage(futureJob) .onItem().invoke(job -> LOGGER.debug("Cancel Job Scheduling {}", job)) @@ -340,10 +410,11 @@ public CompletionStage cancel(String jobId) { @Override public Optional scheduled(String jobId) { - return Optional.ofNullable(schedulerControl.get(jobId)); + SchedulerControlRecord record = schedulerControl.get(jobId); + return Optional.ofNullable(record != null ? record.getScheduledTime() : null); } public void setForceExecuteExpiredJobs(boolean forceExecuteExpiredJobs) { - this.forceExecuteExpiredJobs = Optional.of(forceExecuteExpiredJobs); + this.forceExecuteExpiredJobs = forceExecuteExpiredJobs; } } diff --git a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/JobSchedulerManager.java b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/JobSchedulerManager.java index c924efaeec..b0bd05feac 100644 --- a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/JobSchedulerManager.java +++ b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/JobSchedulerManager.java @@ -18,7 +18,9 @@ */ package org.kie.kogito.jobs.service.scheduler; -import java.util.Optional; +import java.time.LocalDateTime; +import java.time.ZonedDateTime; +import java.util.Date; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; @@ -26,6 +28,7 @@ import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.reactive.streams.operators.PublisherBuilder; import org.kie.kogito.jobs.service.management.MessagingChangeEvent; +import org.kie.kogito.jobs.service.management.ReleaseLeaderEvent; import org.kie.kogito.jobs.service.model.JobDetails; import org.kie.kogito.jobs.service.model.JobStatus; import org.kie.kogito.jobs.service.repository.ReactiveJobRepository; @@ -38,9 +41,14 @@ import io.vertx.mutiny.core.Vertx; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Event; import jakarta.enterprise.event.Observes; import jakarta.inject.Inject; +import static org.kie.kogito.jobs.service.repository.ReactiveJobRepository.SortTerm.byCreated; +import static org.kie.kogito.jobs.service.repository.ReactiveJobRepository.SortTerm.byFireTime; +import static org.kie.kogito.jobs.service.repository.ReactiveJobRepository.SortTerm.byId; + @ApplicationScoped public class JobSchedulerManager { @@ -66,20 +74,52 @@ public class JobSchedulerManager { @ConfigProperty(name = "kogito.jobs-service.loadJobFromCurrentTimeIntervalInMinutes", defaultValue = "0") long loadJobFromCurrentTimeIntervalInMinutes; + /** + * Number of retries configured for the periodic jobs loading procedure. Every time the procedure is started this + * value is considered. + */ + @ConfigProperty(name = "kogito.jobs-service.loadJobRetries", defaultValue = "3") + int loadJobRetries; + + /** + * Error strategy to apply when the periodic jobs loading procedure has exceeded the jobLoadReties. + */ + @ConfigProperty(name = "kogito.jobs-service.loadJobErrorStrategy", defaultValue = "NONE") + String loadJobErrorStrategy; + @Inject TimerDelegateJobScheduler scheduler; @Inject ReactiveJobRepository repository; + @Inject + Event releaseLeaderEvent; + + @Inject + Event jobSchedulerManagerErrorEvent; + @Inject Vertx vertx; final AtomicBoolean enabled = new AtomicBoolean(false); - final AtomicLong periodicTimerIdForLoadJobs = new AtomicLong(-1l); + final AtomicLong periodicTimerIdForLoadJobs = new AtomicLong(-1L); + + final AtomicBoolean initialLoading = new AtomicBoolean(true); + + static final ZonedDateTime INITIAL_DATE = ZonedDateTime.of(LocalDateTime.parse("2000-01-01T00:00:00"), DateUtil.DEFAULT_ZONE); + + enum LoadJobErrorStrategy { + NONE, + /** + * The service liveness check goes to DOWN, indicating that the service must be restarted. + */ + FAIL_SERVICE + } private void startJobsLoadingFromRepositoryTask() { //guarantee it starts the task just in case it is not already active + initialLoading.set(true); if (periodicTimerIdForLoadJobs.get() < 0) { if (loadJobIntervalInMinutes > schedulerChunkInMinutes) { LOGGER.warn("The loadJobIntervalInMinutes ({}) cannot be greater than schedulerChunkInMinutes ({}), " + @@ -115,31 +155,72 @@ protected synchronized void onMessagingStatusChange(@Observes MessagingChangeEve } } - //Runs periodically loading the jobs from the repository in chunks + /** + * Runs periodically loading the jobs from the repository in chunks. + */ void loadJobDetails() { if (!enabled.get()) { LOGGER.info("Skip loading scheduled jobs"); return; } - loadJobsInCurrentChunk() - .filter(j -> !scheduler.scheduled(j.getId()).isPresent())//not consider already scheduled jobs - .flatMapRsPublisher(t -> ErrorHandling.skipErrorPublisher(scheduler::schedule, t)) - .forEach(a -> LOGGER.debug("Loaded and scheduled job {}", a)) + ZonedDateTime fromFireTime = DateUtil.now().minusMinutes(loadJobFromCurrentTimeIntervalInMinutes); + ZonedDateTime toFireTime = DateUtil.now().plusMinutes(schedulerChunkInMinutes); + if (initialLoading.get()) { + fromFireTime = INITIAL_DATE; + } + doLoadJobDetails(fromFireTime, toFireTime, loadJobRetries); + } + + public void doLoadJobDetails(ZonedDateTime fromFireTime, ZonedDateTime toFireTime, final int retries) { + LOGGER.info("Loading jobs to schedule from the repository, fromFireTime: {} toFireTime: {}.", fromFireTime, toFireTime); + loadJobsBetweenDates(fromFireTime, toFireTime) + .filter(this::isNotScheduled) + .flatMapRsPublisher(jobDetails -> ErrorHandling.skipErrorPublisher((jd) -> scheduler.internalSchedule(jd, initialLoading.get()), jobDetails)) + .forEach(jobDetails -> LOGGER.debug("Loaded and scheduled job {}.", jobDetails)) .run() - .whenComplete((v, t) -> Optional.ofNullable(t) - .map(ex -> { - LOGGER.error("Error Loading scheduled jobs!", ex); - return null; - }) - .orElseGet(() -> { - LOGGER.info("Loading scheduled jobs completed !"); - return null; - })); + .whenComplete((unused, throwable) -> { + if (throwable != null) { + LOGGER.error(String.format("Error during jobs loading, retries left: %d.", retries), throwable); + if (retries > 0) { + LOGGER.info("Jobs loading retry: #{} will be executed.", retries - 1); + doLoadJobDetails(fromFireTime, toFireTime, retries - 1); + } else { + LOGGER.error("Jobs loading has failed and no more retires are left, loadJobErrorStrategy: {} will be applied.", loadJobErrorStrategy); + applyLoadJobsErrorStrategy(throwable); + } + } + initialLoading.set(false); + LOGGER.info("Loading scheduled jobs completed !"); + }); } - private PublisherBuilder loadJobsInCurrentChunk() { - return repository.findByStatusBetweenDatesOrderByPriority(DateUtil.now().minusMinutes(loadJobFromCurrentTimeIntervalInMinutes), - DateUtil.now().plusMinutes(schedulerChunkInMinutes), - JobStatus.SCHEDULED, JobStatus.RETRY); + private boolean isNotScheduled(JobDetails jobDetails) { + Date triggerFireTime = jobDetails.getTrigger().hasNextFireTime(); + ZonedDateTime nextFireTime = triggerFireTime != null ? DateUtil.instantToZonedDateTime(triggerFireTime.toInstant()) : null; + boolean scheduled = scheduler.scheduled(jobDetails.getId()).isPresent(); + LOGGER.debug("Job found, id: {}, nextFireTime: {}, created: {}, status: {}, already scheduled: {}", jobDetails.getId(), + nextFireTime, + jobDetails.getCreated(), + jobDetails.getStatus(), + scheduled); + return !scheduled; + } + + private PublisherBuilder loadJobsBetweenDates(ZonedDateTime fromFireTime, ZonedDateTime toFireTime) { + return repository.findByStatusBetweenDates(fromFireTime, toFireTime, + new JobStatus[] { JobStatus.SCHEDULED, JobStatus.RETRY }, + new ReactiveJobRepository.SortTerm[] { byCreated(true), byFireTime(true), byId(true) }); + } + + private void applyLoadJobsErrorStrategy(Throwable throwable) { + if (LoadJobErrorStrategy.FAIL_SERVICE.name().equalsIgnoreCase(loadJobErrorStrategy)) { + scheduler.unscheduleTimers(); + cancelJobsLoadingFromRepositoryTask(); + releaseLeaderEvent.fire(new ReleaseLeaderEvent()); + String message = "An unrecoverable error occurred during the jobs loading procedure from database." + + " Please check the database status and configuration, or contact the administrator for a detailed review of the error: " + throwable.getMessage(); + LOGGER.error(message, throwable); + jobSchedulerManagerErrorEvent.fire(new JobSchedulerManagerErrorEvent(message, throwable)); + } } } diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/tasks/TasksResponse.java b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/JobSchedulerManagerErrorEvent.java similarity index 67% rename from runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/tasks/TasksResponse.java rename to jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/JobSchedulerManagerErrorEvent.java index 54a415dc5a..8813854dea 100644 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/tasks/TasksResponse.java +++ b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/JobSchedulerManagerErrorEvent.java @@ -16,21 +16,24 @@ * specific language governing permissions and limitations * under the License. */ +package org.kie.kogito.jobs.service.scheduler; -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.dataindex.tasks; +public class JobSchedulerManagerErrorEvent { -public class TasksResponse { + private final String message; - private TaskResponseData data; + private final Throwable error; - public TasksResponse() { + public JobSchedulerManagerErrorEvent(String message, Throwable error) { + this.message = message; + this.error = error; } - public TaskResponseData getData() { - return data; + public String getMessage() { + return message; } - public void setData(final TaskResponseData data) { - this.data = data; + public Throwable getError() { + return error; } } diff --git a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/JobSchedulerManagerHealthCheck.java b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/JobSchedulerManagerHealthCheck.java new file mode 100644 index 0000000000..bcca8d3c84 --- /dev/null +++ b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/JobSchedulerManagerHealthCheck.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.jobs.service.scheduler; + +import java.util.concurrent.atomic.AtomicReference; + +import org.eclipse.microprofile.health.HealthCheck; +import org.eclipse.microprofile.health.HealthCheckResponse; +import org.eclipse.microprofile.health.HealthCheckResponseBuilder; +import org.eclipse.microprofile.health.Liveness; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; + +@Liveness +@ApplicationScoped +public class JobSchedulerManagerHealthCheck implements HealthCheck { + + private final AtomicReference errorEvent = new AtomicReference<>(); + + @Override + public HealthCheckResponse call() { + final HealthCheckResponseBuilder responseBuilder = HealthCheckResponse.named("Job Scheduler Manager"); + if (errorEvent.get() != null) { + return responseBuilder.withData("error", errorEvent.get().getMessage()).down().build(); + } + return responseBuilder.up().build(); + } + + protected void onJobSchedulerManagerStatusChange(@Observes JobSchedulerManagerErrorEvent event) { + errorEvent.set(event); + } +} diff --git a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/ReactiveJobScheduler.java b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/ReactiveJobScheduler.java index 07948f5c4b..6cd025abfc 100644 --- a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/ReactiveJobScheduler.java +++ b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/ReactiveJobScheduler.java @@ -30,6 +30,8 @@ public interface ReactiveJobScheduler extends JobScheduler Publisher schedule(JobDetails job); + Publisher internalSchedule(JobDetails job, boolean onServiceStart); + CompletionStage cancel(String jobId); PublisherBuilder reschedule(String id, Trigger trigger); diff --git a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/impl/TimerDelegateJobScheduler.java b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/impl/TimerDelegateJobScheduler.java index 97a8f0a906..6b30c30b0b 100644 --- a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/impl/TimerDelegateJobScheduler.java +++ b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/impl/TimerDelegateJobScheduler.java @@ -19,7 +19,6 @@ package org.kie.kogito.jobs.service.scheduler.impl; import java.util.Objects; -import java.util.Optional; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.reactive.streams.operators.PublisherBuilder; @@ -31,7 +30,6 @@ import org.kie.kogito.jobs.service.model.ManageableJobHandle; import org.kie.kogito.jobs.service.repository.ReactiveJobRepository; import org.kie.kogito.jobs.service.scheduler.BaseTimerJobScheduler; -import org.kie.kogito.jobs.service.stream.JobEventPublisher; import org.kie.kogito.timer.Trigger; import org.reactivestreams.Publisher; import org.slf4j.Logger; @@ -52,8 +50,6 @@ public class TimerDelegateJobScheduler extends BaseTimerJobScheduler { private VertxTimerServiceScheduler delegate; - private JobEventPublisher jobEventPublisher; - protected TimerDelegateJobScheduler() { } @@ -63,21 +59,19 @@ public TimerDelegateJobScheduler(ReactiveJobRepository jobRepository, @ConfigProperty(name = "kogito.jobs-service.maxIntervalLimitToRetryMillis", defaultValue = "60000") long maxIntervalLimitToRetryMillis, @ConfigProperty(name = "kogito.jobs-service.schedulerChunkInMinutes", defaultValue = "10") long schedulerChunkInMinutes, @ConfigProperty(name = "kogito.jobs-service.forceExecuteExpiredJobs", defaultValue = "true") boolean forceExecuteExpiredJobs, - JobExecutorResolver jobExecutorResolver, VertxTimerServiceScheduler delegate, - JobEventPublisher jobEventPublisher) { - super(jobRepository, backoffRetryMillis, maxIntervalLimitToRetryMillis, schedulerChunkInMinutes, forceExecuteExpiredJobs); + @ConfigProperty(name = "kogito.jobs-service.forceExecuteExpiredJobsOnServiceStart", defaultValue = "true") boolean forceExecuteExpiredJobsOnServiceStart, + JobExecutorResolver jobExecutorResolver, VertxTimerServiceScheduler delegate) { + super(jobRepository, backoffRetryMillis, maxIntervalLimitToRetryMillis, schedulerChunkInMinutes, forceExecuteExpiredJobs, forceExecuteExpiredJobsOnServiceStart); this.jobExecutorResolver = jobExecutorResolver; this.delegate = delegate; - this.jobEventPublisher = jobEventPublisher; } @Override - public PublisherBuilder doSchedule(JobDetails job, Optional trigger) { - LOGGER.debug("Job Scheduling {}", job); - return ReactiveStreams - .of(job) - .map(j -> delegate.scheduleJob(new DelegateJob(jobExecutorResolver, jobEventPublisher), new JobDetailsContext(j), - trigger.orElse(j.getTrigger()))); + public PublisherBuilder doSchedule(JobDetails job, Trigger trigger) { + LOGGER.debug("Job Scheduling job: {}, trigger: {}", job, trigger); + ManageableJobHandle jobHandle = delegate.scheduleJob(new DelegateJob(jobExecutorResolver, this), + new JobDetailsContext(job), trigger); + return ReactiveStreams.of(jobHandle); } @Override @@ -94,4 +88,15 @@ public Publisher doCancel(JobDetails scheduledJob) { .buildRs(); } + /** + * Removes only the programed in-memory timers. + */ + public void unscheduleTimers() { + LOGGER.debug("Removing in-memory scheduled timers"); + super.getScheduledJobs().forEach(record -> { + boolean removed = delegate.removeJob(new ManageableJobHandle(record.getHandleId())); + LOGGER.debug("Vertex timer: {} for jobId: {}, was removed: {}", record.getHandleId(), record.getJobId(), removed); + super.unregisterScheduledJob(JobDetails.builder().id(record.getJobId()).build()); + }); + } } diff --git a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/impl/VertxTimerServiceScheduler.java b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/impl/VertxTimerServiceScheduler.java index 431727ac1d..bc538cea2c 100644 --- a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/impl/VertxTimerServiceScheduler.java +++ b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/scheduler/impl/VertxTimerServiceScheduler.java @@ -18,9 +18,7 @@ */ package org.kie.kogito.jobs.service.scheduler.impl; -import java.time.Instant; import java.time.ZonedDateTime; -import java.time.chrono.ChronoZonedDateTime; import java.util.Collection; import java.util.Optional; @@ -43,6 +41,8 @@ public class VertxTimerServiceScheduler implements TimerService, InternalSchedulerService { + private static final long MIN_TIMER_DELAY = 1000; + protected TimerJobFactoryManager jobFactoryManager = DefaultTimerJobFactoryManager.instance; protected final Vertx vertx; @@ -118,13 +118,9 @@ public void internalSchedule(TimerJobInstance timerJobInstance) { handle.setScheduledTime(now); } - private Long calculateDelay(long then, ZonedDateTime now) { - return Optional.of(now) - .map(ChronoZonedDateTime::toInstant) - .map(Instant::toEpochMilli) - .filter(n -> then > n) - .map(n -> then - n) - .orElse(1l); + private long calculateDelay(long then, ZonedDateTime now) { + long delay = then - now.toInstant().toEpochMilli(); + return Math.max(MIN_TIMER_DELAY, delay); } public Vertx getVertx() { diff --git a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/stream/AbstractJobStreams.java b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/stream/AbstractJobStreams.java index 5899942821..64189e2ab1 100644 --- a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/stream/AbstractJobStreams.java +++ b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/stream/AbstractJobStreams.java @@ -34,7 +34,7 @@ import io.smallrye.reactive.messaging.providers.locals.ContextAwareMessage; -public abstract class AbstractJobStreams { +public abstract class AbstractJobStreams implements JobStreams { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractJobStreams.class); @@ -56,17 +56,24 @@ protected AbstractJobStreams(ObjectMapper objectMapper, boolean enabled, Emitter this.url = url; } - protected void jobStatusChange(JobDetails job) { - if (enabled) { + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public void jobStatusChange(JobDetails job) { + if (isEnabled()) { try { JobDataEvent event = JobDataEvent .builder() .source(url + RestApiConstants.JOBS_PATH) .data(ScheduledJobAdapter.of(job))//this should support jobs crated with V1 and V2 .build(); + LOGGER.debug("emit jobStatusChange, hasRequests: {}, eventId: {}, jobDetails: {}", emitter.hasRequests(), event.getId(), job); String json = objectMapper.writeValueAsString(event); emitter.send(decorate(ContextAwareMessage.of(json) - .withAck(() -> onAck(job)) + .withAck(() -> onAck(event.getId(), job)) .withNack(reason -> onNack(reason, job)))); } catch (Exception e) { String msg = String.format("An unexpected error was produced while processing a Job status change for the job: %s", job); @@ -75,13 +82,13 @@ protected void jobStatusChange(JobDetails job) { } } - protected CompletionStage onAck(JobDetails job) { - LOGGER.debug("Job Status change published: {}", job); + protected CompletionStage onAck(String eventId, JobDetails job) { + LOGGER.debug("Job Status change emitted successfully, eventId: {}, jobDetails: {}", eventId, job); return CompletableFuture.completedFuture(null); } protected CompletionStage onNack(Throwable reason, JobDetails job) { - String msg = String.format("An error was produced while publishing a Job status change for the job: %s", job); + String msg = String.format("An error was produced while emitting a Job status change for the job: %s", job); LOGGER.error(msg, reason); return CompletableFuture.completedFuture(null); } diff --git a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/stream/JobEventPublisher.java b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/stream/JobEventPublisher.java index 4342cf00c9..57f8911734 100644 --- a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/stream/JobEventPublisher.java +++ b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/stream/JobEventPublisher.java @@ -19,13 +19,8 @@ package org.kie.kogito.jobs.service.stream; import org.kie.kogito.jobs.service.model.JobDetails; -import org.kie.kogito.jobs.service.model.JobExecutionResponse; public interface JobEventPublisher { - JobExecutionResponse publishJobError(JobExecutionResponse response); - - JobExecutionResponse publishJobSuccess(JobExecutionResponse response); - JobDetails publishJobStatusChange(JobDetails scheduledJob); } diff --git a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/stream/JobStreams.java b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/stream/JobStreams.java new file mode 100644 index 0000000000..4f84e71cd6 --- /dev/null +++ b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/stream/JobStreams.java @@ -0,0 +1,11 @@ +package org.kie.kogito.jobs.service.stream; + +import org.kie.kogito.jobs.service.model.JobDetails; + +public interface JobStreams { + + boolean isEnabled(); + + void jobStatusChange(JobDetails job); + +} diff --git a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/stream/JobStreamsEventPublisher.java b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/stream/JobStreamsEventPublisher.java index f8ef123190..30c9f01112 100644 --- a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/stream/JobStreamsEventPublisher.java +++ b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/stream/JobStreamsEventPublisher.java @@ -18,131 +18,42 @@ */ package org.kie.kogito.jobs.service.stream; -import java.util.Optional; -import java.util.concurrent.CompletionStage; +import java.util.List; +import java.util.stream.Collectors; -import org.eclipse.microprofile.reactive.messaging.Acknowledgment; -import org.eclipse.microprofile.reactive.messaging.Channel; -import org.eclipse.microprofile.reactive.messaging.Emitter; -import org.eclipse.microprofile.reactive.messaging.Incoming; -import org.eclipse.microprofile.reactive.messaging.OnOverflow; -import org.eclipse.microprofile.reactive.messaging.Outgoing; import org.kie.kogito.jobs.service.model.JobDetails; -import org.kie.kogito.jobs.service.model.JobExecutionResponse; -import org.kie.kogito.jobs.service.scheduler.ReactiveJobScheduler; -import org.kie.kogito.jobs.service.utils.ErrorHandling; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.smallrye.reactive.messaging.annotations.Broadcast; - +import jakarta.annotation.PostConstruct; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; -/** - * Class that configure the Consumers for Job Streams,like Job Executed, Job Error... and execute the actions for each - * received item. - */ @ApplicationScoped public class JobStreamsEventPublisher implements JobEventPublisher { private static final Logger LOGGER = LoggerFactory.getLogger(JobStreamsEventPublisher.class); @Inject - ReactiveJobScheduler scheduler; - - /** - * Publish on Stream of Job Error events - */ - @Inject - @Channel(AvailableStreams.JOB_ERROR) - @OnOverflow(value = OnOverflow.Strategy.NONE) - Emitter jobErrorEmitter; - - /** - * Publish on Stream of Job Success events - */ - @Inject - @Channel(AvailableStreams.JOB_SUCCESS) - @OnOverflow(value = OnOverflow.Strategy.NONE) - Emitter jobSuccessEmitter; - - /** - * Publish on Stream of Job Success events - */ - @Inject - @Channel(AvailableStreams.JOB_STATUS_CHANGE) - @OnOverflow(value = OnOverflow.Strategy.NONE) - Emitter jobStatusChangeEmitter; - - public JobExecutionResponse publishJobError(JobExecutionResponse response) { - jobErrorEmitter.send(response); - return response; - } + Instance jobStreamsInstance; - public JobExecutionResponse publishJobSuccess(JobExecutionResponse response) { - jobSuccessEmitter.send(response); - return response; - } - - public JobDetails publishJobStatusChange(JobDetails scheduledJob) { - jobStatusChangeEmitter.send(scheduledJob); - return scheduledJob; - } - - //Stream Processors - @Incoming(AvailableStreams.JOB_ERROR_EVENTS) - @Acknowledgment(Acknowledgment.Strategy.PRE_PROCESSING) - public CompletionStage jobErrorProcessor(JobExecutionResponse response) { - LOGGER.warn("Error received {}", response); - return ErrorHandling.skipErrorPublisherBuilder(scheduler::handleJobExecutionError, response) - .findFirst() - .run() - .thenApply(Optional::isPresent) - .exceptionally(e -> { - LOGGER.error("Error handling error {}", response, e); - return false; - }); - } - - @Incoming(AvailableStreams.JOB_SUCCESS_EVENTS) - @Acknowledgment(Acknowledgment.Strategy.PRE_PROCESSING) - public CompletionStage jobSuccessProcessor(JobExecutionResponse response) { - LOGGER.debug("Success received to be processed {}", response); - return ErrorHandling.skipErrorPublisherBuilder(scheduler::handleJobExecutionSuccess, response) - .findFirst() - .run() - .thenApply(Optional::isPresent) - .exceptionally(e -> { - LOGGER.error("Error handling error {}", response, e); - return false; - }); - } - - // Broadcast Events from Emitter to Streams - @Incoming(AvailableStreams.JOB_ERROR) - @Outgoing(AvailableStreams.JOB_ERROR_EVENTS) - @Acknowledgment(Acknowledgment.Strategy.PRE_PROCESSING) - public JobExecutionResponse jobErrorBroadcast(JobExecutionResponse response) { - LOGGER.debug("Error broadcast published {}", response); - return response; - } + private List enabledStreams; - @Incoming(AvailableStreams.JOB_SUCCESS) - @Outgoing(AvailableStreams.JOB_SUCCESS_EVENTS) - @Broadcast - @Acknowledgment(Acknowledgment.Strategy.PRE_PROCESSING) - public JobExecutionResponse jobSuccessBroadcast(JobExecutionResponse response) { - LOGGER.debug("Success broadcast published {}", response); - return response; + @PostConstruct + void init() { + this.enabledStreams = jobStreamsInstance.stream() + .filter(stream -> { + LOGGER.info("Job stream: {}, enabled: {}", stream, stream.isEnabled()); + return stream.isEnabled(); + }) + .collect(Collectors.toList()); } - @Incoming(AvailableStreams.JOB_STATUS_CHANGE) - @Outgoing(AvailableStreams.JOB_STATUS_CHANGE_EVENTS) - @Broadcast - @Acknowledgment(Acknowledgment.Strategy.PRE_PROCESSING) - public JobDetails jobStatusChangeBroadcast(JobDetails job) { - LOGGER.debug("Status change broadcast for Job {}", job); + @Override + public JobDetails publishJobStatusChange(JobDetails job) { + LOGGER.debug("publishJobStatusChange to streams, job: {}", job); + enabledStreams.forEach(jobStream -> jobStream.jobStatusChange(job)); return job; } } diff --git a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/utils/ErrorHandling.java b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/utils/ErrorHandling.java index a6bed7431c..e1051f2210 100644 --- a/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/utils/ErrorHandling.java +++ b/jobs-service/jobs-service-common/src/main/java/org/kie/kogito/jobs/service/utils/ErrorHandling.java @@ -49,7 +49,7 @@ private ErrorHandling() { public static Publisher skipErrorPublisher(Function> function, T input) { return ReactiveStreams .fromPublisher(function.apply(input)) - .onError(t -> LOGGER.warn("Error skipped when processing {}.", input, t)) + .onError(t -> LOGGER.warn(String.format("Error skipped when processing input: %s.", input), t)) .onErrorResumeWithRsPublisher(t -> ReactiveStreams. empty().buildRs()) .buildRs(); } @@ -68,7 +68,7 @@ public static Publisher skipErrorPublisher(Function PublisherBuilder skipErrorPublisherBuilder(Function> function, T input) { return function.apply(input) - .onError(t -> LOGGER.warn("Error skipped when processing {}.", input, t)) + .onError(t -> LOGGER.warn(String.format("Error skipped when processing input: %s.", input), t)) .onErrorResumeWithRsPublisher(t -> ReactiveStreams. empty().buildRs()); } } diff --git a/jobs-service/jobs-service-common/src/main/resources/META-INF/microprofile-config.properties b/jobs-service/jobs-service-common/src/main/resources/META-INF/microprofile-config.properties index 205ccc9251..a24e7828c7 100644 --- a/jobs-service/jobs-service-common/src/main/resources/META-INF/microprofile-config.properties +++ b/jobs-service/jobs-service-common/src/main/resources/META-INF/microprofile-config.properties @@ -18,7 +18,6 @@ # # Log Config -quarkus.log.level=INFO %dev.quarkus.log.category."org.kie.kogito.jobs".level=DEBUG # Console @@ -46,6 +45,8 @@ kogito.jobs-service.schedulerChunkInMinutes=10 kogito.jobs-service.loadJobIntervalInMinutes=10 kogito.jobs-service.loadJobFromCurrentTimeIntervalInMinutes=60 kogito.jobs-service.forceExecuteExpiredJobs=true +kogito.jobs-service.forceExecuteExpiredJobsOnServiceStart=true + quarkus.oidc.enabled=true quarkus.oidc.tenant-enabled=false diff --git a/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/repository/impl/BaseJobRepositoryTest.java b/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/repository/impl/BaseJobRepositoryTest.java index cfbadc4f8d..5f766a1dde 100644 --- a/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/repository/impl/BaseJobRepositoryTest.java +++ b/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/repository/impl/BaseJobRepositoryTest.java @@ -18,10 +18,10 @@ */ package org.kie.kogito.jobs.service.repository.impl; +import java.time.ZonedDateTime; import java.util.List; import java.util.UUID; import java.util.concurrent.ExecutionException; -import java.util.stream.Collectors; import java.util.stream.IntStream; import org.junit.jupiter.api.BeforeEach; @@ -29,7 +29,6 @@ import org.kie.kogito.jobs.service.api.recipient.http.HttpRecipient; import org.kie.kogito.jobs.service.api.recipient.http.HttpRecipientStringPayloadData; import org.kie.kogito.jobs.service.model.JobDetails; -import org.kie.kogito.jobs.service.model.JobExecutionResponse; import org.kie.kogito.jobs.service.model.JobStatus; import org.kie.kogito.jobs.service.model.Recipient; import org.kie.kogito.jobs.service.model.RecipientInstance; @@ -40,6 +39,7 @@ import org.kie.kogito.timer.impl.PointInTimeTrigger; import static org.assertj.core.api.Assertions.assertThat; +import static org.kie.kogito.jobs.service.repository.ReactiveJobRepository.SortTerm.byFireTime; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; @@ -59,8 +59,6 @@ public void setUp() throws Exception { public JobEventPublisher mockJobEventPublisher() { final JobEventPublisher mock = mock(JobEventPublisher.class); lenient().when(mock.publishJobStatusChange(any(JobDetails.class))).thenAnswer(a -> a.getArgument(0)); - lenient().when(mock.publishJobSuccess(any(JobExecutionResponse.class))).thenAnswer(a -> a.getArgument(0)); - lenient().when(mock.publishJobError(any(JobExecutionResponse.class))).thenAnswer(a -> a.getArgument(0)); return mock; } @@ -69,7 +67,7 @@ public JobEventPublisher mockJobEventPublisher() { @Test void testSaveAndGet() throws ExecutionException, InterruptedException { JobDetails scheduledJob = tested().get(ID).toCompletableFuture().get(); - assertThat(scheduledJob).isEqualTo(job); + assertEqualsToReturnedJob(job, scheduledJob); JobDetails notFound = tested().get(UUID.randomUUID().toString()).toCompletableFuture().get(); assertThat(notFound).isNull(); } @@ -97,35 +95,30 @@ void testExists() throws ExecutionException, InterruptedException { @Test void testDelete() throws ExecutionException, InterruptedException { - JobDetails scheduledJob = tested().delete(ID).toCompletableFuture().get(); - assertThat(scheduledJob).isEqualTo(job); + JobDetails deletedJob = tested().delete(ID).toCompletableFuture().get(); + assertEqualsToReturnedJob(job, deletedJob); JobDetails notFound = tested().get(ID).toCompletableFuture().get(); assertThat(notFound).isNull(); } - @Test - void testFindAll() throws ExecutionException, InterruptedException { - List jobs = tested().findAll().toList().run().toCompletableFuture().get(); - assertThat(jobs.size()).isEqualTo(1); - assertThat(jobs.get(0)).isEqualTo(job); - } - @Test void testFindByStatusBetweenDates() throws ExecutionException, InterruptedException { + ZonedDateTime now = DateUtil.now(); List jobs = IntStream.rangeClosed(1, 10).boxed() .map(id -> JobDetails.builder() .status(JobStatus.SCHEDULED) .id(String.valueOf(id)) .priority(id) - .trigger(new PointInTimeTrigger(DateUtil.now().plusMinutes(id).toInstant().toEpochMilli(), null, null)) + .trigger(new PointInTimeTrigger(now.plusMinutes(id).toInstant().toEpochMilli(), null, null)) .priority(id) .build()) - .peek(j -> FunctionsUtil.unchecked((t) -> tested().save(j).toCompletableFuture().get()).apply(null)) - .collect(Collectors.toList()); + .map(j -> FunctionsUtil.unchecked((t) -> tested().save(j).toCompletableFuture().get()).apply(null)) + .toList(); - final List fetched = tested().findByStatusBetweenDatesOrderByPriority(DateUtil.now(), - DateUtil.now().plusMinutes(5).plusSeconds(1), - JobStatus.SCHEDULED) + final List fetched = tested().findByStatusBetweenDates(now, + now.plusMinutes(5), + new JobStatus[] { JobStatus.SCHEDULED }, + new ReactiveJobRepository.SortTerm[] { byFireTime(true) }) .toList() .run() .toCompletableFuture() @@ -133,13 +126,29 @@ void testFindByStatusBetweenDates() throws ExecutionException, InterruptedExcept assertThat(fetched.size()).isEqualTo(5); - IntStream.rangeClosed(0, 4).forEach( - i -> assertThat(fetched.get(i)).isEqualTo(jobs.get(fetched.size() - 1 - i))); + for (int i = 0; i < fetched.size(); i++) { + assertThat(fetched.get(i)).isEqualTo(jobs.get(i)); + } + + final List fetchedDesc = tested().findByStatusBetweenDates(now, + now.plusMinutes(5), + new JobStatus[] { JobStatus.SCHEDULED }, + new ReactiveJobRepository.SortTerm[] { byFireTime(false) }) + .toList() + .run() + .toCompletableFuture() + .get(); + + assertThat(fetchedDesc.size()).isEqualTo(5); + for (int i = 0; i < fetchedDesc.size(); i++) { + assertThat(fetchedDesc.get(i)).isEqualTo(jobs.get(4 - i)); + } //not found test - List fetchedNotFound = tested().findByStatusBetweenDatesOrderByPriority(DateUtil.now(), - DateUtil.now().plusMinutes(5).plusSeconds(1), - JobStatus.CANCELED) + List fetchedNotFound = tested().findByStatusBetweenDates(now, + now.plusMinutes(5), + new JobStatus[] { JobStatus.CANCELED }, + new ReactiveJobRepository.SortTerm[] { byFireTime(true) }) .toList() .run() .toCompletableFuture() @@ -147,9 +156,10 @@ void testFindByStatusBetweenDates() throws ExecutionException, InterruptedExcept assertThat(fetchedNotFound.size()).isZero(); - fetchedNotFound = tested().findByStatusBetweenDatesOrderByPriority(DateUtil.now().plusDays(1), - DateUtil.now().plusDays(2), - JobStatus.SCHEDULED) + fetchedNotFound = tested().findByStatusBetweenDates(now.plusMinutes(10).plusSeconds(1), + now.plusMinutes(20), + new JobStatus[] { JobStatus.SCHEDULED }, + new ReactiveJobRepository.SortTerm[] { byFireTime(true) }) .toList() .run() .toCompletableFuture() @@ -174,4 +184,19 @@ void testMergeCallbackEndpoint() throws Exception { assertThat(merged.getId()).isEqualTo(job.getId()); assertThat(merged.getTrigger().hasNextFireTime()).isEqualTo(job.getTrigger().hasNextFireTime()); } + + private static void assertEqualsToReturnedJob(JobDetails job, JobDetails returnedJob) { + assertThat(job.getId()).isEqualTo(returnedJob.getId()); + assertThat(job.getCorrelationId()).isEqualTo(returnedJob.getCorrelationId()); + assertThat(job.getStatus()).isEqualTo(returnedJob.getStatus()); + assertThat(job.getScheduledId()).isEqualTo(returnedJob.getScheduledId()); + assertThat(job.getRetries()).isEqualTo(returnedJob.getRetries()); + assertThat(job.getExecutionCounter()).isEqualTo(returnedJob.getExecutionCounter()); + assertThat(job.getExecutionTimeout()).isEqualTo(returnedJob.getExecutionTimeout()); + assertThat(job.getExecutionTimeoutUnit()).isEqualTo(returnedJob.getExecutionTimeoutUnit()); + assertThat(job.getTrigger().hasNextFireTime()).isEqualTo(returnedJob.getTrigger().hasNextFireTime()); + assertThat(job.getRecipient()).isEqualTo(returnedJob.getRecipient()); + assertThat(returnedJob.getCreated()).isNotNull(); + assertThat(returnedJob.getLastUpdate()).isNotNull(); + } } diff --git a/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/repository/marshaller/JobDetailsMarshallerTest.java b/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/repository/marshaller/JobDetailsMarshallerTest.java index a55eb1e8b9..b0c30403c3 100644 --- a/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/repository/marshaller/JobDetailsMarshallerTest.java +++ b/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/repository/marshaller/JobDetailsMarshallerTest.java @@ -54,8 +54,8 @@ void setUp() { String id = "testId"; String correlationId = "testCorrelationId"; JobStatus status = JobStatus.SCHEDULED; - Date date = new Date(); - ZonedDateTime lastUpdate = ZonedDateTime.ofInstant(date.toInstant(), DEFAULT_ZONE); + Date lastUpdateDate = new Date(); + ZonedDateTime lastUpdate = ZonedDateTime.ofInstant(lastUpdateDate.toInstant(), DEFAULT_ZONE); Integer retries = 2; Integer priority = 3; Integer executionCounter = 4; @@ -65,6 +65,8 @@ void setUp() { Trigger trigger = new PointInTimeTrigger(new Date().toInstant().toEpochMilli(), null, null); Long executionTimeout = 10L; ChronoUnit executionTimeoutUnit = ChronoUnit.SECONDS; + Date createdDate = new Date(); + ZonedDateTime created = ZonedDateTime.ofInstant(createdDate.toInstant(), DEFAULT_ZONE); jobDetails = JobDetails.builder() .id(id) @@ -79,13 +81,14 @@ void setUp() { .trigger(trigger) .executionTimeout(executionTimeout) .executionTimeoutUnit(executionTimeoutUnit) + .created(created) .build(); jsonObject = new JsonObject() .put("id", id) .put("correlationId", correlationId) .put("status", status.name()) - .put("lastUpdate", date.getTime()) + .put("lastUpdate", lastUpdateDate.getTime()) .put("retries", retries) .put("executionCounter", executionCounter) .put("scheduledId", scheduledId) @@ -97,7 +100,8 @@ void setUp() { .put("nextFireTime", trigger.hasNextFireTime().getTime()) .put("classType", PointInTimeTrigger.class.getName())) .put("executionTimeout", executionTimeout) - .put("executionTimeoutUnit", executionTimeoutUnit.name()); + .put("executionTimeoutUnit", executionTimeoutUnit.name()) + .put("created", createdDate.getTime()); } @Test diff --git a/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/scheduler/BaseTimerJobSchedulerTest.java b/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/scheduler/BaseTimerJobSchedulerTest.java index 62ebd9b2c8..aab159123a 100644 --- a/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/scheduler/BaseTimerJobSchedulerTest.java +++ b/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/scheduler/BaseTimerJobSchedulerTest.java @@ -81,7 +81,7 @@ public abstract class BaseTimerJobSchedulerTest { public CompletionStage scheduled; @Captor - private ArgumentCaptor> delayCaptor; + private ArgumentCaptor delayCaptor; @Captor private ArgumentCaptor scheduleCaptor; @@ -102,7 +102,7 @@ public abstract class BaseTimerJobSchedulerTest { @BeforeEach public void setUp() { tested().schedulerChunkInMinutes = 5; - tested().forceExecuteExpiredJobs = Optional.of(Boolean.FALSE); + tested().forceExecuteExpiredJobs = false; //expiration on the current scheduler chunk expirationTime = DateUtil.now().plusMinutes(tested().schedulerChunkInMinutes - 1); errorResponse = JobExecutionResponse.builder() @@ -177,7 +177,7 @@ private void testExistingJob(boolean expired, JobStatus jobStatus) { verify(jobRepository, expired || SCHEDULED.equals(jobStatus) ? atLeastOnce() : never()).delete(any(JobDetails.class)); verify(tested(), expired ? never() : times(1)).doSchedule(eq(scheduledJob), delayCaptor.capture()); - verify(jobRepository, expired ? never() : times(1)).save(scheduleCaptor.capture()); + verify(jobRepository, expired ? never() : times(2)).save(scheduleCaptor.capture()); //assert always a scheduled job is canceled (periodic or not) Optional.ofNullable(jobStatus) @@ -208,7 +208,7 @@ void testScheduleExistingJobRetryExpired() { @Test void testScheduleExistingJobRetry() { - testExistingJob(false, JobStatus.RETRY); + testExistingJob(false, SCHEDULED); } @Test @@ -253,7 +253,7 @@ void testHandleJobExecutionSuccessPeriodic() { verify(tested(), never()).cancel(scheduleCaptorFuture.capture()); subscribeOn(executionSuccess.buildRs()); - verify(jobRepository, times(2)).save(scheduleCaptor.capture()); + verify(jobRepository, times(3)).save(scheduleCaptor.capture()); JobDetails scheduleCaptorValue = scheduleCaptor.getValue(); assertThat(scheduleCaptorValue.getStatus()).isEqualTo(SCHEDULED); assertThat(scheduleCaptorValue.getExecutionCounter()).isEqualTo(1); @@ -378,7 +378,7 @@ void testForceExpiredJobToBeExecuted() { verify(tested(), never()).doSchedule(eq(scheduledJob), delayCaptor.capture()); //testing with forcing enabled - tested().forceExecuteExpiredJobs = Optional.of(Boolean.TRUE); + tested().forceExecuteExpiredJobs = true; subscribeOn(tested().schedule(scheduledJob)); verify(tested(), times(1)).doSchedule(eq(scheduledJob), delayCaptor.capture()); } diff --git a/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/scheduler/JobSchedulerManagerTest.java b/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/scheduler/JobSchedulerManagerTest.java index 17d7b85282..dd7c46cf1a 100644 --- a/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/scheduler/JobSchedulerManagerTest.java +++ b/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/scheduler/JobSchedulerManagerTest.java @@ -46,7 +46,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -87,17 +89,18 @@ void setUp() { this.scheduledJob = JobDetails .builder() .id(JOB_ID) + .status(JobStatus.SCHEDULED) .trigger(new PointInTimeTrigger(System.currentTimeMillis(), null, null)) .build(); - lenient().when(repository.findByStatusBetweenDatesOrderByPriority(any(ZonedDateTime.class), + lenient().when(repository.findByStatusBetweenDates(any(ZonedDateTime.class), any(ZonedDateTime.class), - any(JobStatus.class), - any(JobStatus.class))) + any(JobStatus[].class), + any(ReactiveJobRepository.SortTerm[].class))) .thenReturn(ReactiveStreams.of(scheduledJob)); lenient().when(scheduler.scheduled(JOB_ID)) .thenReturn(Optional.empty()); - lenient().when(scheduler.schedule(scheduledJob)) + lenient().when(scheduler.internalSchedule(eq(scheduledJob), anyBoolean())) .thenReturn(ReactiveStreams.of(scheduledJob).buildRs()); ArgumentCaptor action = ArgumentCaptor.forClass(Runnable.class); lenient().doAnswer(a -> { @@ -110,13 +113,13 @@ void setUp() { } @Test - void testLoadJobDetailss() { + void testLoadJobDetails() { tested.loadJobDetails(); - verify(scheduler).schedule(scheduledJob); + verify(scheduler).internalSchedule(scheduledJob, true); } @Test - void testLoadAlreadyJobDetailss() { + void testLoadAlreadyJobDetails() { when(scheduler.scheduled(JOB_ID)).thenReturn(Optional.of(DateUtil.now())); tested.loadJobDetails(); diff --git a/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/scheduler/impl/TimerDelegateJobSchedulerTest.java b/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/scheduler/impl/TimerDelegateJobSchedulerTest.java index f272a790ab..28b21c6189 100644 --- a/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/scheduler/impl/TimerDelegateJobSchedulerTest.java +++ b/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/scheduler/impl/TimerDelegateJobSchedulerTest.java @@ -18,7 +18,6 @@ */ package org.kie.kogito.jobs.service.scheduler.impl; -import java.util.Optional; import java.util.UUID; import org.eclipse.microprofile.reactive.streams.operators.PublisherBuilder; @@ -76,7 +75,7 @@ public BaseTimerJobScheduler tested() { @Test void testDoSchedule() { - PublisherBuilder schedule = tested.doSchedule(scheduledJob, Optional.empty()); + PublisherBuilder schedule = tested.doSchedule(scheduledJob, scheduledJob.getTrigger()); Multi.createFrom().publisher(publisher(schedule.buildRs())).subscribe().with(dummyCallback(), dummyCallback()); verify(timer).scheduleJob(any(DelegateJob.class), any(JobDetailsContext.class), eq(scheduledJob.getTrigger())); } diff --git a/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/scheduler/impl/VertxTimerServiceSchedulerTest.java b/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/scheduler/impl/VertxTimerServiceSchedulerTest.java index 3978fccb1c..b6c1421977 100644 --- a/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/scheduler/impl/VertxTimerServiceSchedulerTest.java +++ b/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/scheduler/impl/VertxTimerServiceSchedulerTest.java @@ -21,15 +21,19 @@ import java.time.ZonedDateTime; import java.util.concurrent.TimeUnit; +import org.eclipse.microprofile.reactive.streams.operators.PublisherBuilder; +import org.eclipse.microprofile.reactive.streams.operators.ReactiveStreams; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.kie.kogito.jobs.service.executor.JobExecutor; import org.kie.kogito.jobs.service.executor.JobExecutorResolver; import org.kie.kogito.jobs.service.job.DelegateJob; import org.kie.kogito.jobs.service.model.JobDetails; import org.kie.kogito.jobs.service.model.JobDetailsContext; +import org.kie.kogito.jobs.service.model.JobExecutionResponse; import org.kie.kogito.jobs.service.model.ManageableJobHandle; -import org.kie.kogito.jobs.service.stream.JobEventPublisher; +import org.kie.kogito.jobs.service.scheduler.ReactiveJobScheduler; import org.kie.kogito.jobs.service.utils.DateUtil; import org.kie.kogito.timer.Job; import org.kie.kogito.timer.JobContext; @@ -41,11 +45,13 @@ import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; +import io.smallrye.mutiny.Uni; import io.vertx.mutiny.core.Vertx; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.given; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -62,7 +68,10 @@ class VertxTimerServiceSchedulerTest { private JobExecutorResolver jobExecutorResolver; @Mock - private JobEventPublisher jobEventPublisher; + private JobExecutor jobExecutor; + + @Mock + private ReactiveJobScheduler reactiveJobScheduler; @Captor private ArgumentCaptor jobCaptor; @@ -82,6 +91,12 @@ public void setUp() { void testScheduleJob() { ZonedDateTime time = DateUtil.now().plusSeconds(1); final ManageableJobHandle handle = schedule(time); + doReturn(jobExecutor).when(jobExecutorResolver).get(any()); + JobExecutionResponse response = new JobExecutionResponse(); + Uni result = Uni.createFrom().item(response); + PublisherBuilder executionSuccessPublisherBuilder = ReactiveStreams.of(jobDetails); + doReturn(executionSuccessPublisherBuilder).when(reactiveJobScheduler).handleJobExecutionSuccess(response); + doReturn(result).when(jobExecutor).execute(jobDetails); verify(vertx).setTimer(timeCaptor.capture(), any()); assertThat(timeCaptor.getValue()).isGreaterThanOrEqualTo(time.toInstant().minusMillis(System.currentTimeMillis()).toEpochMilli()); given().await() @@ -110,7 +125,7 @@ private ManageableJobHandle schedule(ZonedDateTime time) { trigger = new PointInTimeTrigger(timestamp, null, null); jobDetails = JobDetails.builder().build(); context = new JobDetailsContext(jobDetails); - job = new DelegateJob(jobExecutorResolver, jobEventPublisher); + job = new DelegateJob(jobExecutorResolver, reactiveJobScheduler); return tested.scheduleJob(job, context, trigger); } diff --git a/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/stream/AbstractJobStreamsTest.java b/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/stream/AbstractJobStreamsTest.java index 4295530a29..f7ac6ed1ce 100644 --- a/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/stream/AbstractJobStreamsTest.java +++ b/jobs-service/jobs-service-common/src/test/java/org/kie/kogito/jobs/service/stream/AbstractJobStreamsTest.java @@ -44,6 +44,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; @@ -94,7 +96,7 @@ void jobStatusChangeWithAck() throws Exception { doReturn(SERIALIZED_MESSAGE).when(objectMapper).writeValueAsString(any()); Message message = executeStatusChange(job); message.ack(); - verify(jobStreams).onAck(job); + verify(jobStreams).onAck(anyString(), eq(job)); } @Test @@ -141,14 +143,14 @@ void jobStatusChangeWithUnexpectedErrorAndContinue() throws Exception { assertThat(message.getPayload()).isEqualTo(SERIALIZED_MESSAGE); assertExpectedMetadata(message); message.ack(); - verify(jobStreams).onAck(job); + verify(jobStreams).onAck(anyString(), eq(job)); } private void executeStatusChangeWithUnexpectedError(JobDetails job) throws Exception { doThrow(new RuntimeException("Unexpected error")).when(objectMapper).writeValueAsString(any()); jobStreams.jobStatusChange(job); - verify(jobStreams, never()).onAck(any()); + verify(jobStreams, never()).onAck(any(), any()); verify(jobStreams, never()).onNack(any(), any()); } diff --git a/jobs-service/jobs-service-infinispan/src/main/java/org/kie/kogito/jobs/service/repository/infinispan/InfinispanJobRepository.java b/jobs-service/jobs-service-infinispan/src/main/java/org/kie/kogito/jobs/service/repository/infinispan/InfinispanJobRepository.java index 8b9a25fa71..a2ee936edc 100644 --- a/jobs-service/jobs-service-infinispan/src/main/java/org/kie/kogito/jobs/service/repository/infinispan/InfinispanJobRepository.java +++ b/jobs-service/jobs-service-infinispan/src/main/java/org/kie/kogito/jobs/service/repository/infinispan/InfinispanJobRepository.java @@ -22,6 +22,7 @@ import java.util.Arrays; import java.util.concurrent.CompletionStage; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.eclipse.microprofile.reactive.streams.operators.PublisherBuilder; import org.eclipse.microprofile.reactive.streams.operators.ReactiveStreams; @@ -44,6 +45,7 @@ import jakarta.inject.Inject; import static org.kie.kogito.jobs.service.repository.infinispan.InfinispanConfiguration.Caches.JOB_DETAILS; +import static org.kie.kogito.jobs.service.utils.ModelUtil.jobWithCreatedAndLastUpdate; @ApplicationScoped public class InfinispanJobRepository extends BaseReactiveJobRepository implements ReactiveJobRepository { @@ -71,8 +73,12 @@ void init(@Observes InfinispanInitialized event) { @Override public CompletionStage doSave(JobDetails job) { - return runAsync(() -> cache.put(job.getId(), job)) - .thenApply(j -> job); + return runAsync(() -> { + boolean isNew = !cache.containsKey(job.getId()); + JobDetails timeStampedJob = jobWithCreatedAndLastUpdate(isNew, job); + cache.put(timeStampedJob.getId(), timeStampedJob); + return timeStampedJob; + }); } @Override @@ -93,37 +99,56 @@ public CompletionStage delete(String id) { } @Override - public PublisherBuilder findAll() { - Query query = queryFactory. create("from job.service.JobDetails"); + public PublisherBuilder findByStatusBetweenDates(ZonedDateTime fromFireTime, + ZonedDateTime toFireTime, + JobStatus[] status, + SortTerm[] orderBy) { + + String statusFilter = (status != null && status.length > 0) ? createStatusFilter(status) : null; + String fireTimeFilter = createFireTimeFilter("from", "to"); + String orderByCriteria = (orderBy != null && orderBy.length > 0) ? createOrderBy("j", orderBy) : ""; + + StringBuilder queryFilter = new StringBuilder(); + if (statusFilter != null) { + queryFilter.append(statusFilter); + queryFilter.append(" and "); + } + queryFilter.append(fireTimeFilter); + + String findQuery = "from job.service.JobDetails j" + + " where " + queryFilter + + " " + orderByCriteria; + + Query query = queryFactory.create(findQuery); + query.setParameter("from", fromFireTime.toInstant().toEpochMilli()); + query.setParameter("to", toFireTime.toInstant().toEpochMilli()); return ReactiveStreams.fromIterable(query.execute().list()); } - @Override - public PublisherBuilder findByStatus(JobStatus... status) { - Query query = queryFactory.create("from job.service.JobDetails j " + - "where " + - "j.status in (" + createStatusQuery(status) + ")"); - return ReactiveStreams.fromIterable(query.execute().list()); + private static String createFireTimeFilter(String fromParam, String toParam) { + return String.format("j.nextFireTime >= :%s and j.nextFireTime <= :%s ", fromParam, toParam); } - @Override - public PublisherBuilder findByStatusBetweenDatesOrderByPriority(ZonedDateTime from, ZonedDateTime to, - JobStatus... status) { - Query query = queryFactory.create("from job.service.JobDetails j " + - "where " + - "j.trigger.nextFireTime > :from " + - "and j.trigger.nextFireTime < :to " + - "and j.status in (" + createStatusQuery(status) + ") " + - "order by j.priority desc"); - query.setParameter("to", to.toInstant().toEpochMilli()); - query.setParameter("from", from.toInstant().toEpochMilli()); - return ReactiveStreams.fromIterable(query.execute().list()); + private static String createStatusFilter(JobStatus... status) { + return Arrays.stream(status).map(JobStatus::name) + .collect(Collectors.joining("', '", "j.status IN ('", "')")); + } + + private static String createOrderBy(String objName, SortTerm[] sortTerms) { + return Stream.of(sortTerms).map(term -> createOrderByTerm(objName, term)) + .collect(Collectors.joining(", ", "order by ", "")); + } + + private static String createOrderByTerm(String objName, SortTerm sortTerm) { + return objName + "." + toColumName(sortTerm.getField()) + (sortTerm.isAsc() ? " asc" : " desc"); } - //building the query sentence for the status IN (not supported to use array in setParameter on the query) - private String createStatusQuery(JobStatus[] status) { - return Arrays.stream(status) - .map(JobStatus::name) - .collect(Collectors.joining("\', \'", "\'", "\'")); + private static String toColumName(SortTermField field) { + return switch (field) { + case FIRE_TIME -> "nextFireTime"; + case CREATED -> "created"; + case ID -> "id"; + default -> throw new IllegalArgumentException("No colum name is defined for field: " + field); + }; } } diff --git a/jobs-service/jobs-service-infinispan/src/main/java/org/kie/kogito/jobs/service/repository/infinispan/marshaller/JobDetailsMarshaller.java b/jobs-service/jobs-service-infinispan/src/main/java/org/kie/kogito/jobs/service/repository/infinispan/marshaller/JobDetailsMarshaller.java index 64d8e768a4..10ca6045c8 100644 --- a/jobs-service/jobs-service-infinispan/src/main/java/org/kie/kogito/jobs/service/repository/infinispan/marshaller/JobDetailsMarshaller.java +++ b/jobs-service/jobs-service-infinispan/src/main/java/org/kie/kogito/jobs/service/repository/infinispan/marshaller/JobDetailsMarshaller.java @@ -66,6 +66,8 @@ public void writeTo(ProtoStreamWriter writer, JobDetails job) throws IOException writer.writeObject("trigger", job.getTrigger(), getInterface(job.getTrigger())); writer.writeLong("executionTimeout", job.getExecutionTimeout()); writer.writeString("executionTimeoutUnit", job.getExecutionTimeoutUnit() != null ? job.getExecutionTimeoutUnit().name() : null); + writer.writeInstant("nextFireTime", toInstant(job.getTrigger().hasNextFireTime())); + writer.writeInstant("created", zonedDateTimeToInstant(job.getCreated())); } public Class getInterface(Object object) { @@ -90,6 +92,7 @@ public JobDetails readFrom(ProtoStreamReader reader) throws IOException { Trigger trigger = reader.readObject("trigger", Trigger.class); Long executionTimeout = reader.readLong("executionTimeout"); String executionTimeoutUnit = reader.readString("executionTimeoutUnit"); + ZonedDateTime created = instantToZonedDateTime(reader.readInstant("created")); return JobDetails.builder() .id(id) @@ -104,6 +107,7 @@ public JobDetails readFrom(ProtoStreamReader reader) throws IOException { .trigger(trigger) .executionTimeout(executionTimeout) .executionTimeoutUnit(executionTimeoutUnit != null ? ChronoUnit.valueOf(executionTimeoutUnit) : null) + .created(created) .build(); } } diff --git a/jobs-service/jobs-service-infinispan/src/main/resources/META-INF/library.proto b/jobs-service/jobs-service-infinispan/src/main/resources/META-INF/library.proto index fa0c8ae86c..7f08eddad5 100644 --- a/jobs-service/jobs-service-infinispan/src/main/resources/META-INF/library.proto +++ b/jobs-service/jobs-service-infinispan/src/main/resources/META-INF/library.proto @@ -2,6 +2,7 @@ package job.service; /* @Indexed */ message JobDetails { + /* @Field(store = Store.YES) @SortableField */ optional string id = 1; optional string correlationId = 2; /* @Field(store = Store.YES) */ @@ -18,6 +19,10 @@ message JobDetails { optional string type = 11;//enum optional int64 executionTimeout = 12; optional string executionTimeoutUnit = 13; + /* @Field(store = Store.YES) @SortableField */ + optional int64 nextFireTime = 14; + /* @Field(store = Store.YES) @SortableField */ + optional int64 created = 15; } /* @Indexed */ diff --git a/jobs-service/jobs-service-internal-api/src/main/java/org/kie/kogito/jobs/service/model/JobDetails.java b/jobs-service/jobs-service-internal-api/src/main/java/org/kie/kogito/jobs/service/model/JobDetails.java index afa397a07d..bc8016a492 100644 --- a/jobs-service/jobs-service-internal-api/src/main/java/org/kie/kogito/jobs/service/model/JobDetails.java +++ b/jobs-service/jobs-service-internal-api/src/main/java/org/kie/kogito/jobs/service/model/JobDetails.java @@ -42,11 +42,12 @@ public class JobDetails { private Trigger trigger;//when/how it should be executed private Long executionTimeout; private ChronoUnit executionTimeoutUnit; + private ZonedDateTime created; @SuppressWarnings("java:S107") protected JobDetails(String id, String correlationId, JobStatus status, ZonedDateTime lastUpdate, Integer retries, Integer executionCounter, String scheduledId, Recipient recipient, Trigger trigger, Integer priority, - Long executionTimeout, ChronoUnit executionTimeoutUnit) { + Long executionTimeout, ChronoUnit executionTimeoutUnit, ZonedDateTime created) { this.id = id; this.correlationId = correlationId; this.status = status; @@ -59,6 +60,7 @@ protected JobDetails(String id, String correlationId, JobStatus status, ZonedDat this.priority = priority; this.executionTimeout = executionTimeout; this.executionTimeoutUnit = executionTimeoutUnit; + this.created = created; } public String getId() { @@ -109,6 +111,10 @@ public ChronoUnit getExecutionTimeoutUnit() { return executionTimeoutUnit; } + public ZonedDateTime getCreated() { + return created; + } + public static JobDetailsBuilder builder() { return new JobDetailsBuilder(); } @@ -132,13 +138,14 @@ public boolean equals(Object o) { Objects.equals(getRecipient(), that.getRecipient()) && Objects.equals(getTrigger().hasNextFireTime(), that.getTrigger().hasNextFireTime()) && Objects.equals(getExecutionTimeout(), that.getExecutionTimeout()) && - Objects.equals(getExecutionTimeoutUnit(), that.getExecutionTimeoutUnit()); + Objects.equals(getExecutionTimeoutUnit(), that.getExecutionTimeoutUnit()) && + Objects.equals(getCreated(), that.getCreated()); } @Override public int hashCode() { return Objects.hash(getId(), getCorrelationId(), getStatus(), getRetries(), getExecutionCounter(), - getScheduledId(), getRecipient(), getTrigger(), getExecutionTimeout(), getExecutionTimeoutUnit()); + getScheduledId(), getRecipient(), getTrigger(), getExecutionTimeout(), getExecutionTimeoutUnit(), getCreated()); } @Override @@ -155,6 +162,7 @@ public String toString() { .add("trigger=" + trigger) .add("executionTimeout=" + executionTimeout) .add("executionTimeoutUnit=" + executionTimeoutUnit) + .add("created=" + created) .toString(); } } diff --git a/jobs-service/jobs-service-internal-api/src/main/java/org/kie/kogito/jobs/service/model/JobDetailsBuilder.java b/jobs-service/jobs-service-internal-api/src/main/java/org/kie/kogito/jobs/service/model/JobDetailsBuilder.java index 832ed1ee12..12061d4417 100644 --- a/jobs-service/jobs-service-internal-api/src/main/java/org/kie/kogito/jobs/service/model/JobDetailsBuilder.java +++ b/jobs-service/jobs-service-internal-api/src/main/java/org/kie/kogito/jobs/service/model/JobDetailsBuilder.java @@ -38,6 +38,7 @@ public class JobDetailsBuilder { private Integer priority; private Long executionTimeout; private ChronoUnit executionTimeoutUnit; + private ZonedDateTime created; public JobDetailsBuilder id(String id) { this.id = id; @@ -99,9 +100,14 @@ public JobDetailsBuilder executionTimeoutUnit(ChronoUnit executionTimeoutUnit) { return this; } + public JobDetailsBuilder created(ZonedDateTime created) { + this.created = created; + return this; + } + public JobDetails build() { return new JobDetails(id, correlationId, status, lastUpdate, retries, executionCounter, scheduledId, - recipient, trigger, priority, executionTimeout, executionTimeoutUnit); + recipient, trigger, priority, executionTimeout, executionTimeoutUnit, created); } public JobDetailsBuilder of(JobDetails jobDetails) { @@ -116,7 +122,8 @@ public JobDetailsBuilder of(JobDetails jobDetails) { .trigger(jobDetails.getTrigger()) .priority(jobDetails.getPriority()) .executionTimeout(jobDetails.getExecutionTimeout()) - .executionTimeoutUnit(jobDetails.getExecutionTimeoutUnit()); + .executionTimeoutUnit(jobDetails.getExecutionTimeoutUnit()) + .created(jobDetails.getCreated()); } public JobDetailsBuilder incrementRetries() { diff --git a/jobs-service/jobs-service-internal-api/src/main/java/org/kie/kogito/jobs/service/utils/ModelUtil.java b/jobs-service/jobs-service-internal-api/src/main/java/org/kie/kogito/jobs/service/utils/ModelUtil.java index 23353e68d1..ab5322c135 100644 --- a/jobs-service/jobs-service-internal-api/src/main/java/org/kie/kogito/jobs/service/utils/ModelUtil.java +++ b/jobs-service/jobs-service-internal-api/src/main/java/org/kie/kogito/jobs/service/utils/ModelUtil.java @@ -18,11 +18,15 @@ */ package org.kie.kogito.jobs.service.utils; +import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; import java.util.Objects; import org.kie.kogito.jobs.service.adapter.JobDetailsAdapter; import org.kie.kogito.jobs.service.api.Job; +import org.kie.kogito.jobs.service.model.JobDetails; +import org.kie.kogito.jobs.service.model.JobStatus; +import org.kie.kogito.jobs.service.model.ManageableJobHandle; public class ModelUtil { @@ -37,4 +41,25 @@ public static Long getExecutionTimeoutInMillis(Job job) { ChronoUnit chronoUnit = job.getExecutionTimeoutUnit() != null ? JobDetailsAdapter.TemporalUnitAdapter.toChronoUnit(job.getExecutionTimeoutUnit()) : ChronoUnit.MILLIS; return chronoUnit.getDuration().multipliedBy(job.getExecutionTimeout()).toMillis(); } + + public static JobDetails jobWithStatus(JobDetails job, JobStatus status) { + return JobDetails.builder().of(job).status(status).build(); + } + + public static JobDetails jobWithStatusAndHandle(JobDetails job, JobStatus status, ManageableJobHandle handle) { + return JobDetails.builder().of(job).status(status).scheduledId(String.valueOf(handle.getId())).build(); + } + + public static JobDetails jobWithCreatedAndLastUpdate(boolean isNew, JobDetails job) { + ZonedDateTime now = DateUtil.now(); + return isNew ? jobWithCreated(job, now, now) : jobWithLastUpdate(job, now); + } + + public static JobDetails jobWithCreated(JobDetails job, ZonedDateTime created, ZonedDateTime lastUpdate) { + return JobDetails.builder().of(job).created(created).lastUpdate(lastUpdate).build(); + } + + public static JobDetails jobWithLastUpdate(JobDetails job, ZonedDateTime lastUpdate) { + return JobDetails.builder().of(job).lastUpdate(lastUpdate).build(); + } } diff --git a/jobs-service/jobs-service-messaging-http/src/main/java/org/kie/kogito/jobs/service/messaging/http/stream/HttpJobStreams.java b/jobs-service/jobs-service-messaging-http/src/main/java/org/kie/kogito/jobs/service/messaging/http/stream/HttpJobStreams.java index 61c959509c..52bb50d35f 100644 --- a/jobs-service/jobs-service-messaging-http/src/main/java/org/kie/kogito/jobs/service/messaging/http/stream/HttpJobStreams.java +++ b/jobs-service/jobs-service-messaging-http/src/main/java/org/kie/kogito/jobs/service/messaging/http/stream/HttpJobStreams.java @@ -22,15 +22,14 @@ import java.util.function.Supplier; import org.eclipse.microprofile.config.inject.ConfigProperty; -import org.eclipse.microprofile.reactive.messaging.Acknowledgment; import org.eclipse.microprofile.reactive.messaging.Channel; import org.eclipse.microprofile.reactive.messaging.Emitter; -import org.eclipse.microprofile.reactive.messaging.Incoming; import org.eclipse.microprofile.reactive.messaging.Message; import org.eclipse.microprofile.reactive.messaging.OnOverflow; import org.kie.kogito.jobs.service.model.JobDetails; import org.kie.kogito.jobs.service.stream.AbstractJobStreams; -import org.kie.kogito.jobs.service.stream.AvailableStreams; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.ObjectMapper; @@ -47,6 +46,8 @@ public class HttpJobStreams extends AbstractJobStreams { public static final String PUBLISH_EVENTS_CONFIG_KEY = "kogito.jobs-service.http.job-status-change-events"; public static final String JOB_STATUS_CHANGE_EVENTS_HTTP = "kogito-job-service-job-status-events-http"; + private static final Logger LOGGER = LoggerFactory.getLogger(HttpJobStreams.class); + /** * Metadata to include the content-type for structured CloudEvents messages */ @@ -57,15 +58,14 @@ public class HttpJobStreams extends AbstractJobStreams { @Inject public HttpJobStreams(ObjectMapper objectMapper, @ConfigProperty(name = PUBLISH_EVENTS_CONFIG_KEY) Optional config, - @Channel(JOB_STATUS_CHANGE_EVENTS_HTTP) @OnOverflow(value = OnOverflow.Strategy.LATEST) Emitter emitter, + @Channel(JOB_STATUS_CHANGE_EVENTS_HTTP) @OnOverflow(value = OnOverflow.Strategy.UNBOUNDED_BUFFER) Emitter emitter, @ConfigProperty(name = "kogito.service.url", defaultValue = "http://localhost:8080") String url) { super(objectMapper, config.orElse(false), emitter, url); } - @Incoming(AvailableStreams.JOB_STATUS_CHANGE_EVENTS) - @Acknowledgment(Acknowledgment.Strategy.PRE_PROCESSING) @Override public void jobStatusChange(JobDetails job) { + LOGGER.debug("jobStatusChange call received, enabled: {}, job: {}", enabled, job); super.jobStatusChange(job); } diff --git a/jobs-service/jobs-service-messaging-kafka/pom.xml b/jobs-service/jobs-service-messaging-kafka/pom.xml index 6a81e82973..beb82658ed 100644 --- a/jobs-service/jobs-service-messaging-kafka/pom.xml +++ b/jobs-service/jobs-service-messaging-kafka/pom.xml @@ -65,6 +65,10 @@ io.quarkus quarkus-smallrye-reactive-messaging-kafka + + io.smallrye.reactive + smallrye-reactive-messaging-in-memory + diff --git a/jobs-service/jobs-service-messaging-kafka/src/main/java/org/kie/kogito/jobs/service/messaging/kafka/stream/KafkaJobStreams.java b/jobs-service/jobs-service-messaging-kafka/src/main/java/org/kie/kogito/jobs/service/messaging/kafka/stream/KafkaJobStreams.java index c3ee2b5d9e..c9b149ede2 100644 --- a/jobs-service/jobs-service-messaging-kafka/src/main/java/org/kie/kogito/jobs/service/messaging/kafka/stream/KafkaJobStreams.java +++ b/jobs-service/jobs-service-messaging-kafka/src/main/java/org/kie/kogito/jobs/service/messaging/kafka/stream/KafkaJobStreams.java @@ -21,14 +21,14 @@ import java.util.Optional; import org.eclipse.microprofile.config.inject.ConfigProperty; -import org.eclipse.microprofile.reactive.messaging.Acknowledgment; import org.eclipse.microprofile.reactive.messaging.Channel; import org.eclipse.microprofile.reactive.messaging.Emitter; -import org.eclipse.microprofile.reactive.messaging.Incoming; import org.eclipse.microprofile.reactive.messaging.OnOverflow; import org.kie.kogito.jobs.service.model.JobDetails; import org.kie.kogito.jobs.service.stream.AbstractJobStreams; import org.kie.kogito.jobs.service.stream.AvailableStreams; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.ObjectMapper; @@ -39,19 +39,19 @@ public class KafkaJobStreams extends AbstractJobStreams { public static final String PUBLISH_EVENTS_CONFIG_KEY = "kogito.jobs-service.kafka.job-status-change-events"; + private static final Logger LOGGER = LoggerFactory.getLogger(KafkaJobStreams.class); @Inject public KafkaJobStreams(ObjectMapper objectMapper, @ConfigProperty(name = PUBLISH_EVENTS_CONFIG_KEY) Optional config, - @Channel(AvailableStreams.JOB_STATUS_CHANGE_EVENTS_TOPIC) @OnOverflow(value = OnOverflow.Strategy.LATEST) Emitter emitter, + @Channel(AvailableStreams.JOB_STATUS_CHANGE_EVENTS_TOPIC) @OnOverflow(value = OnOverflow.Strategy.UNBOUNDED_BUFFER) Emitter emitter, @ConfigProperty(name = "kogito.service.url", defaultValue = "http://localhost:8080") String url) { super(objectMapper, config.orElse(false), emitter, url); } - @Incoming(AvailableStreams.JOB_STATUS_CHANGE_EVENTS) - @Acknowledgment(Acknowledgment.Strategy.PRE_PROCESSING) @Override public void jobStatusChange(JobDetails job) { + LOGGER.debug("jobStatusChange call received, enabled: {}, job: {}", enabled, job); super.jobStatusChange(job); } } diff --git a/jobs-service/jobs-service-mongodb/src/main/java/org/kie/kogito/jobs/service/repository/mongodb/MongoDBJobRepository.java b/jobs-service/jobs-service-mongodb/src/main/java/org/kie/kogito/jobs/service/repository/mongodb/MongoDBJobRepository.java index 6bc29ec4b4..6ae337214b 100644 --- a/jobs-service/jobs-service-mongodb/src/main/java/org/kie/kogito/jobs/service/repository/mongodb/MongoDBJobRepository.java +++ b/jobs-service/jobs-service-mongodb/src/main/java/org/kie/kogito/jobs/service/repository/mongodb/MongoDBJobRepository.java @@ -19,9 +19,13 @@ package org.kie.kogito.jobs.service.repository.mongodb; import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CompletionStage; +import java.util.stream.Collectors; import org.bson.Document; +import org.bson.conversions.Bson; import org.bson.json.JsonWriterSettings; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.reactive.streams.operators.PublisherBuilder; @@ -38,6 +42,7 @@ import io.quarkus.mongodb.reactive.ReactiveMongoClient; import io.quarkus.mongodb.reactive.ReactiveMongoCollection; import io.quarkus.runtime.StartupEvent; +import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.infrastructure.Infrastructure; import io.vertx.core.Vertx; import io.vertx.core.json.JsonObject; @@ -48,12 +53,13 @@ import static com.mongodb.client.model.Filters.and; import static com.mongodb.client.model.Filters.eq; -import static com.mongodb.client.model.Filters.gt; +import static com.mongodb.client.model.Filters.gte; import static com.mongodb.client.model.Filters.in; -import static com.mongodb.client.model.Filters.lt; +import static com.mongodb.client.model.Filters.lte; import static com.mongodb.client.model.Indexes.ascending; import static com.mongodb.client.model.ReturnDocument.AFTER; import static com.mongodb.client.model.Sorts.descending; +import static com.mongodb.client.model.Sorts.orderBy; import static java.util.Arrays.stream; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.counting; @@ -61,6 +67,7 @@ import static mutiny.zero.flow.adapters.AdaptersToReactiveStreams.publisher; import static org.bson.Document.parse; import static org.eclipse.microprofile.reactive.streams.operators.ReactiveStreams.fromPublisher; +import static org.kie.kogito.jobs.service.utils.ModelUtil.jobWithCreatedAndLastUpdate; @ApplicationScoped public class MongoDBJobRepository extends BaseReactiveJobRepository implements ReactiveJobRepository { @@ -75,6 +82,8 @@ public class MongoDBJobRepository extends BaseReactiveJobRepository implements R static final String FIRE_TIME_COLUMN = "trigger.nextFireTime"; + static final String CREATED_COLUMN = "created"; + private static final JsonWriterSettings jsonWriterSettings = JsonWriterSettings.builder() .int64Converter((value, writer) -> writer.writeNumber(value.toString())).build(); @@ -97,26 +106,34 @@ public MongoDBJobRepository(Vertx vertx, JobEventPublisher jobEventPublisher, Re void onStart(@Observes StartupEvent ev) { this.collection.createIndex(ascending(STATUS_COLUMN, FIRE_TIME_COLUMN)).await().indefinitely(); + this.collection.createIndex(ascending(CREATED_COLUMN, FIRE_TIME_COLUMN, ID)).await().indefinitely(); } @Override public CompletionStage doSave(JobDetails job) { + return collection.find(eq(ID, job.getId())) + .collect().with(counting()) + .map(count -> jobWithCreatedAndLastUpdate(count == 0, job)) + .chain(this::findAndUpdate) + .emitOn(Infrastructure.getDefaultExecutor()) + .convert() + .toCompletionStage(); + } + + private Uni findAndUpdate(JobDetails job) { return collection.findOneAndReplace( eq(ID, job.getId()), jsonToDocument(jobDetailsMarshaller.marshall(job)), new FindOneAndReplaceOptions().upsert(true).returnDocument(AFTER)) - .map(document -> documentToJson(document)) - .map(jobDetailsMarshaller::unmarshall) - .emitOn(Infrastructure.getDefaultExecutor()) - .convert() - .toCompletionStage(); + .map(MongoDBJobRepository::documentToJson) + .map(jobDetailsMarshaller::unmarshall); } @Override public CompletionStage get(String id) { return collection.find(eq(ID, id)) .collect().first() - .map(document -> documentToJson(document)) + .map(MongoDBJobRepository::documentToJson) .map(jobDetailsMarshaller::unmarshall) .emitOn(Infrastructure.getDefaultExecutor()) .convert() @@ -136,7 +153,7 @@ public CompletionStage exists(String id) { @Override public CompletionStage delete(String id) { return collection.findOneAndDelete(eq(ID, id)) - .map(document -> documentToJson(document)) + .map(MongoDBJobRepository::documentToJson) .map(jobDetailsMarshaller::unmarshall) .emitOn(Infrastructure.getDefaultExecutor()) .convert() @@ -144,25 +161,27 @@ public CompletionStage delete(String id) { } @Override - public PublisherBuilder findAll() { - return fromPublisher(publisher(collection.find() - .map(document -> documentToJson(document)) - .map(jobDetailsMarshaller::unmarshall) - .emitOn(Infrastructure.getDefaultExecutor()) - .convert() - .toPublisher())); - } + public PublisherBuilder findByStatusBetweenDates(ZonedDateTime fromFireTime, + ZonedDateTime toFireTime, + JobStatus[] status, + SortTerm[] orderBy) { + + FindOptions findOptions = new FindOptions(); + List filters = new ArrayList<>(); + if (status != null && status.length > 0) { + filters.add(createStatusFilter(status)); + } + filters.add(gte(FIRE_TIME_COLUMN, fromFireTime.toInstant().toEpochMilli())); + filters.add(lte(FIRE_TIME_COLUMN, toFireTime.toInstant().toEpochMilli())); + findOptions.filter(and(filters)); + + if (orderBy != null && orderBy.length > 0) { + findOptions.sort(createOrderBy(orderBy)); + } - @Override - public PublisherBuilder findByStatusBetweenDatesOrderByPriority(ZonedDateTime from, ZonedDateTime to, JobStatus... status) { return fromPublisher(publisher( - collection.find( - and( - in(STATUS_COLUMN, stream(status).map(Enum::name).collect(toList())), - gt(FIRE_TIME_COLUMN, from.toInstant().toEpochMilli()), - lt(FIRE_TIME_COLUMN, to.toInstant().toEpochMilli())), - new FindOptions().sort(descending("priority"))) - .map(document -> documentToJson(document)) + collection.find(findOptions) + .map(MongoDBJobRepository::documentToJson) .map(jobDetailsMarshaller::unmarshall) .emitOn(Infrastructure.getDefaultExecutor()) .convert() @@ -176,4 +195,26 @@ static JsonObject documentToJson(Document document) { static Document jsonToDocument(JsonObject jsonNode) { return ofNullable(jsonNode).map(json -> parse(json.toString())).orElse(null); } + + static Bson createStatusFilter(JobStatus... status) { + return in(STATUS_COLUMN, stream(status).map(Enum::name).collect(toList())); + } + + static Bson createOrderBy(SortTerm[] sortTerms) { + return orderBy(stream(sortTerms).map(MongoDBJobRepository::createOrderByTerm).collect(Collectors.toList())); + } + + static Bson createOrderByTerm(SortTerm sortTerm) { + String columnName = toColumName(sortTerm.getField()); + return sortTerm.isAsc() ? ascending(columnName) : descending(columnName); + } + + static String toColumName(SortTermField field) { + return switch (field) { + case FIRE_TIME -> FIRE_TIME_COLUMN; + case CREATED -> CREATED_COLUMN; + case ID -> ID; + default -> throw new IllegalArgumentException("No colum name is defined for field: " + field); + }; + } } diff --git a/jobs-service/jobs-service-mongodb/src/test/java/org/kie/kogito/jobs/service/repository/mongodb/MongoDBJobRepositoryExecutionTest.java b/jobs-service/jobs-service-mongodb/src/test/java/org/kie/kogito/jobs/service/repository/mongodb/MongoDBJobRepositoryExecutionTest.java index 216300e7ae..8e834b232d 100644 --- a/jobs-service/jobs-service-mongodb/src/test/java/org/kie/kogito/jobs/service/repository/mongodb/MongoDBJobRepositoryExecutionTest.java +++ b/jobs-service/jobs-service-mongodb/src/test/java/org/kie/kogito/jobs/service/repository/mongodb/MongoDBJobRepositoryExecutionTest.java @@ -22,9 +22,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; -import java.util.concurrent.Flow; import org.bson.Document; import org.bson.conversions.Bson; @@ -32,17 +30,25 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.kie.kogito.jobs.service.api.recipient.http.HttpRecipient; +import org.kie.kogito.jobs.service.api.recipient.http.HttpRecipientStringPayloadData; import org.kie.kogito.jobs.service.model.JobDetails; import org.kie.kogito.jobs.service.model.JobDetailsBuilder; import org.kie.kogito.jobs.service.model.JobStatus; import org.kie.kogito.jobs.service.model.Recipient; import org.kie.kogito.jobs.service.model.RecipientInstance; +import org.kie.kogito.jobs.service.repository.ReactiveJobRepository; import org.kie.kogito.jobs.service.repository.marshaller.JobDetailsMarshaller; +import org.kie.kogito.jobs.service.repository.marshaller.RecipientMarshaller; +import org.kie.kogito.jobs.service.repository.marshaller.TriggerMarshaller; +import org.kie.kogito.jobs.service.repository.mongodb.marshaller.MongoDBJobDetailsMarshaller; +import org.kie.kogito.jobs.service.utils.DateUtil; +import org.kie.kogito.timer.Trigger; import org.kie.kogito.timer.impl.PointInTimeTrigger; import org.mockito.ArgumentCaptor; import com.mongodb.client.model.FindOneAndReplaceOptions; import com.mongodb.client.model.ReturnDocument; +import com.mongodb.reactivestreams.client.FindPublisher; import io.quarkus.mongodb.FindOptions; import io.quarkus.mongodb.reactive.ReactiveMongoClient; @@ -50,28 +56,28 @@ import io.quarkus.mongodb.reactive.ReactiveMongoDatabase; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; -import io.smallrye.mutiny.groups.MultiCollect; -import io.smallrye.mutiny.groups.MultiConvert; import io.smallrye.mutiny.groups.UniAwait; -import io.smallrye.mutiny.groups.UniConvert; import io.vertx.core.json.JsonObject; import static com.mongodb.client.model.Filters.and; import static com.mongodb.client.model.Filters.eq; -import static com.mongodb.client.model.Filters.gt; +import static com.mongodb.client.model.Filters.gte; import static com.mongodb.client.model.Filters.in; -import static com.mongodb.client.model.Filters.lt; +import static com.mongodb.client.model.Filters.lte; import static com.mongodb.client.model.Indexes.ascending; import static java.util.stream.Collectors.toList; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.kie.kogito.jobs.service.repository.mongodb.MongoDBJobRepository.CREATED_COLUMN; +import static org.kie.kogito.jobs.service.repository.mongodb.MongoDBJobRepository.FIRE_TIME_COLUMN; import static org.kie.kogito.jobs.service.repository.mongodb.MongoDBJobRepository.ID; -import static org.kie.kogito.jobs.service.utils.DateUtil.DEFAULT_ZONE; +import static org.kie.kogito.jobs.service.repository.mongodb.MongoDBJobRepository.STATUS_COLUMN; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -79,167 +85,186 @@ @SuppressWarnings({ "unchecked", "rawtypes" }) class MongoDBJobRepositoryExecutionTest { - MongoDBJobRepository mongoDBJobRepository; + private static final String JOB_ID = "JOB_ID"; - ReactiveMongoClient mongoClient; + private MongoDBJobRepository mongoDBJobRepository; - ReactiveMongoCollection collection; + private ReactiveMongoCollection collection; - JobDetailsMarshaller jobDetailsMarshaller; - - JsonObject marshalled; - - JobDetails unmarshalled; - - CompletableFuture completableFuture; + private JobDetailsMarshaller jobDetailsMarshaller; @BeforeEach void setUp() { - mongoClient = mock(ReactiveMongoClient.class); + ReactiveMongoClient mongoClient = mock(ReactiveMongoClient.class); collection = mock(ReactiveMongoCollection.class); ReactiveMongoDatabase mongoDatabase = mock(ReactiveMongoDatabase.class); when(mongoClient.getDatabase(anyString())).thenReturn(mongoDatabase); when(mongoDatabase.getCollection(anyString())).thenReturn(collection); + jobDetailsMarshaller = spy(new MongoDBJobDetailsMarshaller(new TriggerMarshaller(), new RecipientMarshaller())); + mongoDBJobRepository = new MongoDBJobRepository(null, null, mongoClient, "test", jobDetailsMarshaller); + } - Uni uni = mock(Uni.class); - Multi multi = mock(Multi.class); - when(collection.findOneAndReplace(any(Bson.class), any(), any(FindOneAndReplaceOptions.class))).thenReturn(uni); - when(collection.find(any(Bson.class))).thenReturn(multi); - when(collection.findOneAndDelete(any(Bson.class))).thenReturn(uni); - when(collection.find()).thenReturn(multi); - when(collection.find(any(Bson.class), any(FindOptions.class))).thenReturn(multi); - when(collection.createIndex(any())).thenReturn(uni); + @Test + void saveExisting() throws Exception { + doSave(createExistingJob(), true); + } - when(uni.map(any())).thenAnswer(invocationOnMock -> { - jobDetailsMarshaller.unmarshall(marshalled); - return uni; - }); - when(uni.await()).thenReturn(mock(UniAwait.class)); + @Test + void saveNotExisting() throws Exception { + JobDetails notExisting = new JobDetailsBuilder() + .id(JOB_ID) + .trigger(createTrigger()) + .recipient(createRecipient()) + .build(); + doSave(notExisting, false); + } - MultiCollect multiCollect = mock(MultiCollect.class); - when(multi.collect()).thenReturn(multiCollect); - when(multiCollect.first()).thenReturn(uni); - when(multiCollect.with(any())).thenReturn(uni); - when(multi.map(any())).thenAnswer(invocationOnMock -> { - jobDetailsMarshaller.unmarshall(marshalled); - return multi; - }); - MultiConvert convertMulti = mock(MultiConvert.class); - when(multi.emitOn(any())).thenReturn(multi); - when(multi.convert()).thenReturn(convertMulti); - Flow.Publisher publisher = mock(Flow.Publisher.class); - when(convertMulti.toPublisher()).thenReturn(publisher); - - completableFuture = mock(CompletableFuture.class); - UniConvert convert = mock(UniConvert.class); - when(uni.emitOn(any())).thenReturn(uni); - when(uni.convert()).thenReturn(convert); - when(convert.toCompletionStage()).thenReturn(completableFuture); - - ZonedDateTime time = ZonedDateTime.now(DEFAULT_ZONE); - PointInTimeTrigger trigger = new PointInTimeTrigger(time.toInstant().getEpochSecond(), null, null); - Recipient recipient = new RecipientInstance(HttpRecipient.builder().forStringPayload().url("test").build()); - unmarshalled = new JobDetailsBuilder().id("test").trigger(trigger).recipient(recipient).build(); - marshalled = new JsonObject().put("id", "test"); - - jobDetailsMarshaller = mock(JobDetailsMarshaller.class); - when(jobDetailsMarshaller.marshall(any(JobDetails.class))).thenReturn(marshalled); - when(jobDetailsMarshaller.unmarshall(any(JsonObject.class))).thenReturn(unmarshalled); + private void doSave(JobDetails job, boolean exists) throws Exception { + ZonedDateTime now = ZonedDateTime.now(); + Multi multi; + if (exists) { + Document document = Document.parse(new JobDetailsMarshaller(new TriggerMarshaller(), new RecipientMarshaller()).marshall(job).toString()); + multi = Multi.createFrom().item(document); + } else { + multi = Multi.createFrom().empty(); + } + when(collection.find(any(Bson.class))).thenReturn(multi); - mongoDBJobRepository = new MongoDBJobRepository(null, null, mongoClient, "test", jobDetailsMarshaller); - } + Document replaced = new Document().append("id", "replaced"); + Uni replacedDocument = Uni.createFrom().item(replaced); + when(collection.findOneAndReplace(any(Bson.class), any(), any(FindOneAndReplaceOptions.class))).thenReturn(replacedDocument); - @Test - void doSave() { - CompletionStage result = mongoDBJobRepository.doSave(unmarshalled); - assertEquals(completableFuture, result); + CompletionStage result = mongoDBJobRepository.doSave(job); + JobDetails saved = result.toCompletableFuture().get(); ArgumentCaptor filterCaptor = ArgumentCaptor.forClass(Bson.class); ArgumentCaptor documentCaptor = ArgumentCaptor.forClass(Document.class); ArgumentCaptor optionCaptor = ArgumentCaptor.forClass(FindOneAndReplaceOptions.class); - verify(collection, times(1)).findOneAndReplace(filterCaptor.capture(), documentCaptor.capture(), optionCaptor.capture()); - verify(jobDetailsMarshaller, times(1)).marshall(unmarshalled); - verify(jobDetailsMarshaller, atLeastOnce()).unmarshall(marshalled); + ArgumentCaptor marshallCaptor = ArgumentCaptor.forClass(JobDetails.class); + ArgumentCaptor unmarshallCaptor = ArgumentCaptor.forClass(JsonObject.class); - assertEquals(eq(ID, unmarshalled.getId()), filterCaptor.getValue()); - assertEquals(Document.parse(marshalled.toString()), documentCaptor.getValue()); - assertTrue(optionCaptor.getValue().isUpsert()); - assertEquals(ReturnDocument.AFTER, optionCaptor.getValue().getReturnDocument()); + verify(collection, times(1)).findOneAndReplace(filterCaptor.capture(), documentCaptor.capture(), optionCaptor.capture()); + verify(jobDetailsMarshaller, times(1)).marshall(marshallCaptor.capture()); + verify(jobDetailsMarshaller).unmarshall(unmarshallCaptor.capture()); + + assertThat(filterCaptor.getValue()).isEqualTo(eq(ID, job.getId())); + assertThat(optionCaptor.getValue().isUpsert()).isTrue(); + assertThat(optionCaptor.getValue().getReturnDocument()).isEqualTo(ReturnDocument.AFTER); + + JobDetails timestampedJob = marshallCaptor.getAllValues().get(0); + assertThat(timestampedJob.getId()).isEqualTo(job.getId()); + if (exists) { + assertThat(timestampedJob.getCreated()).isEqualTo(job.getCreated()); + assertThat(timestampedJob.getLastUpdate()).isAfter(job.getLastUpdate()); + } else { + assertThat(timestampedJob.getCreated()).isAfter(now); + assertThat(timestampedJob.getLastUpdate()).isAfter(now); + } + JsonObject replacedAsJson = new JsonObject(replaced.toJson()); + assertThat(unmarshallCaptor.getValue()).isEqualTo(replacedAsJson); + assertThat(saved.getId()).isEqualTo(replacedAsJson.getString("id")); } @Test - void get() { - CompletionStage result = mongoDBJobRepository.get(unmarshalled.getId()); - assertEquals(completableFuture, result); + void get() throws Exception { + JobDetails job = createExistingJob(); + Document document = Document.parse(new JobDetailsMarshaller(new TriggerMarshaller(), new RecipientMarshaller()).marshall(job).toString()); + Multi multi = Multi.createFrom().item(document); + when(collection.find(any(Bson.class))).thenReturn(multi); + + JobDetails result = mongoDBJobRepository.get(job.getId()).toCompletableFuture().get(); + assertThat(result).isEqualTo(job); ArgumentCaptor filterCaptor = ArgumentCaptor.forClass(Bson.class); + ArgumentCaptor unmarshallCaptor = ArgumentCaptor.forClass(JsonObject.class); + verify(collection, times(1)).find(filterCaptor.capture()); - verify(jobDetailsMarshaller, atLeastOnce()).unmarshall(marshalled); + assertEquals(eq(ID, job.getId()), filterCaptor.getValue()); - assertEquals(eq(ID, unmarshalled.getId()), filterCaptor.getValue()); + verify(jobDetailsMarshaller).unmarshall(unmarshallCaptor.capture()); + assertThat(unmarshallCaptor.getValue()).isEqualTo(new JsonObject(document.toJson())); } @Test - void exists() { - CompletionStage result = mongoDBJobRepository.exists(unmarshalled.getId()); - assertEquals(completableFuture, result); + void exists() throws Exception { + JobDetails job = createExistingJob(); + Document document = Document.parse(new JobDetailsMarshaller(new TriggerMarshaller(), new RecipientMarshaller()).marshall(job).toString()); + Multi multi = Multi.createFrom().item(document); + when(collection.find(any(Bson.class))).thenReturn(multi); + + Boolean result = mongoDBJobRepository.exists(job.getId()).toCompletableFuture().get(); + assertThat(result).isTrue(); ArgumentCaptor filterCaptor = ArgumentCaptor.forClass(Bson.class); verify(collection, times(1)).find(filterCaptor.capture()); - verify(jobDetailsMarshaller, times(1)).unmarshall(marshalled); - - assertEquals(eq(ID, unmarshalled.getId()), filterCaptor.getValue()); + assertEquals(eq(ID, job.getId()), filterCaptor.getValue()); } @Test - void delete() { - CompletionStage result = mongoDBJobRepository.delete(unmarshalled.getId()); - assertEquals(completableFuture, result); + void delete() throws Exception { + JobDetails job = createExistingJob(); + Document document = Document.parse(new JobDetailsMarshaller(new TriggerMarshaller(), new RecipientMarshaller()).marshall(job).toString()); + Uni uni = Uni.createFrom().item(document); + when(collection.findOneAndDelete(any(Bson.class))).thenReturn(uni); + + JobDetails result = mongoDBJobRepository.delete(job.getId()).toCompletableFuture().get(); + assertThat(result).isEqualTo(job); ArgumentCaptor filterCaptor = ArgumentCaptor.forClass(Bson.class); + ArgumentCaptor unmarshallCaptor = ArgumentCaptor.forClass(JsonObject.class); + verify(collection, times(1)).findOneAndDelete(filterCaptor.capture()); - verify(jobDetailsMarshaller, atLeastOnce()).unmarshall(marshalled); + assertEquals(eq(ID, job.getId()), filterCaptor.getValue()); - assertEquals(eq(ID, unmarshalled.getId()), filterCaptor.getValue()); + verify(jobDetailsMarshaller).unmarshall(unmarshallCaptor.capture()); + assertThat(unmarshallCaptor.getValue()).isEqualTo(new JsonObject(document.toJson())); } @Test - void findAll() { - PublisherBuilder result = mongoDBJobRepository.findAll(); - assertNotNull(result); - - verify(collection, times(1)).find(); - verify(jobDetailsMarshaller, atLeastOnce()).unmarshall(marshalled); - } + void findByStatusBetweenDates() { + JobDetails job = createExistingJob(); + Document document = Document.parse(new JobDetailsMarshaller(new TriggerMarshaller(), new RecipientMarshaller()).marshall(job).toString()); + Multi multi = Multi.createFrom().item(document); + doReturn(multi).when(collection).find(any(FindOptions.class)); - @Test - void findByStatusBetweenDatesOrderByPriority() { ZonedDateTime from = ZonedDateTime.now(); ZonedDateTime to = ZonedDateTime.now(); - PublisherBuilder result = mongoDBJobRepository.findByStatusBetweenDatesOrderByPriority(from, to, JobStatus.SCHEDULED, JobStatus.RETRY); + PublisherBuilder result = mongoDBJobRepository.findByStatusBetweenDates(from, to, + new JobStatus[] { JobStatus.SCHEDULED, JobStatus.RETRY }, + new ReactiveJobRepository.SortTerm[] { ReactiveJobRepository.SortTerm.byFireTime(true) }); assertNotNull(result); ArgumentCaptor filterCaptor = ArgumentCaptor.forClass(Bson.class); ArgumentCaptor optionCaptor = ArgumentCaptor.forClass(FindOptions.class); - verify(collection, times(1)).find(filterCaptor.capture(), optionCaptor.capture()); - verify(jobDetailsMarshaller, atLeastOnce()).unmarshall(marshalled); + verify(collection, times(1)).find(optionCaptor.capture()); + + FindPublisher findPublisher = mock(FindPublisher.class); + doReturn(findPublisher).when(findPublisher).filter(any()); + + optionCaptor.getValue().apply(findPublisher); + verify(findPublisher).filter(filterCaptor.capture()); assertEquals(and( in("status", Arrays.stream(new JobStatus[] { JobStatus.SCHEDULED, JobStatus.RETRY }).map(Enum::name).collect(toList())), - gt("trigger.nextFireTime", from.toInstant().toEpochMilli()), - lt("trigger.nextFireTime", to.toInstant().toEpochMilli())), + gte("trigger.nextFireTime", from.toInstant().toEpochMilli()), + lte("trigger.nextFireTime", to.toInstant().toEpochMilli())), filterCaptor.getValue()); } @Test void onStart() { + Uni uni = mock(Uni.class); + when(collection.createIndex(any())).thenReturn(uni); + when(uni.await()).thenReturn(mock(UniAwait.class)); + mongoDBJobRepository.onStart(null); ArgumentCaptor indexCaptor = ArgumentCaptor.forClass(Bson.class); - verify(collection, times(1)).createIndex(indexCaptor.capture()); + verify(collection, times(2)).createIndex(indexCaptor.capture()); - assertEquals(ascending("status", "trigger.nextFireTime"), indexCaptor.getValue()); + assertEquals(ascending(STATUS_COLUMN, FIRE_TIME_COLUMN), indexCaptor.getAllValues().get(0)); + assertEquals(ascending(CREATED_COLUMN, FIRE_TIME_COLUMN, ID), indexCaptor.getAllValues().get(1)); } @Test @@ -269,4 +294,28 @@ void jsonToDocument() { assertEquals(document, MongoDBJobRepository.jsonToDocument(object)); } + + private static JobDetails createExistingJob() { + ZonedDateTime createdTime = ZonedDateTime.parse("2024-01-30T12:00:00.000Z[UTC]"); + ZonedDateTime lastUpdateTime = ZonedDateTime.parse("2024-01-30T15:00:00.000Z[UTC]"); + return new JobDetailsBuilder() + .id(JOB_ID) + .trigger(createTrigger()) + .recipient(createRecipient()) + .created(createdTime) + .lastUpdate(lastUpdateTime) + .build(); + } + + private static Trigger createTrigger() { + return new PointInTimeTrigger(DateUtil.now().toInstant().toEpochMilli(), null, null); + } + + private static Recipient createRecipient() { + return new RecipientInstance(HttpRecipient.builder() + .forStringPayload() + .url("http://my-service") + .payload(HttpRecipientStringPayloadData.from("payload data")) + .build()); + } } diff --git a/jobs-service/jobs-service-postgresql-common/src/main/java/org/kie/kogito/jobs/service/repository/postgresql/PostgreSqlJobRepository.java b/jobs-service/jobs-service-postgresql-common/src/main/java/org/kie/kogito/jobs/service/repository/postgresql/PostgreSqlJobRepository.java index fa95138faa..20db74cbc2 100644 --- a/jobs-service/jobs-service-postgresql-common/src/main/java/org/kie/kogito/jobs/service/repository/postgresql/PostgreSqlJobRepository.java +++ b/jobs-service/jobs-service-postgresql-common/src/main/java/org/kie/kogito/jobs/service/repository/postgresql/PostgreSqlJobRepository.java @@ -56,12 +56,10 @@ @ApplicationScoped public class PostgreSqlJobRepository extends BaseReactiveJobRepository implements ReactiveJobRepository { - public static final Integer MAX_ITEMS_QUERY = 10000; - private static final String JOB_DETAILS_TABLE = "job_details"; private static final String JOB_DETAILS_COLUMNS = "id, correlation_id, status, last_update, retries, " + - "execution_counter, scheduled_id, priority, recipient, trigger, fire_time, execution_timeout, execution_timeout_unit"; + "execution_counter, scheduled_id, priority, recipient, trigger, fire_time, execution_timeout, execution_timeout_unit, created"; private PgPool client; @@ -85,7 +83,7 @@ public PostgreSqlJobRepository(Vertx vertx, JobEventPublisher jobEventPublisher, @Override public CompletionStage doSave(JobDetails job) { return client.preparedQuery("INSERT INTO " + JOB_DETAILS_TABLE + " (" + JOB_DETAILS_COLUMNS + - ") VALUES ($1, $2, $3, now(), $4, $5, $6, $7, $8, $9, $10, $11, $12) " + + ") VALUES ($1, $2, $3, now(), $4, $5, $6, $7, $8, $9, $10, $11, $12, now()) " + "ON CONFLICT (id) DO " + "UPDATE SET correlation_id = $2, status = $3, last_update = now(), retries = $4, " + "execution_counter = $5, scheduled_id = $6, priority = $7, " + @@ -138,43 +136,60 @@ public CompletionStage delete(String id) { } @Override - public PublisherBuilder findByStatus(JobStatus... status) { - String statusQuery = createStatusQuery(status); - String query = " WHERE " + statusQuery; + public PublisherBuilder findByStatusBetweenDates(ZonedDateTime fromFireTime, + ZonedDateTime toFireTime, + JobStatus[] status, + SortTerm[] orderBy) { + + String statusFilter = (status != null && status.length > 0) ? createStatusFilter(status) : null; + String fireTimeFilter = createFireTimeFilter("$1", "$2"); + String orderByCriteria = (orderBy != null && orderBy.length > 0) ? createOrderBy(orderBy) : ""; + + StringBuilder queryFilter = new StringBuilder(); + if (statusFilter != null) { + queryFilter.append(statusFilter); + queryFilter.append(" AND "); + } + queryFilter.append(fireTimeFilter); + + String findQuery = "SELECT " + JOB_DETAILS_COLUMNS + + " FROM " + JOB_DETAILS_TABLE + + " WHERE " + queryFilter + + " " + orderByCriteria; + + Tuple params = Tuple.of(fromFireTime.toOffsetDateTime(), toFireTime.toOffsetDateTime()); return ReactiveStreams.fromPublisher(publisher( - client.preparedQuery("SELECT " + JOB_DETAILS_COLUMNS + " FROM " + JOB_DETAILS_TABLE + query + " ORDER BY priority DESC LIMIT $1").execute(Tuple.of(MAX_ITEMS_QUERY)) + client.preparedQuery(findQuery) + .execute(params) .onItem().transformToMulti(rowSet -> Multi.createFrom().iterable(rowSet)) .onItem().transform(this::from))); } - @Override - public PublisherBuilder findAll() { - return ReactiveStreams.fromPublisher(publisher( - client.preparedQuery("SELECT " + JOB_DETAILS_COLUMNS + " FROM " + JOB_DETAILS_TABLE + " LIMIT $1").execute(Tuple.of(MAX_ITEMS_QUERY)) - .onItem().transformToMulti(rowSet -> Multi.createFrom().iterable(rowSet)) - .onItem().transform(this::from))); + static String createStatusFilter(JobStatus... status) { + return Arrays.stream(status).map(JobStatus::name) + .collect(Collectors.joining("', '", "status IN ('", "')")); } - @Override - public PublisherBuilder findByStatusBetweenDatesOrderByPriority(ZonedDateTime from, ZonedDateTime to, JobStatus... status) { - String statusQuery = createStatusQuery(status); - String timeQuery = createTimeQuery("$2", "$3"); - String query = " WHERE " + statusQuery + " AND " + timeQuery; + static String createFireTimeFilter(String indexFrom, String indexTo) { + return String.format("fire_time BETWEEN %s AND %s", indexFrom, indexTo); + } - return ReactiveStreams.fromPublisher(publisher( - client.preparedQuery("SELECT " + JOB_DETAILS_COLUMNS + " FROM " + JOB_DETAILS_TABLE + query + " ORDER BY priority DESC LIMIT $1") - .execute(Tuple.of(MAX_ITEMS_QUERY, from.toOffsetDateTime(), to.toOffsetDateTime())) - .onItem().transformToMulti(rowSet -> Multi.createFrom().iterable(rowSet)) - .onItem().transform(this::from))); + static String createOrderBy(SortTerm[] sortTerms) { + return Stream.of(sortTerms).map(PostgreSqlJobRepository::createOrderByTerm) + .collect(Collectors.joining(", ", "ORDER BY ", "")); } - static String createStatusQuery(JobStatus... status) { - return Arrays.stream(status).map(JobStatus::name) - .collect(Collectors.joining("', '", "status IN ('", "')")); + static String createOrderByTerm(SortTerm sortTerm) { + return toColumName(sortTerm.getField()) + (sortTerm.isAsc() ? " ASC" : " DESC"); } - static String createTimeQuery(String indexFrom, String indexTo) { - return String.format("fire_time BETWEEN %s AND %s", indexFrom, indexTo); + static String toColumName(SortTermField field) { + return switch (field) { + case FIRE_TIME -> "fire_time"; + case CREATED -> "created"; + case ID -> "id"; + default -> throw new IllegalArgumentException("No colum name is defined for field: " + field); + }; } JobDetails from(Row row) { @@ -191,6 +206,7 @@ JobDetails from(Row row) { .trigger(triggerMarshaller.unmarshall(row.get(JsonObject.class, "trigger"))) .executionTimeout(row.getLong("execution_timeout")) .executionTimeoutUnit(Optional.ofNullable(row.getString("execution_timeout_unit")).map(ChronoUnit::valueOf).orElse(null)) + .created(Optional.ofNullable(row.getOffsetDateTime("created")).map(t -> t.atZoneSameInstant(DEFAULT_ZONE)).orElse(null)) .build(); } } diff --git a/jobs-service/jobs-service-postgresql-common/src/main/resources/db/jobs-service/V3.0.3__Add_Created_Col.sql b/jobs-service/jobs-service-postgresql-common/src/main/resources/db/jobs-service/V3.0.3__Add_Created_Col.sql new file mode 100644 index 0000000000..e76162d170 --- /dev/null +++ b/jobs-service/jobs-service-postgresql-common/src/main/resources/db/jobs-service/V3.0.3__Add_Created_Col.sql @@ -0,0 +1,9 @@ +ALTER TABLE job_details + ADD COLUMN created TIMESTAMPTZ; + +UPDATE job_details +SET created = last_update +WHERE created is null; + +CREATE INDEX job_details_created_idx + ON job_details (created); diff --git a/jobs-service/jobs-service-postgresql-common/src/test/java/org/kie/kogito/jobs/service/repository/postgresql/PostgreSqlJobRepositoryExecutionTest.java b/jobs-service/jobs-service-postgresql-common/src/test/java/org/kie/kogito/jobs/service/repository/postgresql/PostgreSqlJobRepositoryExecutionTest.java index 8d40032982..ff2bd2fa2e 100644 --- a/jobs-service/jobs-service-postgresql-common/src/test/java/org/kie/kogito/jobs/service/repository/postgresql/PostgreSqlJobRepositoryExecutionTest.java +++ b/jobs-service/jobs-service-postgresql-common/src/test/java/org/kie/kogito/jobs/service/repository/postgresql/PostgreSqlJobRepositoryExecutionTest.java @@ -35,6 +35,7 @@ import org.kie.kogito.jobs.service.model.JobStatus; import org.kie.kogito.jobs.service.model.Recipient; import org.kie.kogito.jobs.service.model.RecipientInstance; +import org.kie.kogito.jobs.service.repository.ReactiveJobRepository; import org.kie.kogito.jobs.service.repository.marshaller.RecipientMarshaller; import org.kie.kogito.jobs.service.repository.marshaller.TriggerMarshaller; import org.kie.kogito.jobs.service.utils.DateUtil; @@ -152,11 +153,11 @@ void doSave() { verify(query, times(1)).execute(parameterCaptor.capture()); String query = "INSERT INTO " + JOB_DETAILS + " (id, correlation_id, status, last_update, retries, execution_counter, scheduled_id, " + - "priority, recipient, trigger, fire_time, execution_timeout, execution_timeout_unit) VALUES ($1, $2, $3, now(), $4, $5, $6, $7, $8, $9, $10, $11, $12) " + + "priority, recipient, trigger, fire_time, execution_timeout, execution_timeout_unit, created) VALUES ($1, $2, $3, now(), $4, $5, $6, $7, $8, $9, $10, $11, $12, now()) " + "ON CONFLICT (id) DO UPDATE SET correlation_id = $2, status = $3, last_update = now(), retries = $4, " + "execution_counter = $5, scheduled_id = $6, priority = $7, " + "recipient = $8, trigger = $9, fire_time = $10, execution_timeout = $11, execution_timeout_unit = $12 RETURNING id, correlation_id, status, last_update, retries, " + - "execution_counter, scheduled_id, priority, recipient, trigger, fire_time, execution_timeout, execution_timeout_unit"; + "execution_counter, scheduled_id, priority, recipient, trigger, fire_time, execution_timeout, execution_timeout_unit, created"; Tuple parameter = Tuple.tuple(Stream.of( job.getId(), @@ -199,7 +200,7 @@ void get() { verify(query, times(1)).execute(parameterCaptor.capture()); String query = "SELECT id, correlation_id, status, last_update, retries, execution_counter, scheduled_id, " + - "priority, recipient, trigger, fire_time, execution_timeout, execution_timeout_unit FROM " + JOB_DETAILS + " WHERE id = $1"; + "priority, recipient, trigger, fire_time, execution_timeout, execution_timeout_unit, created FROM " + JOB_DETAILS + " WHERE id = $1"; String parameter = "test"; assertEquals(query, queryCaptor.getValue()); @@ -235,7 +236,7 @@ void delete() { String query = "DELETE FROM " + JOB_DETAILS + " WHERE id = $1 " + "RETURNING id, correlation_id, status, last_update, retries, " + - "execution_counter, scheduled_id, priority, recipient, trigger, fire_time, execution_timeout, execution_timeout_unit"; + "execution_counter, scheduled_id, priority, recipient, trigger, fire_time, execution_timeout, execution_timeout_unit, created"; String parameter = "test"; assertEquals(query, queryCaptor.getValue()); @@ -243,64 +244,54 @@ void delete() { } @Test - void findAll() { - PublisherBuilder result = repository.findAll(); - assertNotNull(result); - - ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(String.class); - verify(client, times(1)).preparedQuery(queryCaptor.capture()); - - String query = "SELECT id, correlation_id, status, last_update, retries, " + - "execution_counter, scheduled_id, priority, recipient, trigger, fire_time, execution_timeout, execution_timeout_unit FROM " + JOB_DETAILS + " LIMIT $1"; - - assertEquals(query, queryCaptor.getValue()); - } - - @Test - void findByStatusBetweenDatesOrderByPriority() { + void findByStatusBetweenDates() { ZonedDateTime from = ZonedDateTime.now(); ZonedDateTime to = ZonedDateTime.now(); - PublisherBuilder result = repository.findByStatusBetweenDatesOrderByPriority(from, to, JobStatus.SCHEDULED, JobStatus.RETRY); + PublisherBuilder result = repository.findByStatusBetweenDates(from, to, + new JobStatus[] { JobStatus.SCHEDULED, JobStatus.RETRY }, + new ReactiveJobRepository.SortTerm[] { ReactiveJobRepository.SortTerm.byFireTime(true) }); assertNotNull(result); ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(String.class); verify(client, times(1)).preparedQuery(queryCaptor.capture()); String query = "SELECT id, correlation_id, status, last_update, retries, execution_counter, scheduled_id, " + - "priority, recipient, trigger, fire_time, execution_timeout, execution_timeout_unit FROM " + JOB_DETAILS + " " + - "WHERE status IN ('SCHEDULED', 'RETRY') AND fire_time BETWEEN $2 AND $3 ORDER BY priority DESC LIMIT $1"; + "priority, recipient, trigger, fire_time, execution_timeout, execution_timeout_unit, created FROM " + JOB_DETAILS + " " + + "WHERE status IN ('SCHEDULED', 'RETRY') AND fire_time BETWEEN $1 AND $2 ORDER BY fire_time ASC"; assertEquals(query, queryCaptor.getValue()); } @Test - void findByStatusBetweenDatesOrderByPriorityNoCondition() { + void findByStatusBetweenDatesNoStatusCondition() { ZonedDateTime from = ZonedDateTime.now(); ZonedDateTime to = ZonedDateTime.now(); - PublisherBuilder result = repository.findByStatusBetweenDatesOrderByPriority(from, to, JobStatus.SCHEDULED); + PublisherBuilder result = repository.findByStatusBetweenDates(from, to, + new JobStatus[] {}, + new ReactiveJobRepository.SortTerm[] { ReactiveJobRepository.SortTerm.byFireTime(false) }); assertNotNull(result); ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(String.class); verify(client, times(1)).preparedQuery(queryCaptor.capture()); String query = "SELECT id, correlation_id, status, last_update, retries, execution_counter, scheduled_id, " + - "priority, recipient, trigger, fire_time, execution_timeout, execution_timeout_unit FROM " + JOB_DETAILS + " " + - "WHERE status IN ('SCHEDULED') AND fire_time BETWEEN $2 AND $3 ORDER BY priority DESC LIMIT $1"; + "priority, recipient, trigger, fire_time, execution_timeout, execution_timeout_unit, created FROM " + JOB_DETAILS + " " + + "WHERE fire_time BETWEEN $1 AND $2 ORDER BY fire_time DESC"; assertEquals(query, queryCaptor.getValue()); } @Test void createStatusQuery() { - String statusQuery = PostgreSqlJobRepository.createStatusQuery(JobStatus.SCHEDULED, JobStatus.RETRY); + String statusQuery = PostgreSqlJobRepository.createStatusFilter(JobStatus.SCHEDULED, JobStatus.RETRY); assertEquals("status IN ('SCHEDULED', 'RETRY')", statusQuery); } @Test void createTimeQuery() { - String timeQuery = PostgreSqlJobRepository.createTimeQuery("$1", "$2"); + String timeQuery = PostgreSqlJobRepository.createFireTimeFilter("$1", "$2"); assertEquals("fire_time BETWEEN $1 AND $2", timeQuery); } diff --git a/jobs-service/kogito-addons-jobs-service/kogito-addons-quarkus-jobs-service-embedded/runtime/src/main/java/org/kie/kogito/addons/quarkus/jobs/service/embedded/KogitoAddonsQuarkusJobsServiceEmbeddedRuntimeConfig.java b/jobs-service/kogito-addons-jobs-service/kogito-addons-quarkus-jobs-service-embedded/runtime/src/main/java/org/kie/kogito/addons/quarkus/jobs/service/embedded/KogitoAddonsQuarkusJobsServiceEmbeddedRuntimeConfig.java index 0f077459a5..94828cf4c5 100644 --- a/jobs-service/kogito-addons-jobs-service/kogito-addons-quarkus-jobs-service-embedded/runtime/src/main/java/org/kie/kogito/addons/quarkus/jobs/service/embedded/KogitoAddonsQuarkusJobsServiceEmbeddedRuntimeConfig.java +++ b/jobs-service/kogito-addons-jobs-service/kogito-addons-quarkus-jobs-service-embedded/runtime/src/main/java/org/kie/kogito/addons/quarkus/jobs/service/embedded/KogitoAddonsQuarkusJobsServiceEmbeddedRuntimeConfig.java @@ -64,4 +64,23 @@ public class KogitoAddonsQuarkusJobsServiceEmbeddedRuntimeConfig { @ConfigItem(name = "forceExecuteExpiredJobs", defaultValue = "true") public boolean forceExecuteExpiredJobs; + /** + * Flag to allow that jobs that where timed-out when the jobs service was down, must be fired immediately at the + * jobs service next startup. + */ + @ConfigItem(name = "forceExecuteExpiredJobsOnServiceStart", defaultValue = "true") + boolean forceExecuteExpiredJobsOnServiceStart; + + /** + * Number of retries configured for the periodic jobs loading procedure. Every time the procedure is started this + * value is considered. + */ + @ConfigItem(name = "loadJobRetries", defaultValue = "3") + int loadJobRetries; + + /** + * Error strategy to apply when the periodic jobs loading procedure has exceeded the jobLoadReties. + */ + @ConfigItem(name = "loadJobErrorStrategy", defaultValue = "NONE") + String loadJobErrorStrategy; } diff --git a/jobs-service/kogito-addons-jobs-service/kogito-addons-quarkus-jobs-service-embedded/runtime/src/main/java/org/kie/kogito/addons/quarkus/jobs/service/embedded/stream/EventPublisherJobStreams.java b/jobs-service/kogito-addons-jobs-service/kogito-addons-quarkus-jobs-service-embedded/runtime/src/main/java/org/kie/kogito/addons/quarkus/jobs/service/embedded/stream/EventPublisherJobStreams.java index 7121c9b78b..7032eebd39 100644 --- a/jobs-service/kogito-addons-jobs-service/kogito-addons-quarkus-jobs-service-embedded/runtime/src/main/java/org/kie/kogito/addons/quarkus/jobs/service/embedded/stream/EventPublisherJobStreams.java +++ b/jobs-service/kogito-addons-jobs-service/kogito-addons-quarkus-jobs-service-embedded/runtime/src/main/java/org/kie/kogito/addons/quarkus/jobs/service/embedded/stream/EventPublisherJobStreams.java @@ -19,11 +19,11 @@ package org.kie.kogito.addons.quarkus.jobs.service.embedded.stream; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import org.eclipse.microprofile.config.inject.ConfigProperty; -import org.eclipse.microprofile.reactive.messaging.Acknowledgment; -import org.eclipse.microprofile.reactive.messaging.Incoming; +import org.eclipse.microprofile.context.ManagedExecutor; import org.kie.kogito.event.EventPublisher; import org.kie.kogito.event.job.JobInstanceDataEvent; import org.kie.kogito.jobs.JobsServiceException; @@ -31,15 +31,14 @@ import org.kie.kogito.jobs.service.model.JobDetails; import org.kie.kogito.jobs.service.model.ScheduledJob; import org.kie.kogito.jobs.service.resource.RestApiConstants; -import org.kie.kogito.jobs.service.stream.AvailableStreams; +import org.kie.kogito.jobs.service.stream.JobEventPublisher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.ObjectMapper; -import io.smallrye.reactive.messaging.annotations.Blocking; - import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Alternative; import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; @@ -50,7 +49,8 @@ * EventPublisher API. Events propagation is enabled only when the embedded data index is present in current application. */ @ApplicationScoped -public class EventPublisherJobStreams { +@Alternative +public class EventPublisherJobStreams implements JobEventPublisher { public static final String DATA_INDEX_EVENT_PUBLISHER = "org.kie.kogito.index.addon.DataIndexEventPublisher"; @@ -62,41 +62,52 @@ public class EventPublisherJobStreams { private final ObjectMapper objectMapper; + private final ManagedExecutor managedExecutor; + @Inject public EventPublisherJobStreams(@ConfigProperty(name = "kogito.service.url", defaultValue = "http://localhost:8080") String url, Instance eventPublishers, - ObjectMapper objectMapper) { + ObjectMapper objectMapper, + ManagedExecutor managedExecutor) { this.url = url; eventPublisher = eventPublishers.stream().collect(Collectors.toList()); this.objectMapper = objectMapper; + this.managedExecutor = managedExecutor; } - @Incoming(AvailableStreams.JOB_STATUS_CHANGE_EVENTS) - @Acknowledgment(Acknowledgment.Strategy.PRE_PROCESSING) - @Blocking - public void onJobStatusChange(JobDetails jobDetails) { - if (eventPublisher != null) { - ScheduledJob scheduledJob = ScheduledJobAdapter.of(jobDetails); - byte[] jsonContent; - try { - jsonContent = objectMapper.writeValueAsBytes(scheduledJob); - } catch (Exception e) { - throw new JobsServiceException("It was not possible to serialize scheduledJob to json: " + scheduledJob, e); - } - JobInstanceDataEvent event = new JobInstanceDataEvent(JOB_EVENT_TYPE, - url + RestApiConstants.JOBS_PATH, - jsonContent, - scheduledJob.getProcessInstanceId(), - scheduledJob.getRootProcessInstanceId(), - scheduledJob.getProcessId(), - scheduledJob.getRootProcessId(), - null); - try { - eventPublisher.forEach(e -> e.publish(event)); - } catch (Exception e) { - LOGGER.error("Job status change propagation has failed at eventPublisher: " + eventPublisher.getClass() + " execution.", e); + @Override + public JobDetails publishJobStatusChange(JobDetails jobDetails) { + try { + managedExecutor.runAsync(() -> { + if (eventPublisher != null) { + ScheduledJob scheduledJob = ScheduledJobAdapter.of(jobDetails); + byte[] jsonContent; + try { + jsonContent = objectMapper.writeValueAsBytes(scheduledJob); + } catch (Exception e) { + throw new JobsServiceException("It was not possible to serialize scheduledJob to json: " + scheduledJob, e); + } + JobInstanceDataEvent event = new JobInstanceDataEvent(JOB_EVENT_TYPE, + url + RestApiConstants.JOBS_PATH, + jsonContent, + scheduledJob.getProcessInstanceId(), + scheduledJob.getRootProcessInstanceId(), + scheduledJob.getProcessId(), + scheduledJob.getRootProcessId(), + null); + try { + eventPublisher.forEach(e -> e.publish(event)); + } catch (Exception e) { + LOGGER.error("Job status change propagation has failed at eventPublisher: " + eventPublisher.getClass() + " execution.", e); + } + } + }).get(); + } catch (InterruptedException | ExecutionException e) { + if (e instanceof InterruptedException) { + Thread.currentThread().interrupt(); } + throw new RuntimeException("Job status change propagation has failed.", e); } + return jobDetails; } - } diff --git a/jobs-service/kogito-addons-jobs-service/kogito-addons-quarkus-jobs-service-embedded/runtime/src/main/resources/application.properties b/jobs-service/kogito-addons-jobs-service/kogito-addons-quarkus-jobs-service-embedded/runtime/src/main/resources/application.properties index c1852d00aa..58cf6e8a22 100644 --- a/jobs-service/kogito-addons-jobs-service/kogito-addons-quarkus-jobs-service-embedded/runtime/src/main/resources/application.properties +++ b/jobs-service/kogito-addons-jobs-service/kogito-addons-quarkus-jobs-service-embedded/runtime/src/main/resources/application.properties @@ -16,6 +16,7 @@ # specific language governing permissions and limitations # under the License. # +quarkus.arc.selected-alternatives=org.kie.kogito.addons.quarkus.jobs.service.embedded.stream.* %dev.quarkus.log.category."org.kie.kogito.jobs".level=INFO diff --git a/jobs-service/kogito-addons-jobs-service/kogito-addons-quarkus-jobs-service-embedded/runtime/src/test/java/org/kie/kogito/addons/quarkus/jobs/service/embedded/stream/EventPublisherJobStreamsTest.java b/jobs-service/kogito-addons-jobs-service/kogito-addons-quarkus-jobs-service-embedded/runtime/src/test/java/org/kie/kogito/addons/quarkus/jobs/service/embedded/stream/EventPublisherJobStreamsTest.java index 3fec2fee68..c6081945c6 100644 --- a/jobs-service/kogito-addons-jobs-service/kogito-addons-quarkus-jobs-service-embedded/runtime/src/test/java/org/kie/kogito/addons/quarkus/jobs/service/embedded/stream/EventPublisherJobStreamsTest.java +++ b/jobs-service/kogito-addons-jobs-service/kogito-addons-quarkus-jobs-service-embedded/runtime/src/test/java/org/kie/kogito/addons/quarkus/jobs/service/embedded/stream/EventPublisherJobStreamsTest.java @@ -22,8 +22,10 @@ import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Date; +import java.util.concurrent.CompletableFuture; import java.util.stream.Stream; +import org.eclipse.microprofile.context.ManagedExecutor; import org.junit.jupiter.api.Test; import org.kie.kogito.event.EventPublisher; import org.kie.kogito.event.job.JobInstanceDataEvent; @@ -44,6 +46,7 @@ import jakarta.enterprise.inject.Instance; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.anyCollection; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -93,14 +96,20 @@ void onJobStatusChange() throws Exception { Instance eventPublisherInstance = mock(Instance.class); Stream eventPublishers = Arrays.stream(new EventPublisher[] { eventPublisher }); doReturn(eventPublishers).when(eventPublisherInstance).stream(); + ManagedExecutor managedExecutor = mock(ManagedExecutor.class); + ArgumentCaptor runnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class); + CompletableFuture completableFuture = CompletableFuture.completedFuture(null); + doReturn(completableFuture).when(managedExecutor).runAsync(any()); ObjectMapper objectMapper = new ObjectMapper() .registerModule(new JavaTimeModule()) .disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - EventPublisherJobStreams eventPublisherJobStreams = new EventPublisherJobStreams(URL, eventPublisherInstance, objectMapper); + EventPublisherJobStreams eventPublisherJobStreams = new EventPublisherJobStreams(URL, eventPublisherInstance, objectMapper, managedExecutor); JobDetails jobDetails = buildJobDetails(); - eventPublisherJobStreams.onJobStatusChange(jobDetails); + eventPublisherJobStreams.publishJobStatusChange(jobDetails); + verify(managedExecutor).runAsync(runnableArgumentCaptor.capture()); + runnableArgumentCaptor.getValue().run(); verify(eventPublisher).publish(eventCaptor.capture()); verify(eventPublisher, never()).publish(anyCollection()); diff --git a/jobs-service/kogito-addons-jobs-service/kogito-addons-quarkus-jobs/src/main/java/org/kie/kogito/jobs/embedded/JobInVMEventPublisher.java b/jobs-service/kogito-addons-jobs-service/kogito-addons-quarkus-jobs/src/main/java/org/kie/kogito/jobs/embedded/JobInVMEventPublisher.java index aa8f331a8e..dae4ac4ecf 100644 --- a/jobs-service/kogito-addons-jobs-service/kogito-addons-quarkus-jobs/src/main/java/org/kie/kogito/jobs/embedded/JobInVMEventPublisher.java +++ b/jobs-service/kogito-addons-jobs-service/kogito-addons-quarkus-jobs/src/main/java/org/kie/kogito/jobs/embedded/JobInVMEventPublisher.java @@ -20,7 +20,6 @@ package org.kie.kogito.jobs.embedded; import java.util.List; -import java.util.Optional; import java.util.concurrent.ExecutionException; import org.eclipse.microprofile.config.inject.ConfigProperty; @@ -30,12 +29,9 @@ import org.kie.kogito.jobs.service.adapter.ScheduledJobAdapter; import org.kie.kogito.jobs.service.api.Recipient; import org.kie.kogito.jobs.service.model.JobDetails; -import org.kie.kogito.jobs.service.model.JobExecutionResponse; import org.kie.kogito.jobs.service.model.ScheduledJob; import org.kie.kogito.jobs.service.resource.RestApiConstants; -import org.kie.kogito.jobs.service.scheduler.ReactiveJobScheduler; import org.kie.kogito.jobs.service.stream.JobEventPublisher; -import org.kie.kogito.jobs.service.utils.ErrorHandling; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -63,9 +59,6 @@ public class JobInVMEventPublisher implements JobEventPublisher { private final ObjectMapper objectMapper; - @Inject - ReactiveJobScheduler scheduler; - @Inject Event bus; @@ -79,47 +72,6 @@ public JobInVMEventPublisher( LOGGER.info("JobInVMEventPublisher Started with url {}", url); } - @Override - public JobExecutionResponse publishJobError(JobExecutionResponse response) { - try { - LOGGER.debug("publishJobError {}", response); - - ErrorHandling.skipErrorPublisherBuilder(scheduler::handleJobExecutionError, response) - .findFirst() - .run() - .thenApply(Optional::isPresent) - .exceptionally(e -> { - LOGGER.error("Error handling error {}", response, e); - return false; - }).toCompletableFuture().get(); - - return response; - } catch (Exception e) { - LOGGER.error("error in publishJobError", e); - return response; - } - } - - @Override - public JobExecutionResponse publishJobSuccess(JobExecutionResponse response) { - try { - LOGGER.debug("publishJobSuccess {}", response); - ErrorHandling.skipErrorPublisherBuilder(scheduler::handleJobExecutionSuccess, response) - .findFirst() - .run() - .thenApply(Optional::isPresent) - .exceptionally(e -> { - LOGGER.error("Error handling error {}", response, e); - return false; - }).toCompletableFuture().get(); - - return response; - } catch (Exception e) { - LOGGER.error("error in publishJobSuccess", e); - return response; - } - } - @Override public JobDetails publishJobStatusChange(JobDetails jobDetails) { try { diff --git a/kogito-apps-bom/pom.xml b/kogito-apps-bom/pom.xml index ae575044b9..bef8a8b114 100644 --- a/kogito-apps-bom/pom.xml +++ b/kogito-apps-bom/pom.xml @@ -968,63 +968,6 @@ javadoc - - - org.kie.kogito - management-console - ${project.version} - - - - - org.kie.kogito - task-console - ${project.version} - - - - - org.jbpm - jbpm-quarkus-devui - ${project.version} - - - org.jbpm - jbpm-quarkus-devui-deployment - ${project.version} - - - - - org.apache.kie.sonataflow - sonataflow-quarkus-devui - ${project.version} - - - org.apache.kie.sonataflow - sonataflow-quarkus-devui - ${project.version} - sources - - - org.apache.kie.sonataflow - sonataflow-quarkus-devui-deployment - ${project.version} - - - org.apache.kie.sonataflow - sonataflow-quarkus-devui-deployment - ${project.version} - sources - - - - - org.kie.kogito - kogito-apps-ui-packages - ${project.version} - pom - diff --git a/kogito-apps-build-parent/pom.xml b/kogito-apps-build-parent/pom.xml index b3b66c2c02..644a451609 100644 --- a/kogito-apps-build-parent/pom.xml +++ b/kogito-apps-build-parent/pom.xml @@ -36,10 +36,6 @@ false - false - false - ${skipTests} - ${skipITs} **/*IT.java **/Native*IT.java @@ -61,7 +57,6 @@ org.kie.kogito/data-index-service-inmemory:${project.version} org.kie.kogito/trusty-service-infinispan:${project.version} org.kie.kogito/trusty-service:${project.version} - org.kie.kogito/trusty-ui:${project.version} org.kie.kogito/integration-tests-trusty-service-quarkus:${project.version} @@ -268,10 +263,6 @@ true - true - true - true - true **/Native*IT.java 8g @@ -294,20 +285,6 @@ - - skipUI - - - skipUI - - - - true - true - true - true - - quickly @@ -316,8 +293,6 @@ - true - true true true true diff --git a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui-deployment/pom.xml b/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui-deployment/pom.xml deleted file mode 100644 index 5527a28c56..0000000000 --- a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui-deployment/pom.xml +++ /dev/null @@ -1,202 +0,0 @@ - - - - 4.0.0 - - - org.apache.kie.sonataflow - sonataflow-quarkus-devui-parent - 999-SNAPSHOT - - - sonataflow-quarkus-devui-deployment - Kogito Apps :: SonataFlow Quarkus Dev UI Extension :: Deployment - - - ../../ui-packages/packages/runtime-tools-dev-ui-webapp - - - - - - io.quarkus - quarkus-development-mode-spi - - - io.quarkus - quarkus-core-deployment - - - io.quarkus - quarkus-arc-deployment - - - io.quarkus - quarkus-resteasy-deployment - - - io.quarkus - quarkus-resteasy-jackson-deployment - - - io.quarkus - quarkus-rest-client-deployment - - - io.quarkus - quarkus-resteasy-multipart-deployment - - - org.kie.kogito - kogito-quarkus-extension-spi - - - - - io.quarkus - quarkus-undertow-deployment - - - - - io.rest-assured - rest-assured - test - - - org.mockito - mockito-core - test - - - org.apache.kie.sonataflow - sonataflow-quarkus-devui - ${project.version} - - - io.quarkus - quarkus-junit5-internal - test - - - org.kie.kogito - kogito-apps-ui-packages - pom - - - - - - - maven-compiler-plugin - - - - io.quarkus - quarkus-extension-processor - ${version.io.quarkus} - - - - - - maven-resources-plugin - - - copy-webapp - process-resources - - copy-resources - - - ${basedir}/target/classes/dev-static/resources/webapp - - - ${path.to.webapp.app}/dist/resources/webapp - false - - - ${path.to.webapp.app}/dist/webapp - false - - - - - - - copy-envelope-resources - process-resources - - copy-resources - - - ${basedir}/target/classes/dev-static/resources - - - ${path.to.webapp.app}/dist/resources - - form-displayer.html - form-displayer.js - serverless-workflow-combined-editor-envelope.html - serverless-workflow-combined-editor-envelope.js - serverless-workflow-text-editor-envelope.html - serverless-workflow-text-editor-envelope.js - serverless-workflow-diagram-editor-envelope.html - serverless-workflow-diagram-editor-envelope.js - - - - - ${path.to.webapp.app}/dist/resources - - diagram/ - - - - - - - - copy-index - process-resources - - copy-resources - - - ${basedir}/target/classes/dev-static - - - ${basedir}/target/classes/static - - index.html - - - - - - - - - - diff --git a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui-deployment/src/main/java/org/kie/kogito/swf/tools/deployment/DevConsoleProcessor.java b/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui-deployment/src/main/java/org/kie/kogito/swf/tools/deployment/DevConsoleProcessor.java deleted file mode 100644 index 6d6d126d63..0000000000 --- a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui-deployment/src/main/java/org/kie/kogito/swf/tools/deployment/DevConsoleProcessor.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.swf.tools.deployment; - -import java.io.IOException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.util.List; - -import org.kie.kogito.swf.tools.runtime.config.DevUIStaticArtifactsRecorder; - -import io.quarkus.deployment.IsDevelopment; -import io.quarkus.deployment.annotations.BuildProducer; -import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.annotations.ExecutionTime; -import io.quarkus.deployment.annotations.Record; -import io.quarkus.deployment.builditem.*; -import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; -import io.quarkus.deployment.util.WebJarUtil; -import io.quarkus.devui.spi.page.CardPageBuildItem; -import io.quarkus.devui.spi.page.Page; -import io.quarkus.maven.dependency.ResolvedDependency; -import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; -import io.quarkus.vertx.http.deployment.RouteBuildItem; -import io.quarkus.vertx.http.runtime.management.ManagementInterfaceBuildTimeConfig; - -public class DevConsoleProcessor { - - private static final String STATIC_RESOURCES_PATH = "dev-static/"; - private static final String BASE_RELATIVE_URL = "/q/dev-ui/org.kie.kogito.sonataflow-quarkus-devui"; - - @BuildStep(onlyIf = IsDevelopment.class) - public CardPageBuildItem pages(NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem, - ManagementInterfaceBuildTimeConfig managementInterfaceBuildTimeConfig, - LaunchModeBuildItem launchModeBuildItem, - List systemPropertyBuildItems, - ConfigurationBuildItem configurationBuildItem) { - - String uiPath = nonApplicationRootPathBuildItem.resolveManagementPath(BASE_RELATIVE_URL, - managementInterfaceBuildTimeConfig, launchModeBuildItem, true); - - String devUIUrl = getProperty(configurationBuildItem, systemPropertyBuildItems, "kogito.dev-ui.url"); - String devUIUrlQueryParam = devUIUrl != null ? "&devUIUrl=" + URLEncoder.encode(devUIUrl, StandardCharsets.UTF_8) : ""; - - String dataIndexUrl = getProperty(configurationBuildItem, systemPropertyBuildItems, "kogito.data-index.url"); - String dataIndexUrlQueryParam = dataIndexUrl != null ? "&dataIndexUrl=" + URLEncoder.encode(dataIndexUrl, StandardCharsets.UTF_8) : ""; - - CardPageBuildItem cardPageBuildItem = new CardPageBuildItem(); - - cardPageBuildItem.addPage(Page.externalPageBuilder("Workflows") - .url(uiPath + "/index.html?page=Processes" + devUIUrlQueryParam + dataIndexUrlQueryParam, uiPath) - .isHtmlContent() - .icon("font-awesome-solid:diagram-project")); - - cardPageBuildItem.addPage(Page.externalPageBuilder("Monitoring") - .url(uiPath + "/index.html?page=Monitoring" + devUIUrlQueryParam + dataIndexUrlQueryParam, uiPath) - .isHtmlContent() - .icon("font-awesome-solid:gauge-high")); - - return cardPageBuildItem; - } - - @BuildStep(onlyIf = IsDevelopment.class) - @Record(ExecutionTime.RUNTIME_INIT) - public void deployStaticResources(final DevUIStaticArtifactsRecorder devUIStaticArtifactsRecorder, - final CurateOutcomeBuildItem curateOutcomeBuildItem, - final LiveReloadBuildItem liveReloadBuildItem, - final LaunchModeBuildItem launchMode, - final ShutdownContextBuildItem shutdownContext, - final BuildProducer routeBuildItemBuildProducer) throws IOException { - ResolvedDependency devConsoleResourcesArtifact = WebJarUtil.getAppArtifact(curateOutcomeBuildItem, - "org.apache.kie.sonataflow", - "sonataflow-quarkus-devui-deployment"); - - Path devConsoleStaticResourcesDeploymentPath = WebJarUtil.copyResourcesForDevOrTest( - liveReloadBuildItem, - curateOutcomeBuildItem, - launchMode, - devConsoleResourcesArtifact, - STATIC_RESOURCES_PATH, - true); - - routeBuildItemBuildProducer.produce(new RouteBuildItem.Builder() - .route(BASE_RELATIVE_URL + "/*") - .handler(devUIStaticArtifactsRecorder.handler(devConsoleStaticResourcesDeploymentPath.toString(), - shutdownContext)) - .build()); - } - - private static String getProperty(ConfigurationBuildItem configurationBuildItem, - List systemPropertyBuildItems, String propertyKey) { - - String propertyValue = configurationBuildItem - .getReadResult() - .getAllBuildTimeValues() - .get(propertyKey); - - if (propertyValue == null) { - propertyValue = configurationBuildItem - .getReadResult() - .getBuildTimeRunTimeValues() - .get(propertyKey); - } else { - return propertyValue; - } - - if (propertyValue == null) { - propertyValue = configurationBuildItem - .getReadResult() - .getRunTimeDefaultValues() - .get(propertyKey); - } - - if (propertyValue != null) { - return propertyValue; - } - - return systemPropertyBuildItems.stream().filter(property -> property.getKey().equals(propertyKey)) - .findAny() - .map(SystemPropertyBuildItem::getValue).orElse(null); - } -} diff --git a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui-deployment/src/main/java/org/kie/kogito/swf/tools/deployment/ServerlessWorkflowQuarkusExtensionProcessor.java b/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui-deployment/src/main/java/org/kie/kogito/swf/tools/deployment/ServerlessWorkflowQuarkusExtensionProcessor.java deleted file mode 100644 index 2f9c2e3365..0000000000 --- a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui-deployment/src/main/java/org/kie/kogito/swf/tools/deployment/ServerlessWorkflowQuarkusExtensionProcessor.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.swf.tools.deployment; - -import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.builditem.FeatureBuildItem; - -class ServerlessWorkflowQuarkusExtensionProcessor { - - private static final String FEATURE = "sonataflow-quarkus-devui"; - - @BuildStep - FeatureBuildItem feature() { - return new FeatureBuildItem(FEATURE); - } -} diff --git a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui-deployment/src/main/resources/static/index.html b/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui-deployment/src/main/resources/static/index.html deleted file mode 100644 index 4040f1fbea..0000000000 --- a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui-deployment/src/main/resources/static/index.html +++ /dev/null @@ -1,48 +0,0 @@ - -
- - - diff --git a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/pom.xml b/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/pom.xml deleted file mode 100644 index cbd4857c38..0000000000 --- a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/pom.xml +++ /dev/null @@ -1,157 +0,0 @@ - - - - 4.0.0 - - org.apache.kie.sonataflow - sonataflow-quarkus-devui-parent - 999-SNAPSHOT - - sonataflow-quarkus-devui - Kogito Apps :: SonataFlow Quarkus Dev UI Extension :: Runtime - Runtime development tools for Serverless Workflows - - - - io.quarkus - quarkus-core - - - - io.quarkus - quarkus-resteasy - - - - io.quarkus - quarkus-resteasy-jackson - - - - io.quarkus - quarkus-rest-client - - - - io.quarkus - quarkus-resteasy-multipart - - - - io.quarkus - quarkus-arc - - - - io.quarkus.arc - arc-processor - - - - - io.quarkus - quarkus-undertow - - - - jakarta.annotation - jakarta.annotation-api - - - - org.apache.commons - commons-lang3 - - - - org.junit.jupiter - junit-jupiter-engine - test - - - - org.mockito - mockito-junit-jupiter - test - - - - io.quarkus - quarkus-junit5 - test - - - - io.quarkus - quarkus-junit5-mockito - test - - - - io.rest-assured - rest-assured - test - - - org.awaitility - awaitility - test - - - - - - io.quarkus - quarkus-extension-maven-plugin - ${version.io.quarkus} - - - compile - - extension-descriptor - - - ${project.groupId}:${project.artifactId}-deployment:${project.version} - - org.apache.kie.sonataflow.quarkus.dev-ui - - - - - - - maven-compiler-plugin - - - - io.quarkus - quarkus-extension-processor - ${version.io.quarkus} - - - - - - - diff --git a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/custom/dashboard/CustomDashboardService.java b/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/custom/dashboard/CustomDashboardService.java deleted file mode 100644 index 7dcc95128a..0000000000 --- a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/custom/dashboard/CustomDashboardService.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.swf.tools.custom.dashboard; - -import org.kie.kogito.swf.tools.custom.dashboard.model.CustomDashboardFilter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import jakarta.inject.Inject; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.QueryParam; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; - -import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; - -@Path("/customDashboard") -public class CustomDashboardService { - - private static final Logger LOGGER = LoggerFactory.getLogger(CustomDashboardService.class); - - private CustomDashboardStorage storage; - - @Inject - public void setStorage(CustomDashboardStorage storage) { - this.storage = storage; - } - - @GET - @Path("/count") - @Produces(MediaType.TEXT_PLAIN) - public Response getCustomDashboardFilesCount() { - try { - return Response.ok(storage.getCustomDashboardFilesCount()).build(); - } catch (Exception e) { - LOGGER.warn("Error while getting CustomDashboard file count: ", e); - return Response.status(INTERNAL_SERVER_ERROR.getStatusCode(), "Unexpected error while getting CustomDashboard files count: " + e.getMessage()).build(); - } - } - - @GET - @Path("/list") - @Produces(MediaType.APPLICATION_JSON) - public Response getCustomDashboardFiles(@QueryParam("names") CustomDashboardFilter filter) { - try { - return Response.ok(storage.getCustomDashboardFiles(filter)).build(); - } catch (Exception e) { - LOGGER.warn("Error while getting CustomDashboard list: ", e); - return Response.status(INTERNAL_SERVER_ERROR.getStatusCode(), "Unexpected error while getting CustomDashboard files list: " + e.getMessage()).build(); - } - } - - @GET - @Path("/{name:\\S+}") - @Produces(MediaType.APPLICATION_JSON) - public Response getCustomDashboardFileContent(@PathParam("name") String name) { - try { - return Response.ok(storage.getCustomDashboardFileContent(name)).build(); - } catch (Exception e) { - LOGGER.warn("Error while getting CustomDashboard file content: ", e); - return Response.status(INTERNAL_SERVER_ERROR.getStatusCode(), "Unexpected error while getting CustomDashboard file content: " + e.getMessage()).build(); - } - } -} diff --git a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/custom/dashboard/CustomDashboardStorage.java b/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/custom/dashboard/CustomDashboardStorage.java deleted file mode 100644 index 7d9c3a3d21..0000000000 --- a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/custom/dashboard/CustomDashboardStorage.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.swf.tools.custom.dashboard; - -import java.io.IOException; -import java.util.Collection; - -import org.kie.kogito.swf.tools.custom.dashboard.model.CustomDashboardFilter; -import org.kie.kogito.swf.tools.custom.dashboard.model.CustomDashboardInfo; - -public interface CustomDashboardStorage { - - int getCustomDashboardFilesCount(); - - Collection getCustomDashboardFiles(CustomDashboardFilter filter); - - String getCustomDashboardFileContent(String name) throws IOException; - - void updateCustomDashboard(String content); -} diff --git a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/custom/dashboard/converter/CustomDashboardFilterParamConverter.java b/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/custom/dashboard/converter/CustomDashboardFilterParamConverter.java deleted file mode 100644 index 7ee3476df9..0000000000 --- a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/custom/dashboard/converter/CustomDashboardFilterParamConverter.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.swf.tools.custom.dashboard.converter; - -import java.util.Collections; -import java.util.StringTokenizer; -import java.util.stream.Collectors; - -import org.kie.kogito.swf.tools.custom.dashboard.model.CustomDashboardFilter; - -import jakarta.ws.rs.ext.ParamConverter; -import jakarta.ws.rs.ext.Provider; - -@Provider -public class CustomDashboardFilterParamConverter implements ParamConverter { - public CustomDashboardFilter fromString(String names) { - StringTokenizer stringTokenizer = new StringTokenizer(names, ";"); - return new CustomDashboardFilter(Collections.list(stringTokenizer).stream().map(s -> (String) s).collect(Collectors.toList())); - } - - public String toString(CustomDashboardFilter names) { - return names.toString(); - } -} diff --git a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/custom/dashboard/converter/CustomDashboardFilterParamConverterProvider.java b/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/custom/dashboard/converter/CustomDashboardFilterParamConverterProvider.java deleted file mode 100644 index a6043248ad..0000000000 --- a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/custom/dashboard/converter/CustomDashboardFilterParamConverterProvider.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.swf.tools.custom.dashboard.converter; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; - -import org.kie.kogito.swf.tools.custom.dashboard.model.CustomDashboardFilter; - -import jakarta.ws.rs.ext.ParamConverter; -import jakarta.ws.rs.ext.ParamConverterProvider; -import jakarta.ws.rs.ext.Provider; - -@Provider -public class CustomDashboardFilterParamConverterProvider implements ParamConverterProvider { - - @SuppressWarnings("unchecked") - @Override - public ParamConverter getConverter(Class rawType, Type genericType, Annotation[] annotations) { - if (rawType.isAssignableFrom(CustomDashboardFilter.class)) { - return (ParamConverter) new CustomDashboardFilterParamConverter(); - } - return null; - } -} diff --git a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/custom/dashboard/impl/CustomDashboardStorageImpl.java b/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/custom/dashboard/impl/CustomDashboardStorageImpl.java deleted file mode 100644 index a2a00437c0..0000000000 --- a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/custom/dashboard/impl/CustomDashboardStorageImpl.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.swf.tools.custom.dashboard.impl; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.nio.file.FileSystems; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.WatchEvent; -import java.nio.file.WatchKey; -import java.nio.file.WatchService; -import java.nio.file.attribute.BasicFileAttributes; -import java.time.Instant; -import java.time.LocalDateTime; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.TimeZone; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringUtils; -import org.eclipse.microprofile.config.ConfigProvider; -import org.kie.kogito.swf.tools.custom.dashboard.CustomDashboardStorage; -import org.kie.kogito.swf.tools.custom.dashboard.model.CustomDashboardFilter; -import org.kie.kogito.swf.tools.custom.dashboard.model.CustomDashboardInfo; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import jakarta.enterprise.context.ApplicationScoped; - -import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; -import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE; -import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; - -@ApplicationScoped -public class CustomDashboardStorageImpl implements CustomDashboardStorage { - - public static final String PROJECT_CUSTOM_DASHBOARD_STORAGE_PROP = "quarkus.kogito-runtime-tools.custom.dashboard.folder"; - private static final String CUSTOM_DASHBOARD_STORAGE_PATH = "/dashboards/"; - private static final Logger LOGGER = LoggerFactory.getLogger(CustomDashboardStorageImpl.class); - - private final Map customDashboardInfoMap = new HashMap<>(); - - private URL classLoaderCustomDashboardUrl; - private URL customDashStorageUrl; - - public CustomDashboardStorageImpl() { - start(Thread.currentThread().getContextClassLoader().getResource(CUSTOM_DASHBOARD_STORAGE_PATH)); - } - - public CustomDashboardStorageImpl(final URL classLoaderFormsUrl) { - start(classLoaderFormsUrl); - } - - private void start(final URL classLoaderFormsUrl) { - start(classLoaderFormsUrl, getCustomDashboardStorageUrl(classLoaderFormsUrl)); - } - - private void start(final URL classLoaderCustomDashboardUrl, final URL customDashStorageUrl) { - try { - this.classLoaderCustomDashboardUrl = classLoaderCustomDashboardUrl; - this.customDashStorageUrl = customDashStorageUrl; - } catch (Exception ex) { - LOGGER.warn("Couldn't properly initialize CustomDashboardStorageImpl"); - } finally { - if (classLoaderCustomDashboardUrl == null) { - return; - } - - init(readCustomDashboardResources()); - String storageUrl = getStorageUrl(classLoaderCustomDashboardUrl); - Thread t = new Thread(new DashboardFilesWatcher(reload(), storageUrl)); - t.start(); - } - } - - protected String getStorageUrl(URL classLoaderCustomDashboardUrl) { - return ConfigProvider.getConfig() - .getOptionalValue(PROJECT_CUSTOM_DASHBOARD_STORAGE_PROP, String.class) - .orElseGet(() -> classLoaderCustomDashboardUrl.getFile()); - } - - private URL getCustomDashboardStorageUrl(URL classLoaderCustomDashboardUrl) { - if (classLoaderCustomDashboardUrl == null) { - return null; - } - - String storageUrl = getStorageUrl(classLoaderCustomDashboardUrl); - - File customDashStorageeFolder = new File(storageUrl); - - if (!customDashStorageeFolder.exists() || !customDashStorageeFolder.isDirectory()) { - LOGGER.warn("Cannot initialize form storage folder in path '" + customDashStorageeFolder.getPath() + "'"); - } - - try { - return customDashStorageeFolder.toURI().toURL(); - } catch (MalformedURLException ex) { - LOGGER.warn("Cannot initialize form storage folder in path '" + customDashStorageeFolder.getPath() + "'", ex); - } - return null; - } - - @Override - public int getCustomDashboardFilesCount() { - return customDashboardInfoMap.size(); - } - - @Override - public Collection getCustomDashboardFiles(CustomDashboardFilter filter) { - if (filter != null && !filter.getNames().isEmpty()) { - return customDashboardInfoMap.entrySet().stream() - .filter(entry -> StringUtils.containsAnyIgnoreCase(entry.getKey(), filter.getNames().toArray(new String[0]))) - .map(Map.Entry::getValue) - .collect(Collectors.toList()); - } else { - return customDashboardInfoMap.values(); - } - } - - @Override - public String getCustomDashboardFileContent(String name) throws IOException { - try { - return IOUtils.toString(new FileInputStream(customDashboardInfoMap.get(name).getPath()), StandardCharsets.UTF_8); - } catch (IOException e) { - LOGGER.info("custom-dashboard's file {} can not ready, because of {}", customDashboardInfoMap.get(name).getPath(), e.getMessage()); - throw e; - } - } - - @Override - public void updateCustomDashboard(String content) { - - } - - private void init(Collection files) { - customDashboardInfoMap.clear(); - files.stream() - .forEach(file -> { - LocalDateTime lastModified = LocalDateTime.ofInstant(Instant.ofEpochMilli(file.lastModified()), TimeZone.getDefault().toZoneId()); - customDashboardInfoMap.put(file.getName(), - new CustomDashboardInfo(file.getName(), file.getPath(), lastModified)); - }); - } - - private Collection readCustomDashboardResources() { - if (classLoaderCustomDashboardUrl != null) { - LOGGER.info("custom-dashboard's files path is {}", classLoaderCustomDashboardUrl.toString()); - File rootFolder = FileUtils.toFile(classLoaderCustomDashboardUrl); - return FileUtils.listFiles(rootFolder, new String[] { "dash.yaml", "dash.yml" }, true); - } - return Collections.emptyList(); - } - - private Consumer> reload() { - return this::init; - } - - private class DashboardFilesWatcher implements Runnable { - - private final Map keys = new HashMap<>(); - private Consumer> consumer; - private String folder; - - public DashboardFilesWatcher(Consumer> consumer, String folder) { - this.consumer = consumer; - this.folder = folder; - } - - @Override - public void run() { - try (WatchService ws = FileSystems.getDefault().newWatchService()) { - Path path = Path.of(folder); - keys.put(path.register(ws, ENTRY_MODIFY, ENTRY_CREATE), path); - - Files.walkFileTree(path, new SimpleFileVisitor() { - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - keys.put(dir.register(ws, ENTRY_MODIFY, ENTRY_CREATE, ENTRY_DELETE), dir); - return FileVisitResult.CONTINUE; - } - }); - WatchKey key; - while ((key = ws.take()) != null) { - for (WatchEvent event : key.pollEvents()) { - LOGGER.warn("Event kind: {}. File affected: {}", event.kind(), event.context()); - consumer.accept(readCustomDashboardResources()); - } - key.reset(); - } - } catch (InterruptedException e) { - LOGGER.warn("Exception in custom dashboard folder watcher for folder: {}, message: {}", folder, e.getMessage(), e); - Thread.currentThread().interrupt(); - } catch (IOException ex) { - LOGGER.warn("Exception in custom dashboard folder watcher for folder: {}, message: {}", folder, ex.getMessage(), ex); - } - } - } -} diff --git a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/custom/dashboard/model/CustomDashboardFilter.java b/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/custom/dashboard/model/CustomDashboardFilter.java deleted file mode 100644 index d3982535ce..0000000000 --- a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/custom/dashboard/model/CustomDashboardFilter.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.swf.tools.custom.dashboard.model; - -import java.util.ArrayList; -import java.util.List; - -public class CustomDashboardFilter { - - private final List names; - - public CustomDashboardFilter() { - this.names = new ArrayList<>(); - } - - public CustomDashboardFilter(List names) { - this.names = names; - } - - public List getNames() { - return names; - } - - public void setNames(List names) { - this.names.addAll(names); - } - - @Override - public String toString() { - return "CustomDashboardFilter{" + - "names=" + names + - '}'; - } -} diff --git a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/custom/dashboard/model/CustomDashboardInfo.java b/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/custom/dashboard/model/CustomDashboardInfo.java deleted file mode 100644 index a7f80ac6af..0000000000 --- a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/custom/dashboard/model/CustomDashboardInfo.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.swf.tools.custom.dashboard.model; - -import java.time.LocalDateTime; - -public class CustomDashboardInfo { - String name; - String path; - LocalDateTime lastModified; - - public CustomDashboardInfo(String name, String path, LocalDateTime lastModified) { - this.name = name; - this.path = path; - this.lastModified = lastModified; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getPath() { - return path; - } - - public void setPath(String path) { - this.path = path; - } - - public LocalDateTime getLastModified() { - return lastModified; - } - - public void setLastModified(LocalDateTime lastModified) { - this.lastModified = lastModified; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - CustomDashboardInfo that = (CustomDashboardInfo) o; - - if (name != null ? !name.equals(that.name) : that.name != null) { - return false; - } - if (path != null ? !path.equals(that.path) : that.path != null) { - return false; - } - return lastModified != null ? lastModified.equals(that.lastModified) : that.lastModified == null; - } - - @Override - public int hashCode() { - int result = name != null ? name.hashCode() : 0; - result = 31 * result + (path != null ? path.hashCode() : 0); - result = 31 * result + (lastModified != null ? lastModified.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "CustomDashboardInfo{" + - "name='" + name + '\'' + - ", path='" + path + '\'' + - ", lastModified=" + lastModified + - '}'; - } - -} diff --git a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/runtime/config/DevUIStaticArtifactsRecorder.java b/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/runtime/config/DevUIStaticArtifactsRecorder.java deleted file mode 100644 index c564e0a0ac..0000000000 --- a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/java/org/kie/kogito/swf/tools/runtime/config/DevUIStaticArtifactsRecorder.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.kie.kogito.swf.tools.runtime.config; - -import java.util.ArrayList; -import java.util.List; - -import io.quarkus.runtime.ShutdownContext; -import io.quarkus.runtime.annotations.Recorder; -import io.quarkus.vertx.http.runtime.devmode.FileSystemStaticHandler; -import io.vertx.core.Handler; -import io.vertx.ext.web.RoutingContext; - -@Recorder -public class DevUIStaticArtifactsRecorder { - public Handler handler(String deploymentArtifactPath, ShutdownContext shutdownContext) { - List webRootConfigurations = new ArrayList<>(); - webRootConfigurations.add( - new FileSystemStaticHandler.StaticWebRootConfiguration(deploymentArtifactPath, "")); - - FileSystemStaticHandler fileSystemStaticHandler = new FileSystemStaticHandler(webRootConfigurations); - - shutdownContext.addShutdownTask(new ShutdownContext.CloseRunnable(fileSystemStaticHandler)); - - return fileSystemStaticHandler; - } -} diff --git a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/resources/META-INF/beans.xml b/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/resources/META-INF/beans.xml deleted file mode 100644 index a0eb9fbf8c..0000000000 --- a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/resources/META-INF/beans.xml +++ /dev/null @@ -1,20 +0,0 @@ - diff --git a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/resources/META-INF/quarkus-extension.yaml b/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/resources/META-INF/quarkus-extension.yaml deleted file mode 100644 index 6b857e4b94..0000000000 --- a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/main/resources/META-INF/quarkus-extension.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - - -name: SonataFlow Runtime Tools Quarkus Dev UI -description: Enables the SonataFlow Runtime tools in Quarkus Dev UI -metadata: - keywords: - - "kogito" - - "workflows" - - "dev-ui" - guide: "https://quarkus.io/guides/kogito" - categories: - - "business-automation" - status: "preview" \ No newline at end of file diff --git a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/test/java/org/kie/kogito/swf/tools/custom/dashboard/impl/CustomDashboardStorageTest.java b/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/test/java/org/kie/kogito/swf/tools/custom/dashboard/impl/CustomDashboardStorageTest.java deleted file mode 100644 index 88a398014a..0000000000 --- a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/test/java/org/kie/kogito/swf/tools/custom/dashboard/impl/CustomDashboardStorageTest.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.swf.tools.custom.dashboard.impl; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.TimeUnit; - -import org.apache.commons.io.FileUtils; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.kie.kogito.swf.tools.custom.dashboard.CustomDashboardStorage; -import org.kie.kogito.swf.tools.custom.dashboard.model.CustomDashboardFilter; -import org.kie.kogito.swf.tools.custom.dashboard.model.CustomDashboardInfo; - -import static org.awaitility.Awaitility.await; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -public class CustomDashboardStorageTest { - - private static String[] DASHBOARD_NAMES = { "age.dash.yml", "products.dash.yaml" }; - private static String DASHBOARD_NAME = "age.dash.yml"; - - private CustomDashboardStorage customDashboardStorage; - private URL tempFolder; - - @BeforeAll - public void init() { - tempFolder = Thread.currentThread().getContextClassLoader().getResource("custom/dashboards/"); - - customDashboardStorage = new CustomDashboardStorageImpl(tempFolder); - } - - @Test - public void testGetFormInfoList() { - Collection customDashboardInfoFilterAll = customDashboardStorage.getCustomDashboardFiles(null); - assertEquals(2, customDashboardInfoFilterAll.size()); - - CustomDashboardFilter filterEmpty = new CustomDashboardFilter(); - filterEmpty.setNames(Collections.emptyList()); - Collection customDashboardInfoAllEmptyFilter = customDashboardStorage.getCustomDashboardFiles(filterEmpty); - assertEquals(2, customDashboardInfoAllEmptyFilter.size()); - - CustomDashboardFilter filter = new CustomDashboardFilter(); - filter.setNames(Arrays.asList(DASHBOARD_NAMES)); - - Collection formInfos = customDashboardStorage.getCustomDashboardFiles(filter); - assertEquals(2, formInfos.size()); - } - - @Test - public void testHotReloading() throws IOException { - String storageUrl = Thread.currentThread().getContextClassLoader().getResource("custom/dashboards/").getFile(); - File srcFile = new File(storageUrl + "products.dash.yaml"); - File targetFile = new File(storageUrl + "copy.dash.yml"); - - assertEquals(false, targetFile.exists()); - FileUtils.copyFile(srcFile, targetFile); - assertEquals(true, targetFile.exists()); - await().atMost(20, TimeUnit.SECONDS).until(() -> testBeforeDelete()); - Collection customDashboardInfoFilterAllBeforeDelete = customDashboardStorage.getCustomDashboardFiles(null); - assertEquals(3, customDashboardInfoFilterAllBeforeDelete.size()); - - assertEquals(true, targetFile.exists()); - FileUtils.delete(targetFile); - assertEquals(false, targetFile.exists()); - await().atMost(20, TimeUnit.SECONDS).until(() -> testAfterDelete()); - - Collection customDashboardInfoFilterAllAfterDelete = customDashboardStorage.getCustomDashboardFiles(null); - assertEquals(2, customDashboardInfoFilterAllAfterDelete.size()); - } - - private boolean testBeforeDelete() { - if (customDashboardStorage.getCustomDashboardFiles(null).size() == 3) { - return true; - } - return false; - } - - private boolean testAfterDelete() { - if (customDashboardStorage.getCustomDashboardFiles(null).size() == 2) { - return true; - } - return false; - } - - @Test - public void testGetFormContent() throws IOException { - String content = customDashboardStorage.getCustomDashboardFileContent(DASHBOARD_NAME); - assertNotNull(content); - } -} diff --git a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/test/java/org/kie/kogito/swf/tools/custom/dashboard/impl/CustomDashboardStorageTestProfile.java b/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/test/java/org/kie/kogito/swf/tools/custom/dashboard/impl/CustomDashboardStorageTestProfile.java deleted file mode 100644 index a901f73229..0000000000 --- a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/test/java/org/kie/kogito/swf/tools/custom/dashboard/impl/CustomDashboardStorageTestProfile.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.swf.tools.custom.dashboard.impl; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.util.Collections; -import java.util.Map; - -import io.quarkus.test.junit.QuarkusTestProfile; - -import static org.kie.kogito.swf.tools.custom.dashboard.impl.CustomDashboardStorageImpl.PROJECT_CUSTOM_DASHBOARD_STORAGE_PROP; - -public class CustomDashboardStorageTestProfile implements QuarkusTestProfile { - - private String storagePath; - - public CustomDashboardStorageTestProfile() throws IOException { - File storage = Files.createTempDirectory("CustomDashboardStorageTestProfile").toFile(); - storage.deleteOnExit(); - storage.mkdir(); - storagePath = storage.getAbsolutePath(); - } - - @Override - public Map getConfigOverrides() { - return Collections.singletonMap(PROJECT_CUSTOM_DASHBOARD_STORAGE_PROP, storagePath); - } -} diff --git a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/test/resources/custom/dashboards/products.dash.yaml b/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/test/resources/custom/dashboards/products.dash.yaml deleted file mode 100644 index ea685a0b5c..0000000000 --- a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/test/resources/custom/dashboards/products.dash.yaml +++ /dev/null @@ -1,61 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -datasets: - - uuid: products - content: >- - [ - ["Computers", "Scanner", 5, 3], - ["Computers", "Printer", 7, 4], - ["Computers", "Laptop", 3, 2], - ["Electronics", "Camera", 10, 7], - ["Electronics", "Headphones", 5, 9] - ] - columns: - - id: Section - type: LABEL - - id: Product - type: LABEL - - id: Quantity - type: NUMBER - - id: Quantity2 - type: NUMBER -pages: - - components: - - html: Welcome to Dashbuilder! - properties: - font-size: xx-large - margin-bottom: 30px - - settings: - type: BARCHART - dataSetLookup: - uuid: products - group: - - columnGroup: - source: Product - groupFunctions: - - source: Product - - source: Quantity - function: SUM - - source: Quantity2 - function: SUM - - settings: - type: TABLE - dataSetLookup: - uuid: products diff --git a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/test/resources/custom/dashboards/subdir/age.dash.yml b/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/test/resources/custom/dashboards/subdir/age.dash.yml deleted file mode 100644 index 011aff8684..0000000000 --- a/kogito-quarkus-serverless-workflow-devui-parent/kogito-quarkus-serverless-workflow-devui/src/test/resources/custom/dashboards/subdir/age.dash.yml +++ /dev/null @@ -1,44 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -datasets: - - uuid: age - content: >- - [ - ["John", 5], - ["Mary", 7], - ["Mark", 3] - ] - columns: - - id: Name - type: LABEL - - id: Age - type: Number -pages: - - components: - - settings: - type: BARCHART - dataSetLookup: - uuid: age - group: - - columnGroup: - source: Name - groupFunctions: - - source: Name - - source: Age diff --git a/kogito-quarkus-serverless-workflow-devui-parent/pom.xml b/kogito-quarkus-serverless-workflow-devui-parent/pom.xml deleted file mode 100644 index 3a21f04af8..0000000000 --- a/kogito-quarkus-serverless-workflow-devui-parent/pom.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - - - org.kie.kogito - kogito-apps-build-parent - 999-SNAPSHOT - ../kogito-apps-build-parent/pom.xml - - 4.0.0 - - Kogito Apps :: SonataFlow Quarkus Dev UI Extension - org.apache.kie.sonataflow - sonataflow-quarkus-devui-parent - pom - - - org.kie.kogito.quarkus.swf.dev.ui - - - - kogito-quarkus-serverless-workflow-devui-deployment - kogito-quarkus-serverless-workflow-devui - - - - - - - io.quarkus - quarkus-maven-plugin - - - maven-surefire-plugin - - - org.jboss.logmanager.LogManager - - - - - maven-failsafe-plugin - - - org.jboss.logmanager.LogManager - - - - - maven-compiler-plugin - - - - - diff --git a/management-console/.dockerignore b/management-console/.dockerignore deleted file mode 100644 index b86c7ac340..0000000000 --- a/management-console/.dockerignore +++ /dev/null @@ -1,4 +0,0 @@ -* -!target/*-runner -!target/*-runner.jar -!target/lib/* \ No newline at end of file diff --git a/management-console/README.md b/management-console/README.md deleted file mode 100644 index ecaf7d7ab5..0000000000 --- a/management-console/README.md +++ /dev/null @@ -1,246 +0,0 @@ -# management-console project - -This project uses Quarkus, the Supersonic Subatomic Java Framework. - -If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ . - -## Running the application in dev mode - -You can run your application in dev mode that enables live coding using: -``` -mvn quarkus:dev -``` -Note: Live coding of the React JS frontend application is not yet in place. - -## Packaging and running the application - -The application is packageable using: -``` -mvn package -``` -It produces the executable `management-console-8.0.0-SNAPSHOT-runner.jar` file in `/target` directory. -Be aware that it’s not an _über-jar_ as the dependencies are copied into the `target/lib` directory. - -The application is now runnable using: -``` -java -jar target/management-console-8.0.0-SNAPSHOT-runner.jar -``` - -> Note: management-console requires keycloak server to be initialied for authentication. - -## Creating a native executable - -You can create a native executable using: -``` -mvn package -Dnative -``` - -Or you can use Docker to build the native executable using: -``` -mvn package -Dnative -Dquarkus.native.container-build=true -``` - -You can then execute your binary: `./target/management-console-8.0.0-SNAPSHOT-runner` - -If you want to learn more about building native executables, please consult https://quarkus.io/guides/building-native-image-guide . - -## Packaging together with the React app - -The application makes use of a separately developed [React UI application](../ui-packages/packages/management-console-webapp/package.json). The JS based frontend can be built as part of the build of this project by using the profile defined in dependency [ui-packages](../ui-packages/pom.xml), invoked by default. Using the property `-Dskip.ui.build` as in following command you can skip the build of UI and use what is already built in the respective package: -``` -mvn package -Dskip.ui.build -``` - -To prepare all the dependencies needed for the build of UI, there's a maven profile activated by default. Using the `-Dskip.ui.deps` property you can skip the profile. - - -The single command to disable both UI build related profiles is: -``` -mvn package -Dskip.ui.deps -Dskip.ui.build -``` - -## Creating a native executable -The native build of the application bundling in the React JS frontend does not differ from the instructions above. The only thing that's new is again the invocation of UI specific profiles. -``` -mvn package -Dui -Dnative -``` - -## Working with management-console features - -### Process instances -The process instances page shows the list of process instances available. The list is an expandable list , which shows all the child process instance of the particular process instance when expanded. Each row in the list contains the information about the process instances. The info includes details like *name*, *status*, *endpoint* etc... The list also provides many ways to [filter](#filters) out the process instances and also to perform [process-management](#process-management) operations to the processes. The details of them are mentioned below. - -#### Filters - -![Filters](./docs/filters.png?raw=true "Filters") - -It has two ways of filtering the list: -* filter by status (a checkbox based filter) -* search by Business key (a textbox based filter) -These two filters can be used in combination to filter the list. - -##### a) Filter by status : -![Status](./docs/status.png?raw=true "Status") - - - -There are five status in total : - * Active - * Completed - * Aborted - * Suspended - * Error - -Initially the process instance list loads all the active instances available.To search using different filters, click on the dropdown with the **Status** tag and select the necessary status from the drowdown. Click on **Apply filter** to load the new list.Also there is a **chip array** below the filters to show the current filters applied. The filters for status can be removed by either by deselecting the options from the dropdown and clicking on **Apply filter** or by clicking on the '**X**' in the chip. - -##### b) Filter by business key : - ![Businesskeysearch](./docs/businesskeysearch.gif "Businessekeysearch") - - The business key is a business relevant identifier that might or might not be present in the process instance. The business key, if available would appear as a **blue coloured badge** near the process name in the list. We can enter the business key or parts of business key in the textbox and either click **Apply filter** or press **Enter** key to apply the filter. Similar to the Status filter, the chips with the typed keywords would appear below the textbox. The search for business key works on a *OR* based condition. Also to remove a filter based on the keyword, click on the '**X**' in the chip to remove and the list will reload to show the applied filter. The search supports [Wild cards](https://en.wikipedia.org/wiki/Wildcard_character "Wild cards") in its keywords. For example, consider a process having a business key as *WIOO2E*. This business key can be searched by entering *W\** or *\*OO\** or *WIOO2E*. - -#### Bulk Process Actions -![Multiselect](./docs/multiselect.gif "Multiselect") - - The multi select is used to select multiple process instances while performing bulk [process-management](#process-management) actions.The multi select checkbox by default selects all the parent and child process instances(if loaded). It also has drop-down actions to select none (removes all selections), select only parent process instances, select all the process instances (both parent and child). The multi-select works in correlation with the set of buttons present as a part of the toolbar. The buttons present are - **Abort selected**, **Retry selected** and **Skip selected**. This is a part of the [bulk operations](#bulk-operations) performed in the list. - - ![Bulkoperations](./docs/bulkoperations.gif "Bulkoperations") - - The process instance list allows the user to select multiple process instances and perform bulk process management operations.It consist of *Abort selected*, *Retry selected* and *Skip selected*.The user can select individual process instances by selecting the checkbox present in the list or use the [multi select checkbox](#multi-select-checkbox) to select the process instances. - - - Clicking on the *Abort selected* will open a box to show the instances being aborted and the instances being skipped(An instance which is already in *Completed* or *Aborted* cannot be Aborted again, hence the instances are skipped). - - Clicking on the *Retry selected* or *Skip selected* will open a box to show the instances being retriggered or skipped respectively. These actions can be performed on instances which are in *Error* state only. Other instances(in different states), if selected will appear under the skipped list. - - For all the bulk actions, if any of the instance goes throws an error while execution, they appear under the error list of the box. - -In addition to these , there is a **reload** button(a sync-icon), to reload the list and a **Reset to default** button which resets all the filters to its original state(Active status and no business key filter). - -> **Note :** all the filters are applied only to the parent process instances. - -#### Process list - -![Processlist](./docs/processlist.png?raw=true "Processlist") - - The List shows details of the process instances. Initially it loads only the parent process instances. The following are the details shows in the list : - * Checkbox - * Process name - * State of the process - * when was the process created - * when was the process updated - * a kebab button to provide [process-management](#process-management) functions - - The list has a toggle button on the left end, which when toggled would load all the child process instances of that parent process instance. - The child process instance also has a similar sturcture as mentioned above. - - **Checkbox** - A checkbox to select the process instance to perform [process-management](#process-management) operations. Checkboxes which are disabled either do not have `kogito.service.url` missing or the `process management` capability is disabled. - - **Process name** - It shows the process name of the instance along with the business key (as a blue badge) if available. If business key is absent, then it shows - the spliced process instance id(spliced to 5 chars). When hovered over the process name ,a tooltip with the full process instance id pops up. - - **Status** - The process status along with their corresponding icons is shown. For instances in **Error**, a pop over shows up with the corresponding error message. Also there is a provision to either Skip or Retry the error instance. - - **Process** **Created** - This shows the time elapsed from the process creation. - - **Last** **Updated** - This shows the time elapsed from the last update. - - **Kebab** **button** - The kebab button is enabled or disabled based on the process instance status and the contents of the kebab button varies based on the process-management capability provided to the particular instance. It consists of Abort, Skip and Retry. - -#### Process management - - There are currently three process management capabilities in the process instance list. - * Abort - * Skip - * Retry - - ##### Abort : - An instance which is either in *Active*, *Error*, *Suspended* state can be aborted. Clicking on the abort would open up a box displaying the instance which was aborted. - #### Skip : - A node in *Error* state can be skipped. Clicking on the skip would open up a box displaying the success or failure status of the skip operation. - #### Retry : - A node in *Error* state can be Retriggered. Clicking on the Retry would open up a box displaying the success or failure status of the Retry operation. - - ### Process instance Details - - ![Processdetails](./docs/processdetails.png?raw=true "Processdetails") - - The process details page consist of the following : - - #### Heading - The heading shows the name of the process instance. The heading also shows the business key(if available) as a blue coloured badge. - In the absence of business key it shows the process instance id as a concatenated string. - - #### Process management buttons - The process management buttons consist of the following - * *Abort* - Abort button aborts the process instance. Clicking on abort button results in opening a checkbox to show the process instance which was aborted. - - #### Details - The details consist of the following : - - * *Name* - shows the name of the travels - * *BusinessKey* - shows the business key if available - * *State* - shows the current state of the process instance - * *Id* - shows the full unique id of the process instance - * *Endpoint* - Shows the endpoint of the backend to which the client is connected - * *Start* - shows the time elapased from the start of the process - * *End* - shows the time elapased from the end of the process(if available) - * *Parent process* - shows if the current process instance has a parent process. Allows navigation to the parent process when clicked on. Shows the unique id when hovered over. - * *Sub processes* - shows if the current process instance has sub processes.Allows navigation to the respective child process when clicked on. Shows the unique id when hovered over - - #### Timeline - The timeline shows the timeline of all the nodes of a process.It shows the state of the node(*Active* or *Completed* or *Error*), the name of - the node(*User Icon* on the side for user tasks ) and a kebab button. The nodes in *error* are shown(hovering over the error icon would sho the error message) can be *skipped* or *retriggered* by selection the required option from the kebab toggle. - - #### Process variables - The process variables shows the domain data in *JSON* format. - - ### Process Diagram - The process diagram panel contains the BPMN process diagram, which the users you to view the current progress of the process. - - ![Process Diagram](./docs/processdiagram.png?raw=true "ProcessDiagram") - - ### Jobs Panel - The Jobs panel shows the list of jobs(timer) and we can execute operations on the jobs using this panel. The available operations are - *View details*, *Reschedule* and *Cancel*. - - ![Jobs Panel](./docs/jobspanel.png?raw=true "JobsPanel") - - ### Node Trigger Panel - The nodes of a process can be tirggered manually using this panel. It consist of a dropdown, which shows the list of triggerable nodes. Once the required node is selected, click on *Trigger* button to trigger the node. - - ![Node Trigger](./docs/nodetrigger.png?raw=true "NodeTrigger") - - ### Milestones Panel - The milestones panel show the list of milestones present and their current states. - - ![Milestones](./docs/milestones.png?raw=true "Milestones") - -## Enabling Keycloak security - -### Starting and Configuring the Keycloak Server - -To start a Keycloak Server you can use Docker and just run the following command: - -``` -docker run -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin -e KEYCLOAK_IMPORT=/tmp/kogito-realm.json -v {absolute_path}/kogito-apps/config/kogito-realm.json:/tmp/kogito-realm.json -p 8280:8080 jboss/keycloak -``` - -You should be able to access your Keycloak Server at [localhost:8280/auth](http://localhost:8280) -and verify keycloak server is running properly: log in as the admin user to access the Keycloak Administration Console. -Username should be admin and password admin. - -The following are the users available in keycloak - -| Login | Password | Roles | -| ------------- | ---------- | ------------------- | -| admin | admin | *admin*, *managers* | -| alice | alice | *user* | -| jdoe | jdoe | *managers* | - -To change any of this client configuration access to http://localhost:8280/auth/admin/master/console/#/realms/kogito. - -### Starting Kogito Management Console in dev mode - -Start the management console at port 8380, (the keycloak client 'kogito-console-quarkus' is configured to use that port ) -and enabling auth: - -``` -mvn clean compile quarkus:dev -Dquarkus.http.port=8380 -``` diff --git a/management-console/docs/DELandingpage.png b/management-console/docs/DELandingpage.png deleted file mode 100644 index d90ed49321..0000000000 Binary files a/management-console/docs/DELandingpage.png and /dev/null differ diff --git a/management-console/docs/bulkoperations.gif b/management-console/docs/bulkoperations.gif deleted file mode 100644 index e346e3ed89..0000000000 Binary files a/management-console/docs/bulkoperations.gif and /dev/null differ diff --git a/management-console/docs/businesskeysearch.gif b/management-console/docs/businesskeysearch.gif deleted file mode 100755 index d11f281b47..0000000000 Binary files a/management-console/docs/businesskeysearch.gif and /dev/null differ diff --git a/management-console/docs/expandRow.gif b/management-console/docs/expandRow.gif deleted file mode 100644 index ed82f079ca..0000000000 Binary files a/management-console/docs/expandRow.gif and /dev/null differ diff --git a/management-console/docs/filters.png b/management-console/docs/filters.png deleted file mode 100644 index 42c0e78790..0000000000 Binary files a/management-console/docs/filters.png and /dev/null differ diff --git a/management-console/docs/jobspanel.png b/management-console/docs/jobspanel.png deleted file mode 100644 index 1cc21c2b78..0000000000 Binary files a/management-console/docs/jobspanel.png and /dev/null differ diff --git a/management-console/docs/milestones.png b/management-console/docs/milestones.png deleted file mode 100644 index 2ab97c557c..0000000000 Binary files a/management-console/docs/milestones.png and /dev/null differ diff --git a/management-console/docs/multiselect.gif b/management-console/docs/multiselect.gif deleted file mode 100644 index 75aad1b1b8..0000000000 Binary files a/management-console/docs/multiselect.gif and /dev/null differ diff --git a/management-console/docs/navigate.gif b/management-console/docs/navigate.gif deleted file mode 100644 index 0e3bfdd394..0000000000 Binary files a/management-console/docs/navigate.gif and /dev/null differ diff --git a/management-console/docs/nodetrigger.png b/management-console/docs/nodetrigger.png deleted file mode 100644 index a8930236a4..0000000000 Binary files a/management-console/docs/nodetrigger.png and /dev/null differ diff --git a/management-console/docs/pickColumns.gif b/management-console/docs/pickColumns.gif deleted file mode 100644 index 78518bfc00..0000000000 Binary files a/management-console/docs/pickColumns.gif and /dev/null differ diff --git a/management-console/docs/processdetails.png b/management-console/docs/processdetails.png deleted file mode 100644 index f3d07f0bab..0000000000 Binary files a/management-console/docs/processdetails.png and /dev/null differ diff --git a/management-console/docs/processdiagram.png b/management-console/docs/processdiagram.png deleted file mode 100644 index ddbd3757a8..0000000000 Binary files a/management-console/docs/processdiagram.png and /dev/null differ diff --git a/management-console/docs/processlist.png b/management-console/docs/processlist.png deleted file mode 100644 index d6b67fa388..0000000000 Binary files a/management-console/docs/processlist.png and /dev/null differ diff --git a/management-console/docs/status.png b/management-console/docs/status.png deleted file mode 100644 index 0938efc428..0000000000 Binary files a/management-console/docs/status.png and /dev/null differ diff --git a/management-console/docs/testusersystem-add-user.png b/management-console/docs/testusersystem-add-user.png deleted file mode 100644 index ba9f883afe..0000000000 Binary files a/management-console/docs/testusersystem-add-user.png and /dev/null differ diff --git a/management-console/docs/testusersystem-menu-add.png b/management-console/docs/testusersystem-menu-add.png deleted file mode 100644 index 967990a740..0000000000 Binary files a/management-console/docs/testusersystem-menu-add.png and /dev/null differ diff --git a/management-console/docs/testusersystem-menu.png b/management-console/docs/testusersystem-menu.png deleted file mode 100644 index f4a70b1cfd..0000000000 Binary files a/management-console/docs/testusersystem-menu.png and /dev/null differ diff --git a/management-console/pom.xml b/management-console/pom.xml deleted file mode 100644 index 2634c3eb2f..0000000000 --- a/management-console/pom.xml +++ /dev/null @@ -1,154 +0,0 @@ - - - - - - org.kie.kogito - kogito-apps-build-parent - 999-SNAPSHOT - ../kogito-apps-build-parent/pom.xml - - 4.0.0 - - management-console - Kogito Apps :: Management Console - - - org.kie.kogito.mgmt - ../ui-packages/packages/management-console-webapp - - - - - org.kie - kie-addons-quarkus-process-svg - - - io.quarkus - quarkus-reactive-routes - - - io.quarkus - quarkus-resteasy - - - io.quarkus - quarkus-qute - - - - - io.quarkus - quarkus-smallrye-health - - - - io.quarkus - quarkus-junit5 - test - - - io.rest-assured - rest-assured - test - - - org.kie.kogito - kogito-apps-ui-packages - pom - - - - - - io.quarkus - quarkus-maven-plugin - - true - - - - - build - - - - - - - maven-resources-plugin - - - copy-resources - process-resources - - copy-resources - - - ${basedir}/target/classes/META-INF/resources/ - - - ${path.to.frontend.app}/dist - - index.html - - false - - - - - - - - org.apache.maven.plugins - maven-assembly-plugin - - - src/assembly/image-build-zip.xml - - - - - package - - single - - - - - - - - - native - - - native - - - - native - - - - \ No newline at end of file diff --git a/management-console/src/assembly/image-build-zip.xml b/management-console/src/assembly/image-build-zip.xml deleted file mode 100644 index 2592d48e8a..0000000000 --- a/management-console/src/assembly/image-build-zip.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - image-build - - zip - - false - - - target/quarkus-app - - - - \ No newline at end of file diff --git a/management-console/src/main/docker/Dockerfile.jvm b/management-console/src/main/docker/Dockerfile.jvm deleted file mode 100644 index e823153020..0000000000 --- a/management-console/src/main/docker/Dockerfile.jvm +++ /dev/null @@ -1,47 +0,0 @@ -#### -# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode -# -# Before building the docker image run: -# -# mvn package -# -# Then, build the image with: -# -# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/management-console-jvm . -# -# Then run the container using: -# -# docker run -i --rm -p 8080:8080 quarkus/management-console-jvm -# -### -FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1 - -ARG JAVA_PACKAGE=java-1.8.0-openjdk-headless -ARG RUN_JAVA_VERSION=1.3.5 - -ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' - -# Install java and the run-java script -# Also set up permissions for user `1001` -RUN microdnf install openssl curl ca-certificates ${JAVA_PACKAGE} \ - && microdnf update \ - && microdnf clean all \ - && mkdir /deployments \ - && chown 1001 /deployments \ - && chmod "g+rwX" /deployments \ - && chown 1001:root /deployments \ - && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \ - && chown 1001 /deployments/run-java.sh \ - && chmod 540 /deployments/run-java.sh \ - && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security - -# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size. -ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" - -COPY target/lib/* /deployments/lib/ -COPY target/*-runner.jar /deployments/app.jar - -EXPOSE 8080 -USER 1001 - -ENTRYPOINT [ "/deployments/run-java.sh" ] \ No newline at end of file diff --git a/management-console/src/main/docker/Dockerfile.native b/management-console/src/main/docker/Dockerfile.native deleted file mode 100644 index 423e650f2a..0000000000 --- a/management-console/src/main/docker/Dockerfile.native +++ /dev/null @@ -1,30 +0,0 @@ -#### -# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode -# -# Before building the docker image run: -# -# mvn package -Pnative -Dquarkus.native.container-build=true -# -# Then, build the image with: -# -# docker build -f src/main/docker/Dockerfile.native -t quarkus/management-console . -# -# Then run the container using: -# -# docker run -i --rm -p 8080:8080 quarkus/management-console -# -### -FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1 -WORKDIR /work/ -COPY target/*-runner /work/application - -# set up permissions for user `1001` -RUN chmod 775 /work /work/application \ - && chown -R 1001 /work \ - && chmod -R "g+rwX" /work \ - && chown -R 1001:root /work - -EXPOSE 8080 -USER 1001 - -CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] \ No newline at end of file diff --git a/management-console/src/main/java/org/kie/kogito/mgmt/VertxRouter.java b/management-console/src/main/java/org/kie/kogito/mgmt/VertxRouter.java deleted file mode 100644 index 80c761841c..0000000000 --- a/management-console/src/main/java/org/kie/kogito/mgmt/VertxRouter.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.mgmt; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.quarkus.qute.Location; -import io.quarkus.qute.Template; -import io.vertx.core.http.HttpHeaders; -import io.vertx.ext.web.Router; -import io.vertx.ext.web.RoutingContext; -import io.vertx.ext.web.handler.StaticHandler; - -import jakarta.annotation.PostConstruct; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.event.Observes; - -@ApplicationScoped -public class VertxRouter { - - private static final Logger LOGGER = LoggerFactory.getLogger(VertxRouter.class); - - @Location("index") - Template indexTemplate; - - private String index; - - @PostConstruct - public void init() { - index = indexTemplate.render(); - } - - void setupRouter(@Observes Router router) { - router.route("/").handler(ctx -> ctx.response().putHeader("location", "/ProcessInstances/").setStatusCode(302).end()); - router.route("/Process*").handler(this::handle); - router.route("/DomainExplorer*").handler(this::handle); - router.route("/JobsManagement*").handler(this::handle); - router.route().handler(StaticHandler.create()); - } - - public void handle(RoutingContext context) { - try { - context.response() - .putHeader(HttpHeaders.CACHE_CONTROL, "no-cache") - .putHeader(HttpHeaders.CONTENT_TYPE, "text/html;charset=utf8") - .end(index); - } catch (Exception ex) { - LOGGER.error("Error handling index.html", ex); - context.fail(500, ex); - } - } -} diff --git a/management-console/src/main/resources/application.properties b/management-console/src/main/resources/application.properties deleted file mode 100644 index 17cdb19e50..0000000000 --- a/management-console/src/main/resources/application.properties +++ /dev/null @@ -1,28 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -quarkus.http.cors=true -kogito.dataindex.http.url=http://localhost:8180/graphql -#keycloak client config -kogito.consoles.keycloak.config.realm=kogito -kogito.consoles.keycloak.config.url=http://localhost:8280/auth/ -kogito.consoles.keycloak.config.client-id=kogito-console-quarkus -kogito.consoles.keycloak.config.health-check-url=http://localhost:8280/auth/realms/kogito/.well-known/openid-configuration -kogito.consoles.keycloak.config.disable-health-check=false -kogito.consoles.keycloak.config.update-token-validity=30 \ No newline at end of file diff --git a/management-console/src/main/resources/templates/index.html b/management-console/src/main/resources/templates/index.html deleted file mode 100755 index 94b0e6bc19..0000000000 --- a/management-console/src/main/resources/templates/index.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - Kogito - Management Console - - - - - - - -
- - - diff --git a/management-console/src/test/java/org/kie/kogito/mgmt/NativeStaticContentTestIT.java b/management-console/src/test/java/org/kie/kogito/mgmt/NativeStaticContentTestIT.java deleted file mode 100644 index d6a4fc4df8..0000000000 --- a/management-console/src/test/java/org/kie/kogito/mgmt/NativeStaticContentTestIT.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.mgmt; - -import io.quarkus.test.junit.QuarkusIntegrationTest; - -@QuarkusIntegrationTest -public class NativeStaticContentTestIT extends StaticContentIT { - - // Execute the same tests but in native mode. -} \ No newline at end of file diff --git a/management-console/src/test/java/org/kie/kogito/mgmt/StaticContentIT.java b/management-console/src/test/java/org/kie/kogito/mgmt/StaticContentIT.java deleted file mode 100644 index d8aed0c8e3..0000000000 --- a/management-console/src/test/java/org/kie/kogito/mgmt/StaticContentIT.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.mgmt; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.nio.charset.StandardCharsets; - -import org.junit.jupiter.api.Test; - -import io.quarkus.test.common.http.TestHTTPResource; -import io.quarkus.test.junit.QuarkusTest; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -@QuarkusTest -public class StaticContentIT { - - @TestHTTPResource - URL url; - - private static String readStream(InputStream in) throws IOException { - byte[] data = new byte[1024]; - int r; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - while ((r = in.read(data)) > 0) { - out.write(data, 0, r); - } - return new String(out.toByteArray(), StandardCharsets.UTF_8); - } - - @Test - public void testIndexHtml() throws Exception { - try (InputStream in = url.openStream()) { - String contents = readStream(in); - assertTrue(contents.contains("Kogito - Management Console")); - } - } -} diff --git a/management-console/src/test/java/org/kie/kogito/mgmt/VertxRouterIT.java b/management-console/src/test/java/org/kie/kogito/mgmt/VertxRouterIT.java deleted file mode 100644 index b2198382db..0000000000 --- a/management-console/src/test/java/org/kie/kogito/mgmt/VertxRouterIT.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.mgmt; - -import org.junit.jupiter.api.Test; - -import io.quarkus.test.junit.QuarkusTest; - -import static io.restassured.RestAssured.given; - -@QuarkusTest -public class VertxRouterIT { - - @Test - public void testHandlePath() { - given().when().get("/ProcessInstances") - .then() - .statusCode(200); - - given().when().get("/Process/a1e139d5-4e77-48c9-84ae-34578e904e5a") - .then() - .statusCode(200); - - given().when().get("/DomainExplorer") - .then() - .statusCode(200); - - given().when().get("/DomainExplorer/travels") - .then() - .statusCode(200); - - given().when().get("/JobsManagement") - .then() - .statusCode(200); - - given().when().get("/Another") - .then() - .statusCode(404); - } -} diff --git a/pom.xml b/pom.xml index 12ce381108..1382b62669 100644 --- a/pom.xml +++ b/pom.xml @@ -82,17 +82,11 @@ jobs-service data-index data-audit - ui-packages security-commons - management-console - trusty-ui - task-console explainability trusty jitexecutor apps-integration-tests - runtime-tools-quarkus-extension-parent - kogito-quarkus-serverless-workflow-devui-parent diff --git a/runtime-tools-quarkus-extension-parent/pom.xml b/runtime-tools-quarkus-extension-parent/pom.xml deleted file mode 100644 index 96b7ee8362..0000000000 --- a/runtime-tools-quarkus-extension-parent/pom.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - - - org.kie.kogito - kogito-apps-build-parent - 999-SNAPSHOT - ../kogito-apps-build-parent/pom.xml - - 4.0.0 - - jBPM Quarkus DevUI - Parent - org.jbpm - jbpm-quarkus-devui-parent - pom - - - org.kie.kogito.quarkus.runtime.tools.dev.ui - - - - runtime-tools-quarkus-extension-deployment - runtime-tools-quarkus-extension - - - - - - - io.quarkus - quarkus-maven-plugin - - - maven-surefire-plugin - - - org.jboss.logmanager.LogManager - - - - - maven-failsafe-plugin - - - org.jboss.logmanager.LogManager - - - - - maven-compiler-plugin - - - - - diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension-deployment/pom.xml b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension-deployment/pom.xml deleted file mode 100644 index a5525fec62..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension-deployment/pom.xml +++ /dev/null @@ -1,173 +0,0 @@ - - - - 4.0.0 - - - org.jbpm - jbpm-quarkus-devui-parent - 999-SNAPSHOT - - - jbpm-quarkus-devui-deployment - jBPM Quarkus DevUI - Deployment - - - ../../ui-packages/packages/runtime-tools-dev-ui-webapp - - - - - - io.quarkus - quarkus-development-mode-spi - - - io.quarkus - quarkus-core-deployment - - - io.quarkus - quarkus-arc-deployment - - - io.quarkus - quarkus-rest-client-deployment - - - io.quarkus - quarkus-resteasy-deployment - - - io.quarkus - quarkus-resteasy-jackson-deployment - - - io.quarkus - quarkus-rest-client-deployment - - - io.quarkus - quarkus-resteasy-multipart-deployment - - - org.kie.kogito - kogito-quarkus-extension-spi - - - - - io.quarkus - quarkus-undertow-deployment - - - - - io.rest-assured - rest-assured - test - - - org.mockito - mockito-core - test - - - org.jbpm - jbpm-quarkus-devui - ${project.version} - - - io.quarkus - quarkus-junit5-internal - test - - - org.kie.kogito - kogito-apps-ui-packages - pom - - - - - - - maven-compiler-plugin - - - - io.quarkus - quarkus-extension-processor - ${version.io.quarkus} - - - - - - maven-resources-plugin - - - copy-webapp - process-resources - - copy-resources - - - ${basedir}/target/classes/dev-static/webapp - - - ${path.to.webapp.app}/dist/resources/webapp - false - - - ${path.to.webapp.app}/dist/webapp - false - - - - - - copy-envelope-resources - process-resources - - copy-resources - - - ${basedir}/target/classes/dev-static/ - - - ${path.to.webapp.app}/dist/resources - - form-displayer.html - form-displayer.js - - - - - - - - - - diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension-deployment/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/deployment/DevConsoleProcessor.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension-deployment/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/deployment/DevConsoleProcessor.java deleted file mode 100644 index 803a79379e..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension-deployment/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/deployment/DevConsoleProcessor.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.runtime.tools.quarkus.extension.deployment; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -import org.kie.kogito.quarkus.extensions.spi.deployment.KogitoDataIndexServiceAvailableBuildItem; -import org.kie.kogito.quarkus.extensions.spi.deployment.TrustyServiceAvailableBuildItem; -import org.kie.kogito.runtime.tools.quarkus.extension.deployment.data.UserInfo; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.config.DevConsoleRuntimeConfig; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.config.DevUIStaticArtifactsRecorder; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.rpc.JBPMDevuiJsonRPCService; - -import io.quarkus.deployment.Capabilities; -import io.quarkus.deployment.IsDevelopment; -import io.quarkus.deployment.annotations.BuildProducer; -import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.annotations.ExecutionTime; -import io.quarkus.deployment.annotations.Record; -import io.quarkus.deployment.builditem.*; -import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; -import io.quarkus.deployment.util.WebJarUtil; -import io.quarkus.devui.spi.JsonRPCProvidersBuildItem; -import io.quarkus.devui.spi.page.CardPageBuildItem; -import io.quarkus.devui.spi.page.Page; -import io.quarkus.maven.dependency.ResolvedDependency; -import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; -import io.quarkus.vertx.http.deployment.RouteBuildItem; -import io.quarkus.vertx.http.runtime.management.ManagementInterfaceBuildTimeConfig; - -public class DevConsoleProcessor { - - private static final String STATIC_RESOURCES_PATH = "dev-static/"; - private static final String BASE_RELATIVE_URL = "/q/dev-ui/org.jbpm.jbpm-quarkus-devui"; - private static final String DATA_INDEX_CAPABILITY = "org.kie.kogito.data-index"; - - @SuppressWarnings("unused") - @BuildStep(onlyIf = IsDevelopment.class) - @Record(ExecutionTime.RUNTIME_INIT) - public void deployStaticResources(final DevUIStaticArtifactsRecorder devUIStaticArtifactsRecorder, - final CurateOutcomeBuildItem curateOutcomeBuildItem, - final LiveReloadBuildItem liveReloadBuildItem, - final LaunchModeBuildItem launchMode, - final ShutdownContextBuildItem shutdownContext, - final BuildProducer routeBuildItemBuildProducer) throws IOException { - ResolvedDependency devConsoleResourcesArtifact = WebJarUtil.getAppArtifact(curateOutcomeBuildItem, - "org.jbpm", - "jbpm-quarkus-devui-deployment"); - - Path devConsoleStaticResourcesDeploymentPath = WebJarUtil.copyResourcesForDevOrTest( - liveReloadBuildItem, - curateOutcomeBuildItem, - launchMode, - devConsoleResourcesArtifact, - STATIC_RESOURCES_PATH, - true); - - routeBuildItemBuildProducer.produce(new RouteBuildItem.Builder() - .route(BASE_RELATIVE_URL + "/resources/*") - .handler(devUIStaticArtifactsRecorder.handler(devConsoleStaticResourcesDeploymentPath.toString(), - shutdownContext)) - .build()); - - routeBuildItemBuildProducer.produce(new RouteBuildItem.Builder() - .route(BASE_RELATIVE_URL + "/*") - .handler(devUIStaticArtifactsRecorder.handler(devConsoleStaticResourcesDeploymentPath.toString(), - shutdownContext)) - .build()); - } - - @BuildStep(onlyIf = IsDevelopment.class) - public JsonRPCProvidersBuildItem createJsonRPCServiceForJBPMDevUi() { - return new JsonRPCProvidersBuildItem(JBPMDevuiJsonRPCService.class); - } - - @BuildStep(onlyIf = IsDevelopment.class) - public CardPageBuildItem pages( - final NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem, - final DevConsoleRuntimeConfig devConsoleRuntimeConfig, - final ManagementInterfaceBuildTimeConfig managementInterfaceBuildTimeConfig, - final LaunchModeBuildItem launchModeBuildItem, - final ConfigurationBuildItem configurationBuildItem, - final List systemPropertyBuildItems, - final Optional dataIndexServiceAvailableBuildItem, - final Optional trustyServiceAvailableBuildItem, - final Capabilities capabilities) { - - CardPageBuildItem cardPageBuildItem = new CardPageBuildItem(); - - String uiPath = nonApplicationRootPathBuildItem.resolveManagementPath(BASE_RELATIVE_URL, - managementInterfaceBuildTimeConfig, launchModeBuildItem, true); - - String openapiPath = getProperty(configurationBuildItem, systemPropertyBuildItems, "quarkus.smallrye-openapi.path"); - String devUIUrl = getProperty(configurationBuildItem, systemPropertyBuildItems, "kogito.dev-ui.url"); - String dataIndexUrl = getProperty(configurationBuildItem, systemPropertyBuildItems, "kogito.data-index.url"); - String trustyServiceUrl = getProperty(configurationBuildItem, systemPropertyBuildItems, "kogito.trusty.http.url"); - - cardPageBuildItem.addBuildTimeData("extensionBasePath", uiPath); - cardPageBuildItem.addBuildTimeData("openapiPath", openapiPath); - cardPageBuildItem.addBuildTimeData("devUIUrl", devUIUrl); - cardPageBuildItem.addBuildTimeData("dataIndexUrl", dataIndexUrl); - cardPageBuildItem.addBuildTimeData("isTracingEnabled", trustyServiceAvailableBuildItem.isPresent()); - cardPageBuildItem.addBuildTimeData("trustyServiceUrl", trustyServiceUrl); - cardPageBuildItem.addBuildTimeData("userData", readUsersInfo(devConsoleRuntimeConfig)); - - if (dataIndexServiceAvailableBuildItem.isPresent() || capabilities.isPresent(DATA_INDEX_CAPABILITY)) { - cardPageBuildItem.addPage(Page.webComponentPageBuilder() - .componentLink("qwc-jbpm-quarkus-devui.js") - .metadata("page", "Processes") - .title("Process Instances") - .icon("font-awesome-solid:diagram-project") - .dynamicLabelJsonRPCMethodName("queryProcessInstancesCount")); - - cardPageBuildItem.addPage(Page.webComponentPageBuilder() - .componentLink("qwc-jbpm-quarkus-devui.js") - .metadata("page", "TaskInbox") - .title("Tasks") - .icon("font-awesome-solid:bars-progress") - .dynamicLabelJsonRPCMethodName("queryTasksCount")); - - cardPageBuildItem.addPage(Page.webComponentPageBuilder() - .componentLink("qwc-jbpm-quarkus-devui.js") - .metadata("page", "JobsManagement") - .title("Jobs") - .icon("font-awesome-solid:clock") - .dynamicLabelJsonRPCMethodName("queryJobsCount")); - - cardPageBuildItem.addPage(Page.webComponentPageBuilder() - .componentLink("qwc-jbpm-quarkus-devui.js") - .metadata("page", "Forms") - .title("Forms") - .icon("font-awesome-solid:table-cells") - .dynamicLabelJsonRPCMethodName("getFormsCount")); - } - - if (trustyServiceAvailableBuildItem.isPresent()) { - cardPageBuildItem.addPage(Page.webComponentPageBuilder() - .componentLink("qwc-jbpm-quarkus-devui.js") - .metadata("page", "Audit") - .title("Audit investigation") - .icon("font-awesome-solid:gauge-high")); - } - - return cardPageBuildItem; - } - - private Collection readUsersInfo(DevConsoleRuntimeConfig devConsoleRuntimeConfig) { - if (devConsoleRuntimeConfig.userConfigByUser.isEmpty()) { - return Collections.emptyList(); - } - - return devConsoleRuntimeConfig.userConfigByUser.entrySet().stream() - .map(entry -> new UserInfo(entry.getKey(), entry.getValue().groups)) - .collect(Collectors.toList()); - } - - private static String getProperty(ConfigurationBuildItem configurationBuildItem, - List systemPropertyBuildItems, String propertyKey) { - - String propertyValue = configurationBuildItem - .getReadResult() - .getAllBuildTimeValues() - .get(propertyKey); - - if (propertyValue == null) { - propertyValue = configurationBuildItem - .getReadResult() - .getBuildTimeRunTimeValues() - .get(propertyKey); - } else { - return propertyValue; - } - - if (propertyValue == null) { - propertyValue = configurationBuildItem - .getReadResult() - .getRunTimeDefaultValues() - .get(propertyKey); - } - - if (propertyValue != null) { - return propertyValue; - } - - return systemPropertyBuildItems.stream().filter(property -> property.getKey().equals(propertyKey)) - .findAny() - .map(SystemPropertyBuildItem::getValue).orElse(null); - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension-deployment/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/deployment/RuntimeToolsQuarkusExtensionProcessor.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension-deployment/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/deployment/RuntimeToolsQuarkusExtensionProcessor.java deleted file mode 100644 index 80d3ce521f..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension-deployment/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/deployment/RuntimeToolsQuarkusExtensionProcessor.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.runtime.tools.quarkus.extension.deployment; - -import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.builditem.FeatureBuildItem; - -class RuntimeToolsQuarkusExtensionProcessor { - - private static final String FEATURE = "jbpm-quarkus-devui"; - - @BuildStep - FeatureBuildItem feature() { - return new FeatureBuildItem(FEATURE); - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension-deployment/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/deployment/data/UserInfo.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension-deployment/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/deployment/data/UserInfo.java deleted file mode 100644 index 142a15021e..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension-deployment/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/deployment/data/UserInfo.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.runtime.tools.quarkus.extension.deployment.data; - -import java.util.List; - -public class UserInfo { - - private String id; - - private List groups; - - public UserInfo(final String id, - final List groups) { - this.id = id; - this.groups = groups; - } - - public String getId() { - return id; - } - - public List getGroups() { - return groups; - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension-deployment/src/main/resources/dev-ui/qwc-jbpm-quarkus-devui.js b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension-deployment/src/main/resources/dev-ui/qwc-jbpm-quarkus-devui.js deleted file mode 100644 index d4de6dd653..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension-deployment/src/main/resources/dev-ui/qwc-jbpm-quarkus-devui.js +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import {html, LitElement} from 'lit'; -import { - dataIndexUrl, - devUIUrl, - extensionBasePath, - isTracingEnabled, - openapiPath, - trustyServiceUrl, - userData -} from 'build-time-data'; -import {RouterController} from 'router-controller'; - -export class QwcJbpmQuarkusDevui extends LitElement { - _routerController = new RouterController(this); - - constructor() { - super(); - } - - render() { - return html` -
`; - } - - async connectedCallback() { - super.connectedCallback(); - await this.updateComplete - if (!document.querySelector('#jbpm-devui-script')) { - const script = document.createElement('script'); - script.setAttribute("async", ""); - script.setAttribute("id", "jbpm-devui-script"); - script.setAttribute("src", `${extensionBasePath}/resources/webapp/standalone.js`); - script.addEventListener("load", () => { - this.initUI() - }) - document.head.appendChild(script); - } else { - this.initUI(); - } - } - - initUI() { - const metadata = this._routerController.getCurrentMetaData(); - const container = this.renderRoot.querySelector("#envelope-app"); - RuntimeToolsDevUI.open({ - container: container, - isDataIndexAvailable: true, - isTracingEnabled: isTracingEnabled, - dataIndexUrl: `${dataIndexUrl ?? "http://localhost:8180"}/graphql`, - trustyServiceUrl: `${trustyServiceUrl ?? "http://localhost:1336"}`, - page: metadata.page ?? "Processes", - devUIUrl: `${devUIUrl ?? window.location.origin}`, - openApiPath: `${openapiPath ?? "q/openapi.json"}`, - availablePages: ["Processes", "Jobs", "Tasks", "Forms"], - users: userData ?? [] - }); - } -} - -customElements.define('qwc-jbpm-quarkus-devui', QwcJbpmQuarkusDevui); \ No newline at end of file diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/pom.xml b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/pom.xml deleted file mode 100644 index 56f7285b25..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/pom.xml +++ /dev/null @@ -1,152 +0,0 @@ - - - - 4.0.0 - - org.jbpm - jbpm-quarkus-devui-parent - 999-SNAPSHOT - - jbpm-quarkus-devui - jBPM Quarkus DevUI - Runtime - Runtime development tools for jBPM projects - - - - io.quarkus - quarkus-core - - - - io.quarkus - quarkus-resteasy - - - - io.quarkus - quarkus-resteasy-jackson - - - - io.quarkus - quarkus-rest-client - - - - io.quarkus - quarkus-resteasy-multipart - - - - io.quarkus - quarkus-arc - - - - io.quarkus.arc - arc-processor - - - - - io.quarkus - quarkus-undertow - - - - jakarta.annotation - jakarta.annotation-api - - - - org.apache.commons - commons-lang3 - - - - org.junit.jupiter - junit-jupiter-engine - test - - - - org.mockito - mockito-junit-jupiter - test - - - - io.quarkus - quarkus-junit5 - test - - - - io.quarkus - quarkus-junit5-mockito - test - - - - io.rest-assured - rest-assured - test - - - - - - io.quarkus - quarkus-extension-maven-plugin - ${version.io.quarkus} - - - compile - - extension-descriptor - - - ${project.groupId}:${project.artifactId}-deployment:${project.version} - - org.jbpm.quarkus.dev-ui - - - - - - - maven-compiler-plugin - - - - io.quarkus - quarkus-extension-processor - ${version.io.quarkus} - - - - - - - diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/config/DevConsoleRuntimeConfig.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/config/DevConsoleRuntimeConfig.java deleted file mode 100644 index 2f821c5004..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/config/DevConsoleRuntimeConfig.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.config; - -import java.util.Map; - -import io.quarkus.runtime.annotations.ConfigItem; -import io.quarkus.runtime.annotations.ConfigPhase; -import io.quarkus.runtime.annotations.ConfigRoot; - -@ConfigRoot(name = "", prefix = "kogito", phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED) -public class DevConsoleRuntimeConfig { - - /** - * Mocked users data for the task inbox screen. - */ - @ConfigItem(name = "users") - public Map userConfigByUser; -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/config/DevUIStaticArtifactsRecorder.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/config/DevUIStaticArtifactsRecorder.java deleted file mode 100644 index 4c87f694ca..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/config/DevUIStaticArtifactsRecorder.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.config; - -import java.util.ArrayList; -import java.util.List; - -import io.quarkus.runtime.ShutdownContext; -import io.quarkus.runtime.annotations.Recorder; -import io.quarkus.vertx.http.runtime.devmode.FileSystemStaticHandler; -import io.vertx.core.Handler; -import io.vertx.ext.web.RoutingContext; - -@Recorder -public class DevUIStaticArtifactsRecorder { - - public Handler handler(String deploymentArtifactPath, ShutdownContext shutdownContext) { - List webRootConfigurations = new ArrayList<>(); - webRootConfigurations.add( - new FileSystemStaticHandler.StaticWebRootConfiguration(deploymentArtifactPath, "")); - - FileSystemStaticHandler fileSystemStaticHandler = new FileSystemStaticHandler(webRootConfigurations); - - shutdownContext.addShutdownTask(new ShutdownContext.CloseRunnable(fileSystemStaticHandler)); - - return fileSystemStaticHandler; - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/config/UserConfig.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/config/UserConfig.java deleted file mode 100644 index 30158794d2..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/config/UserConfig.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.config; - -import java.util.List; - -import io.quarkus.runtime.annotations.ConfigGroup; -import io.quarkus.runtime.annotations.ConfigItem; - -@ConfigGroup -public class UserConfig { - - /** - * Groups which the user belong to. - */ - @ConfigItem(name = "groups") - public List groups; -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/DataIndexClient.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/DataIndexClient.java deleted file mode 100644 index 6a9af3c588..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/DataIndexClient.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.dataindex; - -import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.dataindex.jobs.JobsResponse; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.dataindex.processes.ProcessInstancesResponse; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.dataindex.tasks.TasksResponse; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.core.MediaType; - -import static org.kie.kogito.runtime.tools.quarkus.extension.runtime.dataindex.DataIndexClient.DATA_INDEX_CONFIG_KEY; - -@Path("/graphql") -@RegisterRestClient(configKey = DATA_INDEX_CONFIG_KEY) -@ApplicationScoped -public interface DataIndexClient { - - String DATA_INDEX_CONFIG_KEY = "kogito.data-index.url"; - - @POST - @Consumes(MediaType.APPLICATION_JSON) - ProcessInstancesResponse queryProcessInstances(String query); - - @POST - @Consumes(MediaType.APPLICATION_JSON) - TasksResponse queryTasks(String query); - - @POST - @Consumes(MediaType.APPLICATION_JSON) - JobsResponse queryJobs(String query); -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/jobs/Job.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/jobs/Job.java deleted file mode 100644 index 5601fb3877..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/jobs/Job.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.dataindex.jobs; - -public class Job { - - private String id; - - public Job() { - } - - public Job(final String id) { - this.id = id; - } - - public String getId() { - return id; - } - - public void setId(final String id) { - this.id = id; - } - - @Override - public String toString() { - return id; - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/jobs/JobsData.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/jobs/JobsData.java deleted file mode 100644 index 0f1b0b9ffb..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/jobs/JobsData.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.dataindex.jobs; - -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class JobsData { - - @JsonProperty("Jobs") - private List jobsList; - - public JobsData() { - } - - public JobsData(final List jobsList) { - this.jobsList = jobsList; - } - - public List getJobs() { - return jobsList; - } - - public void setJobs(final List jobsList) { - this.jobsList = jobsList; - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/jobs/JobsResponse.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/jobs/JobsResponse.java deleted file mode 100644 index 1286f7ba14..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/jobs/JobsResponse.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.dataindex.jobs; - -public class JobsResponse { - - private JobsData data; - - public JobsResponse() { - } - - public JobsResponse(final JobsData data) { - this.data = data; - } - - public JobsData getData() { - return data; - } - - public void setData(final JobsData data) { - this.data = data; - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/processes/ProcessInstance.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/processes/ProcessInstance.java deleted file mode 100644 index a25f9043aa..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/processes/ProcessInstance.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.dataindex.processes; - -public class ProcessInstance { - - private String id; - - public ProcessInstance() { - } - - public ProcessInstance(final String id) { - this.id = id; - } - - public String getId() { - return id; - } - - public void setId(final String id) { - this.id = id; - } - - @Override - public String toString() { - return id; - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/processes/ProcessInstancesData.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/processes/ProcessInstancesData.java deleted file mode 100644 index 4a74a66903..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/processes/ProcessInstancesData.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.dataindex.processes; - -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class ProcessInstancesData { - - @JsonProperty("ProcessInstances") - private List instances; - - public ProcessInstancesData() { - } - - public ProcessInstancesData(final List instances) { - this.instances = instances; - } - - public List getProcessInstances() { - return instances; - } - - public void setProcessInstances(final List processInstancesList) { - this.instances = processInstancesList; - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/processes/ProcessInstancesResponse.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/processes/ProcessInstancesResponse.java deleted file mode 100644 index 51a64885df..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/processes/ProcessInstancesResponse.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.dataindex.processes; - -public class ProcessInstancesResponse { - - private ProcessInstancesData data; - - public ProcessInstancesResponse() { - } - - public ProcessInstancesResponse(final ProcessInstancesData data) { - this.data = data; - } - - public ProcessInstancesData getData() { - return data; - } - - public void setData(final ProcessInstancesData data) { - this.data = data; - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/tasks/Task.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/tasks/Task.java deleted file mode 100644 index 935eceaf78..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/tasks/Task.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.dataindex.tasks; - -public class Task { - - private String id; - - public Task() { - } - - public Task(final String id) { - this.id = id; - } - - public String getId() { - return id; - } - - public void setId(final String id) { - this.id = id; - } - - @Override - public String toString() { - return id; - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/tasks/TaskResponseData.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/tasks/TaskResponseData.java deleted file mode 100644 index 016c095cdf..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/dataindex/tasks/TaskResponseData.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.dataindex.tasks; - -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class TaskResponseData { - - @JsonProperty("UserTaskInstances") - private List tasks; - - public TaskResponseData() { - } - - public TaskResponseData(final List tasks) { - this.tasks = tasks; - } - - public List getTasks() { - return tasks; - } - - public void setTasks(final List userTaskInstancesList) { - this.tasks = userTaskInstancesList; - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/FormsService.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/FormsService.java deleted file mode 100644 index 08c9bdf708..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/FormsService.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms; - -import java.io.FileNotFoundException; - -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model.FormContent; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model.FormFilter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import jakarta.inject.Inject; -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.QueryParam; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; - -import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; - -@Path("/forms") -public class FormsService { - - private FormsStorage storage; - - private static final Logger LOGGER = LoggerFactory.getLogger(FormsService.class); - - @Inject - public FormsService(FormsStorage storage) { - this.storage = storage; - } - - @GET - @Path("/list") - @Produces(MediaType.APPLICATION_JSON) - public Response getFormsList(@QueryParam("names") FormFilter filter) { - try { - return Response.ok(storage.getFormInfoList(filter)).build(); - } catch (Exception e) { - LOGGER.warn("Error while getting forms list: ", e); - return Response.status(INTERNAL_SERVER_ERROR.getStatusCode(), "Unexpected error while getting forms list: " + e.getMessage()).build(); - } - } - - @GET - @Path("/count") - @Produces(MediaType.TEXT_PLAIN) - public Response formsCount() { - try { - return Response.ok(storage.getFormsCount()).build(); - } catch (Exception e) { - LOGGER.error("Error while getting forms count: ", e); - return Response.status(INTERNAL_SERVER_ERROR.getStatusCode(), "Unexpected error while getting forms count: " + e.getMessage()).build(); - } - } - - @GET - @Path("/{formName:\\S+}/") - @Produces(MediaType.APPLICATION_JSON) - public Response getFormContent(@PathParam("formName") String formName) { - try { - Response.ResponseBuilder responseBuilder = Response.ok(storage.getFormContent(formName)); - return responseBuilder.build(); - } catch (FileNotFoundException fe) { - return Response.status(INTERNAL_SERVER_ERROR.getStatusCode(), fe.getMessage()).build(); - } catch (Exception e) { - LOGGER.warn("Coudln't find form '" + formName + "'"); - return Response.status(INTERNAL_SERVER_ERROR.getStatusCode(), "Unexpected error while getting form content: " + e.getMessage()).build(); - } - } - - @POST - @Path("/{formName:\\S+}/") - @Produces(MediaType.APPLICATION_JSON) - @Consumes(MediaType.APPLICATION_JSON) - public Response updateFormContent(@PathParam("formName") String formName, FormContent formContent) { - try { - storage.updateFormContent(formName, formContent); - return Response.ok().build(); - } catch (Exception e) { - return Response.status(INTERNAL_SERVER_ERROR.getStatusCode(), e.getMessage()).build(); - } - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/FormsStorage.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/FormsStorage.java deleted file mode 100644 index d0fe195db4..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/FormsStorage.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms; - -import java.io.IOException; -import java.util.Collection; - -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model.Form; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model.FormContent; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model.FormFilter; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model.FormInfo; - -public interface FormsStorage { - - int getFormsCount(); - - Collection getFormInfoList(FormFilter filter); - - Form getFormContent(String formName) throws IOException; - - void updateFormContent(String formName, FormContent formContent) throws IOException; -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/converter/FormFilterParamConverter.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/converter/FormFilterParamConverter.java deleted file mode 100644 index 9c6bfddd95..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/converter/FormFilterParamConverter.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.converter; - -import java.util.Collections; -import java.util.StringTokenizer; -import java.util.stream.Collectors; - -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model.FormFilter; - -import jakarta.ws.rs.ext.ParamConverter; -import jakarta.ws.rs.ext.Provider; - -@Provider -public class FormFilterParamConverter implements ParamConverter { - public FormFilter fromString(String names) { - StringTokenizer stringTokenizer = new StringTokenizer(names, ";"); - return new FormFilter(Collections.list(stringTokenizer).stream().map(s -> (String) s).collect(Collectors.toList())); - } - - public String toString(FormFilter names) { - return names.toString(); - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/converter/FormFilterParamConverterProvider.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/converter/FormFilterParamConverterProvider.java deleted file mode 100644 index a48083216a..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/converter/FormFilterParamConverterProvider.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.converter; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; - -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model.FormFilter; - -import jakarta.ws.rs.ext.ParamConverter; -import jakarta.ws.rs.ext.ParamConverterProvider; -import jakarta.ws.rs.ext.Provider; - -@Provider -public class FormFilterParamConverterProvider implements ParamConverterProvider { - - @SuppressWarnings("unchecked") - @Override - public ParamConverter getConverter(Class rawType, Type genericType, Annotation[] annotations) { - if (rawType.isAssignableFrom(FormFilter.class)) { - return (ParamConverter) new FormFilterParamConverter(); - } - return null; - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/impl/FormsStorageImpl.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/impl/FormsStorageImpl.java deleted file mode 100644 index 2cb4a9dd65..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/impl/FormsStorageImpl.java +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.impl; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.time.LocalDateTime; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.TimeZone; -import java.util.stream.Collectors; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringUtils; -import org.eclipse.microprofile.config.ConfigProvider; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.FormsStorage; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model.Form; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model.FormConfiguration; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model.FormContent; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model.FormFilter; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model.FormInfo; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model.FormResources; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.vertx.core.json.JsonObject; - -import jakarta.enterprise.context.ApplicationScoped; - -@ApplicationScoped -public class FormsStorageImpl implements FormsStorage { - - public static final String PROJECT_FORM_STORAGE_PROP = "quarkus.kogito-runtime-tools.forms.folder"; - - private static final String CONFIG_EXT = ".config"; - - private static final String FORMS_STORAGE_PATH = "/forms"; - - private static final String JAR_FORMS_STORAGE_PATH = "/target/classes" + FORMS_STORAGE_PATH; - private static final String FS_FORMS_STORAGE_PATH = "/src/main/resources" + FORMS_STORAGE_PATH; - - private static final Logger LOGGER = LoggerFactory.getLogger(FormsStorageImpl.class); - - private final Map formInfoMap = new HashMap<>(); - private final Map modifiedForms = new HashMap<>(); - - private URL classLoaderFormsUrl; - private URL formsStorageUrl; - - public FormsStorageImpl() { - start(Thread.currentThread().getContextClassLoader().getResource(FORMS_STORAGE_PATH)); - } - - FormsStorageImpl(final URL classLoaderFormsUrl) { - start(classLoaderFormsUrl); - } - - private void start(final URL classLoaderFormsUrl) { - start(classLoaderFormsUrl, getFormStorageUrl(classLoaderFormsUrl)); - } - - private void start(final URL classLoaderFormsUrl, final URL formsStorageUrl) { - try { - this.classLoaderFormsUrl = classLoaderFormsUrl; - this.formsStorageUrl = formsStorageUrl; - } catch (Exception ex) { - LOGGER.warn("Couldn't properly initialize FormsStorageImpl"); - } finally { - init(); - } - } - - private URL getFormStorageUrl(URL classLoaderFormsUrl) { - if (classLoaderFormsUrl == null) { - return null; - } - - String storageUrl = ConfigProvider.getConfig() - .getOptionalValue(PROJECT_FORM_STORAGE_PROP, String.class) - .orElseGet(() -> classLoaderFormsUrl.getFile().replace(JAR_FORMS_STORAGE_PATH, FS_FORMS_STORAGE_PATH)); - - File formsStorageFolder = new File(storageUrl); - - if (!formsStorageFolder.exists() || !formsStorageFolder.isDirectory()) { - LOGGER.warn("Cannot initialize form storage folder in path '" + formsStorageFolder.getPath() + "'"); - } - - try { - return formsStorageFolder.toURI().toURL(); - } catch (MalformedURLException ex) { - LOGGER.warn("Cannot initialize form storage folder in path '" + formsStorageFolder.getPath() + "'", ex); - } - return null; - } - - @Override - public int getFormsCount() { - return formInfoMap.keySet().size(); - } - - @Override - public Collection getFormInfoList(FormFilter filter) { - if (filter != null && !filter.getNames().isEmpty()) { - return formInfoMap.entrySet().stream() - .filter(entry -> StringUtils.containsAnyIgnoreCase(entry.getKey(), filter.getNames().toArray(new String[0]))) - .map(Map.Entry::getValue) - .collect(Collectors.toList()); - } else { - return formInfoMap.values(); - } - } - - private FormInfo.FormType getFormType(String type) { - switch (type) { - case "html": - return FormInfo.FormType.HTML; - case "tsx": - return FormInfo.FormType.TSX; - } - return null; - } - - @Override - public Form getFormContent(String formName) throws IOException { - FormInfo formInfo = formInfoMap.get(formName); - - if (formInfo == null) { - throw new RuntimeException("Cannot find form '" + formName + "'"); - } - - return modifiedForms.getOrDefault(formName, loadForm(formInfo)); - } - - private Form loadForm(FormInfo formInfo) throws IOException { - File formFile = getFormFile(formInfo.getName(), formInfo); - File formConfig = getFormConfigFile(formInfo.getName()); - String formConfiguration = ""; - if (formConfig != null && formConfig.exists()) { - formConfiguration = IOUtils.toString(new FileInputStream(formConfig), StandardCharsets.UTF_8); - } - Form form; - if (formFile != null && formFile.exists()) { - form = new Form(formInfo, IOUtils.toString(new FileInputStream(formFile), StandardCharsets.UTF_8), readFormConfiguration(formConfiguration)); - } else { - throw new FileNotFoundException(formInfo.getName() + "'s config file was not found"); - } - return form; - } - - private FormConfiguration readFormConfiguration(String configStr) { - if (StringUtils.isEmpty(configStr)) { - return new FormConfiguration("", new FormResources()); - } - - JsonObject configJSON = new JsonObject(configStr); - - FormResources resources = new FormResources(); - - JsonObject resourcesJSON = configJSON.getJsonObject("resources"); - - resourcesJSON.getJsonObject("scripts").stream().forEach(entry -> resources.getScripts().put(entry.getKey(), entry.getValue().toString())); - - resourcesJSON.getJsonObject("styles").stream().forEach(entry -> resources.getStyles().put(entry.getKey(), entry.getValue().toString())); - - return new FormConfiguration(configJSON.getString("schema"), resources); - } - - private File getFormFile(String formName, FormInfo formInfo) throws MalformedURLException { - URL formUrl = new URL(classLoaderFormsUrl.toString() + File.separator + formName + "." + formInfo.getType().getValue()); - return FileUtils.toFile(formUrl); - } - - private File getFormConfigFile(String formName) throws MalformedURLException { - URL configUri = new URL(classLoaderFormsUrl.toString() + File.separator + formName + CONFIG_EXT); - return FileUtils.toFile(configUri); - } - - @Override - public void updateFormContent(String formName, FormContent formContent) throws IOException { - if (this.formsStorageUrl == null) { - throw new RuntimeException("Cannot store form'" + formName + "'. Form storage couldnt be properly initialized."); - } - - FormInfo formInfo = formInfoMap.get(formName); - - if (formInfo == null) { - throw new RuntimeException("Cannot find form '" + formName + "'"); - } - - if (formContent == null) { - throw new RuntimeException("Invalid form content"); - } - - File formFile = getPersistableFormFile(formInfo); - File configFile = getPersistableConfigFile(formInfo); - - if (!formFile.exists() || !configFile.exists()) { - throw new RuntimeException("Cannot store form '" + formName + "'. Unable to find form"); - } - - FileUtils.write(formFile, formContent.getSource(), StandardCharsets.UTF_8); - - FileUtils.write(configFile, JsonObject.mapFrom(formContent.getConfiguration()).encodePrettily(), StandardCharsets.UTF_8); - - LocalDateTime lastModified = LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis()), TimeZone.getDefault().toZoneId()); - FormInfo newInfo = new FormInfo(formName, formInfo.getType(), lastModified); - - formInfoMap.put(formName, newInfo); - - modifiedForms.put(formName, new Form(newInfo, formContent.getSource(), formContent.getConfiguration())); - } - - private File getPersistableFormFile(FormInfo formInfo) { - return FileUtils.getFile(this.formsStorageUrl.getFile() + "/" + formInfo.getName() + "." + formInfo.getType().getValue()); - } - - private File getPersistableConfigFile(FormInfo formInfo) { - return FileUtils.getFile(this.formsStorageUrl.getFile() + "/" + formInfo.getName() + CONFIG_EXT); - } - - private void init() { - readFormResources().stream() - .filter(file -> hasConfigFile(FilenameUtils.removeExtension(file.getName()))) - .forEach(file -> { - LocalDateTime lastModified = LocalDateTime.ofInstant(Instant.ofEpochMilli(file.lastModified()), TimeZone.getDefault().toZoneId()); - formInfoMap.put(FilenameUtils.removeExtension(file.getName()), - new FormInfo(FilenameUtils.removeExtension(file.getName()), getFormType(FilenameUtils.getExtension(file.getName())), lastModified)); - }); - } - - private Collection readFormResources() { - if (classLoaderFormsUrl != null) { - LOGGER.info("form's files path is {}", classLoaderFormsUrl.toString()); - File rootFolder = FileUtils.toFile(classLoaderFormsUrl); - return FileUtils.listFiles(rootFolder, new String[] { "html", "tsx" }, false); - } - return Collections.emptyList(); - } - - private boolean hasConfigFile(String formName) { - try { - return getFormConfigFile(formName).exists(); - } catch (MalformedURLException e) { - LOGGER.info(e.getMessage()); - return false; - } - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/model/Form.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/model/Form.java deleted file mode 100644 index 6b870d80ee..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/model/Form.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model; - -public class Form { - - private final FormInfo formInfo; - private final String source; - private final FormConfiguration configuration; - - public Form(FormInfo formInfo, String source, FormConfiguration configuration) { - this.formInfo = formInfo; - this.source = source; - this.configuration = configuration; - } - - public FormInfo getFormInfo() { - return formInfo; - } - - public String getSource() { - return source; - } - - public FormConfiguration getConfiguration() { - return configuration; - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/model/FormConfiguration.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/model/FormConfiguration.java deleted file mode 100644 index 79b6ca2314..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/model/FormConfiguration.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model; - -public class FormConfiguration { - - private String schema; - private FormResources resources; - - public FormConfiguration() { - } - - public FormConfiguration(String schema, FormResources resources) { - this.schema = schema; - this.resources = resources; - } - - public String getSchema() { - return schema; - } - - public void setSchema(String schema) { - this.schema = schema; - } - - public FormResources getResources() { - return resources; - } - - public void setResources(FormResources resources) { - this.resources = resources; - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/model/FormContent.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/model/FormContent.java deleted file mode 100644 index 76598c2b4b..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/model/FormContent.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model; - -public class FormContent { - - private String source; - private FormConfiguration configuration; - - public FormContent() { - } - - public FormContent(String source, FormConfiguration configuration) { - this.source = source; - this.configuration = configuration; - } - - public void setSource(String source) { - this.source = source; - } - - public void setConfiguration(FormConfiguration configuration) { - this.configuration = configuration; - } - - public String getSource() { - return source; - } - - public FormConfiguration getConfiguration() { - return configuration; - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/model/FormFilter.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/model/FormFilter.java deleted file mode 100644 index d09eb1237b..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/model/FormFilter.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model; - -import java.util.ArrayList; -import java.util.List; - -public class FormFilter { - - private final List names; - - public FormFilter() { - this.names = new ArrayList<>(); - } - - public FormFilter(List names) { - this.names = names; - } - - public List getNames() { - return names; - } - - public void setNames(List names) { - this.names.addAll(names); - } - - @Override - public String toString() { - return "FormFilter{" + - "names=" + names + - '}'; - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/model/FormInfo.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/model/FormInfo.java deleted file mode 100644 index 43e31a26df..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/model/FormInfo.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model; - -import java.time.LocalDateTime; -import java.util.Objects; - -public class FormInfo { - - public enum FormType { - HTML("html"), - TSX("tsx"); - - private final String value; - - FormType(String value) { - this.value = value; - } - - public String getValue() { - return value; - } - } - - private final String name; - private final FormType type; - private final LocalDateTime lastModified; - - public FormInfo(String name, FormType type, LocalDateTime lastModified) { - this.name = name; - this.lastModified = lastModified; - this.type = type; - } - - public String getName() { - return name; - } - - public FormType getType() { - return type; - } - - public LocalDateTime getLastModified() { - return lastModified; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - FormInfo that = (FormInfo) o; - - if (!Objects.equals(getName(), that.getName())) { - return false; - } - if (getType() != that.getType()) { - return false; - } - return Objects.equals(getLastModified(), that.getLastModified()); - } - - @Override - public int hashCode() { - int result = getName() != null ? getName().hashCode() : 0; - result = 31 * result + (getType() != null ? getType().hashCode() : 0); - result = 31 * result + (getLastModified() != null ? getLastModified().hashCode() : 0); - return result; - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/model/FormResources.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/model/FormResources.java deleted file mode 100644 index a819898d1f..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/model/FormResources.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model; - -import java.util.LinkedHashMap; -import java.util.Map; - -public class FormResources { - - private Map scripts = new LinkedHashMap<>(); - - private Map styles = new LinkedHashMap<>(); - - public FormResources() { - } - - public Map getScripts() { - return scripts; - } - - public void setScripts(Map scripts) { - this.scripts = scripts; - } - - public Map getStyles() { - return styles; - } - - public void setStyles(Map styles) { - this.styles = styles; - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/rpc/JBPMDevuiJsonRPCService.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/rpc/JBPMDevuiJsonRPCService.java deleted file mode 100644 index fd3f4070e6..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/rpc/JBPMDevuiJsonRPCService.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.rpc; - -import java.util.function.Supplier; - -import org.eclipse.microprofile.rest.client.inject.RestClient; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.dataindex.DataIndexClient; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.FormsStorage; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import io.smallrye.common.annotation.NonBlocking; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; - -@ApplicationScoped -public class JBPMDevuiJsonRPCService { - public static final String ALL_TASKS_IDS_QUERY = "{ \"operationName\": \"getAllTasksIds\", \"query\": \"query getAllTasksIds{ UserTaskInstances{ id } }\" }"; - public static final String ALL_PROCESS_INSTANCES_IDS_QUERY = "{ \"operationName\": \"getAllProcessesIds\", \"query\": \"query getAllProcessesIds{ ProcessInstances{ id } }\" }"; - public static final String ALL_JOBS_IDS_QUERY = "{ \"operationName\": \"getAllJobsIds\", \"query\": \"query getAllJobsIds{ Jobs{ id } }\" }"; - - private final ObjectMapper mapper; - private final DataIndexClient dataIndexClient; - private final FormsStorage formsStorage; - - @Inject - public JBPMDevuiJsonRPCService(ObjectMapper mapper, @RestClient DataIndexClient dataIndexClient, FormsStorage formsStorage) { - this.mapper = mapper; - this.dataIndexClient = dataIndexClient; - this.formsStorage = formsStorage; - } - - @NonBlocking - public String queryProcessInstancesCount() { - return doQuery(() -> this.dataIndexClient.queryProcessInstances(ALL_PROCESS_INSTANCES_IDS_QUERY).getData().getProcessInstances().size()); - } - - @NonBlocking - public String queryTasksCount() { - return doQuery(() -> this.dataIndexClient.queryTasks(ALL_TASKS_IDS_QUERY).getData().getTasks().size()); - } - - @NonBlocking - public String queryJobsCount() { - return doQuery(() -> this.dataIndexClient.queryJobs(ALL_JOBS_IDS_QUERY).getData().getJobs().size()); - } - - @NonBlocking - public String getFormsCount() { - return doQuery(this.formsStorage::getFormsCount); - } - - private String doQuery(Supplier countSupplier) { - try { - return String.valueOf(countSupplier.get()); - } catch (Exception ex) { - return "-"; - } - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/resources/META-INF/beans.xml b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/resources/META-INF/beans.xml deleted file mode 100644 index a0eb9fbf8c..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/resources/META-INF/beans.xml +++ /dev/null @@ -1,20 +0,0 @@ - diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/resources/META-INF/quarkus-extension.yaml b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/resources/META-INF/quarkus-extension.yaml deleted file mode 100644 index 913453b40e..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/resources/META-INF/quarkus-extension.yaml +++ /dev/null @@ -1,40 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -name: jBPM Quarkus Dev UI -description: Enables the jBPM Runtime tools in Quarkus Dev UI -metadata: - keywords: - - "kogito" - - "drools" - - "jbpm" - - "processes" - - "tasks" - - "rules" - - "decisions" - - "BPMN" - - "DMN" - - "DRL" - - "dev-ui" - guide: "https://quarkus.io/guides/kogito" - categories: - - "business-automation" - status: "preview" - config: - - "kogito." \ No newline at end of file diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/resources/application.properties b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/resources/application.properties deleted file mode 100644 index 7f9b9ba1fb..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/main/resources/application.properties +++ /dev/null @@ -1,21 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -kogito.users.admin.groups=admin -quarkus.rest-client."kogito.data-index.url".url=${kogito.data-index.url}/ diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/test/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/impl/FormsStorageImplTest.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/test/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/impl/FormsStorageImplTest.java deleted file mode 100644 index d29daa53a5..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/test/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/impl/FormsStorageImplTest.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.impl; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; - -import org.apache.commons.io.FileUtils; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.FormsStorage; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model.Form; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model.FormConfiguration; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model.FormContent; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model.FormFilter; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model.FormInfo; -import org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.model.FormResources; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.impl.FormsStorageImpl.PROJECT_FORM_STORAGE_PROP; - -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -public class FormsStorageImplTest { - - private static final String TEST_FORM_CONTENT = "
"; - private static final String STYLE1 = "style1"; - private static final String SCRIPT1 = "script1"; - - private static final String FORM_NAME = "hiring_HRInterview"; - private static final String PARTIAL_FORM_NAME = "hiring"; - private static final String FORM_NAME_WITH_OUT_CONFIG = "hiring_HRInterviewWithoutConfig"; - - private FormsStorage formsStorage; - - @BeforeAll - public void init() throws IOException { - File storage = Files.createTempDirectory("FormsTestProfile").toFile(); - storage.deleteOnExit(); - storage.mkdir(); - System.setProperty(PROJECT_FORM_STORAGE_PROP, storage.getAbsolutePath()); - - URL formsFolder = Thread.currentThread().getContextClassLoader().getResource("forms"); - - formsStorage = new FormsStorageImpl(formsFolder); - } - - @Test - public void testGetFormsCount() { - assertEquals(2, formsStorage.getFormsCount()); - } - - @Test - public void testGetFormInfoList() { - Collection formInfosAll = formsStorage.getFormInfoList(null); - assertEquals(2, formInfosAll.size()); - - FormFilter filterEmpty = new FormFilter(); - filterEmpty.setNames(Collections.emptyList()); - Collection formInfosAllEmptyFilter = formsStorage.getFormInfoList(filterEmpty); - assertEquals(2, formInfosAll.size()); - - FormFilter filter = new FormFilter(); - filter.setNames(Arrays.asList(FORM_NAME)); - - Collection formInfos = formsStorage.getFormInfoList(filter); - assertEquals(1, formInfos.size()); - - FormFilter partialFilter = new FormFilter(); - partialFilter.setNames(Arrays.asList(PARTIAL_FORM_NAME)); - - Collection formInfosPartial = formsStorage.getFormInfoList(partialFilter); - assertEquals(2, formInfosPartial.size()); - } - - @Test - public void testGetFormContent() throws IOException { - Form formContent = formsStorage.getFormContent(FORM_NAME); - assertNotNull(formContent); - FormInfo formInfo = formContent.getFormInfo(); - assertEquals(FORM_NAME, formInfo.getName()); - } - - @Test - public void testGetFormContentWithoutConfig() { - assertThrows(RuntimeException.class, () -> formsStorage.getFormContent(FORM_NAME_WITH_OUT_CONFIG)); - assertThrows(RuntimeException.class, () -> formsStorage.getFormContent("ERROR")); - } - - @Test - public void testUpdateFormContentInvalidForms() { - assertThrows(RuntimeException.class, () -> formsStorage.updateFormContent(FORM_NAME_WITH_OUT_CONFIG, new FormContent())); - assertThrows(RuntimeException.class, () -> formsStorage.updateFormContent(FORM_NAME, null)); - } - - @Test - public void testUpdateValidForms() throws IOException { - File storage = new File(System.getProperties().getProperty(PROJECT_FORM_STORAGE_PROP)); - - File sourceFile = new File(storage.toURI().resolve(FORM_NAME + ".html")); - File configFile = new File(storage.toURI().resolve(FORM_NAME + ".config")); - - sourceFile.createNewFile(); - configFile.createNewFile(); - - FileUtils.write(sourceFile, "", StandardCharsets.UTF_8); - FileUtils.write(configFile, "", StandardCharsets.UTF_8); - - Form form = formsStorage.getFormContent(FORM_NAME); - FormResources resources = new FormResources(); - - resources.getStyles().put(STYLE1, STYLE1); - resources.getScripts().put(SCRIPT1, SCRIPT1); - - FormContent content = new FormContent(TEST_FORM_CONTENT, new FormConfiguration(form.getConfiguration().getSchema(), resources)); - - formsStorage.updateFormContent(FORM_NAME, content); - - Form newForm = formsStorage.getFormContent(FORM_NAME); - - assertEquals(TEST_FORM_CONTENT, newForm.getSource()); - assertEquals(form.getConfiguration().getSchema(), newForm.getConfiguration().getSchema()); - assertEquals(STYLE1, newForm.getConfiguration().getResources().getStyles().get(STYLE1)); - assertEquals(SCRIPT1, newForm.getConfiguration().getResources().getScripts().get(SCRIPT1)); - - FileUtils.deleteQuietly(sourceFile); - FileUtils.deleteQuietly(configFile); - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/test/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/impl/FormsTestProfile.java b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/test/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/impl/FormsTestProfile.java deleted file mode 100644 index 164e561657..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/test/java/org/kie/kogito/runtime/tools/quarkus/extension/runtime/forms/impl/FormsTestProfile.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.impl; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.util.Collections; -import java.util.Map; - -import io.quarkus.test.junit.QuarkusTestProfile; - -import static org.kie.kogito.runtime.tools.quarkus.extension.runtime.forms.impl.FormsStorageImpl.PROJECT_FORM_STORAGE_PROP; - -public class FormsTestProfile implements QuarkusTestProfile { - - private String storagePath; - - public FormsTestProfile() throws IOException { - File storage = Files.createTempDirectory("FormsTestProfile").toFile(); - storage.deleteOnExit(); - storage.mkdir(); - storagePath = storage.getAbsolutePath(); - } - - @Override - public Map getConfigOverrides() { - return Collections.singletonMap(PROJECT_FORM_STORAGE_PROP, storagePath); - } -} diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/test/resources/forms/hiring_HRInterview.config b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/test/resources/forms/hiring_HRInterview.config deleted file mode 100644 index 2eb07abb35..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/test/resources/forms/hiring_HRInterview.config +++ /dev/null @@ -1,13 +0,0 @@ -{ - "resources": { - "styles": { - "bootstrap.min.css": "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" - }, - "scripts": { - "bootstrap.min.js": "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js", - "jquery.js": "https://code.jquery.com/jquery-3.2.1.slim.min.js", - "popper.js": "https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" - } - }, - "schema": "{\"$schema\":\"https://json-schema.org/draft/2019-09/schema\",\"type\":\"object\",\"properties\":{\"candidate\":{\"type\":\"object\",\"properties\":{\"email\":{\"type\":\"string\"},\"name\":{\"type\":\"string\"},\"salary\":{\"type\":\"integer\"},\"skills\":{\"type\":\"string\"}},\"input\":true},\"approve\":{\"type\":\"boolean\",\"output\":true}}}" -} \ No newline at end of file diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/test/resources/forms/hiring_HRInterview.html b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/test/resources/forms/hiring_HRInterview.html deleted file mode 100644 index a663b92ede..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/test/resources/forms/hiring_HRInterview.html +++ /dev/null @@ -1,85 +0,0 @@ - -
-
- Candidate -
-
- - -
- -
- - -
- -
- - -
- -
- - -
-
-
- -
- - -
-
diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/test/resources/forms/hiring_HRInterviewWithoutConfig.html b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/test/resources/forms/hiring_HRInterviewWithoutConfig.html deleted file mode 100644 index a663b92ede..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/test/resources/forms/hiring_HRInterviewWithoutConfig.html +++ /dev/null @@ -1,85 +0,0 @@ - -
-
- Candidate -
-
- - -
- -
- - -
- -
- - -
- -
- - -
-
-
- -
- - -
-
diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/test/resources/forms/hiring_ITInterview.config b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/test/resources/forms/hiring_ITInterview.config deleted file mode 100644 index 59e9200575..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/test/resources/forms/hiring_ITInterview.config +++ /dev/null @@ -1,13 +0,0 @@ -{ - "resources": { - "styles": { - "bootstrap.min.css": "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" - }, - "scripts": { - "bootstrap.min.js": "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js", - "jquery.js": "https://code.jquery.com/jquery-3.2.1.slim.min.js", - "popper.js": "https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" - } - }, - "schema": "{\"$schema\":\"https://json-schema.org/draft/2019-09/schema\",\"type\":\"object\",\"properties\":{\"approve\":{\"type\":\"boolean\",\"output\":true},\"candidate\":{\"type\":\"object\",\"properties\":{\"email\":{\"type\":\"string\"},\"name\":{\"type\":\"string\"},\"salary\":{\"type\":\"integer\"},\"skills\":{\"type\":\"string\"}},\"input\":true}}}" -} \ No newline at end of file diff --git a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/test/resources/forms/hiring_ITInterview.html b/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/test/resources/forms/hiring_ITInterview.html deleted file mode 100644 index 8a051de719..0000000000 --- a/runtime-tools-quarkus-extension-parent/runtime-tools-quarkus-extension/src/test/resources/forms/hiring_ITInterview.html +++ /dev/null @@ -1,85 +0,0 @@ - -
-
- - -
- -
- Candidate -
-
- - -
- -
- - -
- -
- - -
- -
- - -
-
-
-
diff --git a/task-console/.dockerignore b/task-console/.dockerignore deleted file mode 100644 index b86c7ac340..0000000000 --- a/task-console/.dockerignore +++ /dev/null @@ -1,4 +0,0 @@ -* -!target/*-runner -!target/*-runner.jar -!target/lib/* \ No newline at end of file diff --git a/task-console/README.md b/task-console/README.md deleted file mode 100644 index edb59d5bf2..0000000000 --- a/task-console/README.md +++ /dev/null @@ -1,135 +0,0 @@ -# task-console project - -This project uses Quarkus, the Supersonic Subatomic Java Framework. - -If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ . - -## Running the application in dev mode - -You can run your application in dev mode that enables live coding using: -``` -mvn quarkus:dev -``` -Note: Live coding of the React JS frontend application is not yet in place. - -## Packaging and running the application - -The application is packageable using: -``` -mvn package -``` -It produces the executable `task-console-8.0.0-SNAPSHOT-runner.jar` file in `/target` directory. -Be aware that it’s not an _über-jar_ as the dependencies are copied into the `target/lib` directory. - -The application is now runnable using: -``` -java -jar target/task-console-8.0.0-SNAPSHOT-runner.jar -``` - -> Note: task-console requires keycloak server to be initialied for authentication. - -## Creating a native executable - -You can create a native executable using: -``` -mvn package -Dnative -``` - -Or you can use Docker to build the native executable using: -``` -mvn package -Dnative -Dquarkus.native.container-build=true -``` - -You can then execute your binary: `./target/task-console-8.0.0-SNAPSHOT-runner` - -If you want to learn more about building native executables, please consult https://quarkus.io/guides/building-native-image-guide . - -## Packaging together with the React app - - -The application makes use of a separately developed [React UI application](../ui-packages/packages/task-console/package.json). The JS based frontend can be built as part of the build of this project by using the profile defined in dependency [ui-packages](../ui-packages/pom.xml), invoked by default. Using the property `-Dskip.ui.build` as in following command you can skip the build of UI and use what is already built in the respective package: - -``` -mvn package -Dskip.ui.build -``` - -To prepare all the dependencies needed for the build of UI, there's a maven profile activated by default. Using the `-Dskip.ui.deps` property you can skip the profile. - -The single command to disable both UI build related profiles is: -``` -mvn package -Dskip.ui.deps -Dskip.ui.build -``` - -## Creating a native executable -The native build of the application bundling in the React JS frontend does not differ from the instructions above. The only thing that's new is again the invocation of UI specific profiles. -``` -mvn package -Dui -Dnative -``` - -## Enabling Keycloak security - -### Starting and Configuring the Keycloak Server - -To start a Keycloak Server you can use Docker and just run the following command: - -``` -docker run -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin -e KEYCLOAK_IMPORT=/tmp/kogito-realm.json -v {absolute_path}/kogito-apps/config/kogito-realm.json:/tmp/kogito-realm.json -p 8280:8080 jboss/keycloak -``` - -You should be able to access your Keycloak Server at [localhost:8280/auth](http://localhost:8280) -and verify keycloak server is running properly: log in as the admin user to access the Keycloak Administration Console. -Username should be admin and password admin. - -The following are the users available in keycloak - -| Login | Password | Roles | -| ------------- | ---------- | ------------------- | -| admin | admin | *admin*, *managers* | -| alice | alice | *user* | -| jdoe | jdoe | *managers* | - -To change any of this client configuration access to http://localhost:8280/auth/admin/master/console/#/realms/kogito. - -### Working with Task Console features - -The task console shows a list of user tasks which are available for a process. Each column contains detailed information about the user task which are - *Name*, *Process*, *Priority*, *Status*, *Started* and *Last update*. The columns are sortable. - -![Task Console](./docs/taskconsole.png?raw=true "TaskConsole") - -The task console consist of filters, which can be used to narrow down the search on a user task. There are two filters available - -- A filter based on the status(dropdown) -- A filter based on the task name(text search) - -![Filters](./docs/filters.png?raw=true "Filters") - -The *Status* filter can be dropped down to view the and select the states available - -![States](./docs/states.png?raw=true "States") - -A *refresh* button is available to refresh the list of user tasks - -A *Reset to default* button is available to reset the filters to its initial state. - -The user task list also supports pagination. - -Clicking on the name of the user task will navigate to another screen, which consist of the auto generated forms. - -### Task details - -The task details page consist of an auto generated forms and buttons to perform corresponding action on the user tasks. - -![Forms](./docs/forms.png?raw=true "Forms") - -The task details page also contains a *View details* button, to view more details about the task. - -![Details](./docs/details.png?raw=true "Details") - -### Starting Kogito Task Console in dev mode - -Start the task console at port 8380, (the keycloak client 'kogito-console-quarkus' is configured to use that port ) -and enabling auth: - -``` -mvn clean compile quarkus:dev -Dquarkus.http.port=8380 -``` diff --git a/task-console/docs/details.png b/task-console/docs/details.png deleted file mode 100644 index 5ad82453a5..0000000000 Binary files a/task-console/docs/details.png and /dev/null differ diff --git a/task-console/docs/filters.png b/task-console/docs/filters.png deleted file mode 100644 index 8d6071e60c..0000000000 Binary files a/task-console/docs/filters.png and /dev/null differ diff --git a/task-console/docs/forms.png b/task-console/docs/forms.png deleted file mode 100644 index f282b834c6..0000000000 Binary files a/task-console/docs/forms.png and /dev/null differ diff --git a/task-console/docs/states.png b/task-console/docs/states.png deleted file mode 100644 index c3e53865c4..0000000000 Binary files a/task-console/docs/states.png and /dev/null differ diff --git a/task-console/docs/taskconsole.png b/task-console/docs/taskconsole.png deleted file mode 100644 index e892a3dcbd..0000000000 Binary files a/task-console/docs/taskconsole.png and /dev/null differ diff --git a/task-console/docs/testusersystem-add-user.png b/task-console/docs/testusersystem-add-user.png deleted file mode 100644 index 06c807ea41..0000000000 Binary files a/task-console/docs/testusersystem-add-user.png and /dev/null differ diff --git a/task-console/docs/testusersystem-menu-add.png b/task-console/docs/testusersystem-menu-add.png deleted file mode 100644 index 598232cab8..0000000000 Binary files a/task-console/docs/testusersystem-menu-add.png and /dev/null differ diff --git a/task-console/docs/testusersystem-menu.png b/task-console/docs/testusersystem-menu.png deleted file mode 100644 index 0c161d75e1..0000000000 Binary files a/task-console/docs/testusersystem-menu.png and /dev/null differ diff --git a/task-console/pom.xml b/task-console/pom.xml deleted file mode 100644 index 7beb810d45..0000000000 --- a/task-console/pom.xml +++ /dev/null @@ -1,158 +0,0 @@ - - - - - - org.kie.kogito - kogito-apps-build-parent - 999-SNAPSHOT - ../kogito-apps-build-parent/pom.xml - - 4.0.0 - - task-console - Kogito Apps :: Task Console - - - org.kie.kogito.task.console - ../ui-packages/packages/task-console-webapp - - - - - io.quarkus - quarkus-reactive-routes - - - io.quarkus - quarkus-resteasy - - - io.quarkus - quarkus-qute - - - - - io.quarkus - quarkus-smallrye-health - - - - io.quarkus - quarkus-junit5 - test - - - io.rest-assured - rest-assured - test - - - org.kie.kogito - kogito-apps-ui-packages - pom - - - - - - io.quarkus - quarkus-maven-plugin - - true - - - - - build - - - - - - maven-surefire-plugin - - - org.jboss.logmanager.LogManager - - - - - - maven-resources-plugin - - - copy-resources - process-resources - - copy-resources - - - ${basedir}/target/classes/META-INF/resources/ - - - ${path.to.frontend.app}/dist - - index.html - - false - - - - - - - - org.apache.maven.plugins - maven-assembly-plugin - - - src/assembly/image-build-zip.xml - - - - - package - - single - - - - - - - - - native - - - native - - - - native - - - - diff --git a/task-console/src/assembly/image-build-zip.xml b/task-console/src/assembly/image-build-zip.xml deleted file mode 100644 index 2592d48e8a..0000000000 --- a/task-console/src/assembly/image-build-zip.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - image-build - - zip - - false - - - target/quarkus-app - - - - \ No newline at end of file diff --git a/task-console/src/main/docker/Dockerfile.jvm b/task-console/src/main/docker/Dockerfile.jvm deleted file mode 100644 index 02468a6a13..0000000000 --- a/task-console/src/main/docker/Dockerfile.jvm +++ /dev/null @@ -1,47 +0,0 @@ -#### -# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode -# -# Before building the docker image run: -# -# mvn package -# -# Then, build the image with: -# -# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/task-console-jvm . -# -# Then run the container using: -# -# docker run -i --rm -p 8080:8080 quarkus/task-console-jvm -# -### -FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1 - -ARG JAVA_PACKAGE=java-1.8.0-openjdk-headless -ARG RUN_JAVA_VERSION=1.3.5 - -ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' - -# Install java and the run-java script -# Also set up permissions for user `1001` -RUN microdnf install openssl curl ca-certificates ${JAVA_PACKAGE} \ - && microdnf update \ - && microdnf clean all \ - && mkdir /deployments \ - && chown 1001 /deployments \ - && chmod "g+rwX" /deployments \ - && chown 1001:root /deployments \ - && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \ - && chown 1001 /deployments/run-java.sh \ - && chmod 540 /deployments/run-java.sh \ - && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security - -# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size. -ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" - -COPY target/lib/* /deployments/lib/ -COPY target/*-runner.jar /deployments/app.jar - -EXPOSE 8080 -USER 1001 - -ENTRYPOINT [ "/deployments/run-java.sh" ] \ No newline at end of file diff --git a/task-console/src/main/docker/Dockerfile.native b/task-console/src/main/docker/Dockerfile.native deleted file mode 100644 index 2901ae40ef..0000000000 --- a/task-console/src/main/docker/Dockerfile.native +++ /dev/null @@ -1,30 +0,0 @@ -#### -# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode -# -# Before building the docker image run: -# -# mvn package -Pnative -Dquarkus.native.container-build=true -# -# Then, build the image with: -# -# docker build -f src/main/docker/Dockerfile.native -t quarkus/task-console . -# -# Then run the container using: -# -# docker run -i --rm -p 8080:8080 quarkus/task-console -# -### -FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1 -WORKDIR /work/ -COPY target/*-runner /work/application - -# set up permissions for user `1001` -RUN chmod 775 /work /work/application \ - && chown -R 1001 /work \ - && chmod -R "g+rwX" /work \ - && chown -R 1001:root /work - -EXPOSE 8080 -USER 1001 - -CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] \ No newline at end of file diff --git a/task-console/src/main/java/org/kie/kogito/task/console/VertxRouter.java b/task-console/src/main/java/org/kie/kogito/task/console/VertxRouter.java deleted file mode 100644 index 1ebdea4192..0000000000 --- a/task-console/src/main/java/org/kie/kogito/task/console/VertxRouter.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.task.console; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.quarkus.qute.Location; -import io.quarkus.qute.Template; -import io.vertx.core.http.HttpHeaders; -import io.vertx.ext.web.Router; -import io.vertx.ext.web.RoutingContext; -import io.vertx.ext.web.handler.StaticHandler; - -import jakarta.annotation.PostConstruct; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.event.Observes; - -@ApplicationScoped -public class VertxRouter { - - private static final Logger LOGGER = LoggerFactory.getLogger(VertxRouter.class); - - @Location("index") - Template indexTemplate; - - private String index; - - @PostConstruct - public void init() { - index = indexTemplate.render(); - } - - void setupRouter(@Observes Router router) { - router.route("/").handler(ctx -> ctx.response().putHeader("location", "/TaskInbox/").setStatusCode(302).end()); - router.route("/TaskInbox").handler(this::handle); - router.route("/TaskDetails*").handler(this::handle); - router.route().handler(StaticHandler.create()); - } - - public void handle(RoutingContext context) { - try { - context.response() - .putHeader(HttpHeaders.CACHE_CONTROL, "no-cache") - .putHeader(HttpHeaders.CONTENT_TYPE, "text/html;charset=utf8") - .end(index); - } catch (Exception ex) { - LOGGER.error("Error handling index.html", ex); - context.fail(500, ex); - } - } -} diff --git a/task-console/src/main/resources/application.properties b/task-console/src/main/resources/application.properties deleted file mode 100644 index f9f62e1406..0000000000 --- a/task-console/src/main/resources/application.properties +++ /dev/null @@ -1,31 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -quarkus.http.cors=true -kogito.dataindex.http.url=http://localhost:8180/graphql -#keycloak client config -kogito.consoles.keycloak.config.realm=kogito -kogito.consoles.keycloak.config.url=http://localhost:8280/auth/ -kogito.consoles.keycloak.config.client-id=kogito-console-quarkus -kogito.consoles.keycloak.config.health-check-url=http://localhost:8280/auth/realms/kogito/.well-known/openid-configuration -kogito.consoles.keycloak.config.disable-health-check=false -kogito.consoles.keycloak.config.update-token-validity=30 - -kogito.task.active.states.list=Ready,Reserved -kogito.task.states.list=Ready,Reserved,Completed,Aborted,Skipped diff --git a/task-console/src/main/resources/templates/index.html b/task-console/src/main/resources/templates/index.html deleted file mode 100644 index b439540967..0000000000 --- a/task-console/src/main/resources/templates/index.html +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - Kogito - Task Console - - - - - - - -
- - - diff --git a/task-console/src/test/java/org/kie/kogito/task/console/NativeStaticContentTestIT.java b/task-console/src/test/java/org/kie/kogito/task/console/NativeStaticContentTestIT.java deleted file mode 100644 index 39d0949064..0000000000 --- a/task-console/src/test/java/org/kie/kogito/task/console/NativeStaticContentTestIT.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.task.console; - -import io.quarkus.test.junit.QuarkusIntegrationTest; - -@QuarkusIntegrationTest -class NativeStaticContentTestIT extends StaticContentTest { - - // Execute the same tests but in native mode. -} \ No newline at end of file diff --git a/task-console/src/test/java/org/kie/kogito/task/console/StaticContentTest.java b/task-console/src/test/java/org/kie/kogito/task/console/StaticContentTest.java deleted file mode 100644 index f982a23c01..0000000000 --- a/task-console/src/test/java/org/kie/kogito/task/console/StaticContentTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.task.console; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.nio.charset.StandardCharsets; - -import org.junit.jupiter.api.Test; - -import io.quarkus.test.common.http.TestHTTPResource; -import io.quarkus.test.junit.QuarkusTest; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -@QuarkusTest -class StaticContentTest { - - @TestHTTPResource - URL url; - - private static String readStream(InputStream in) throws IOException { - byte[] data = new byte[1024]; - int r; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - while ((r = in.read(data)) > 0) { - out.write(data, 0, r); - } - return new String(out.toByteArray(), StandardCharsets.UTF_8); - } - - @Test - void testIndexHtml() throws Exception { - try (InputStream in = url.openStream()) { - String contents = readStream(in); - assertTrue(contents.contains("Kogito - Task Console")); - } - } -} diff --git a/task-console/src/test/java/org/kie/kogito/task/console/VertxRouterTest.java b/task-console/src/test/java/org/kie/kogito/task/console/VertxRouterTest.java deleted file mode 100644 index 27d8b90a60..0000000000 --- a/task-console/src/test/java/org/kie/kogito/task/console/VertxRouterTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.task.console; - -import org.junit.jupiter.api.Test; - -import io.quarkus.test.junit.QuarkusTest; - -import static io.restassured.RestAssured.given; - -@QuarkusTest -class VertxRouterTest { - - @Test - void testHandlePath() { - - given().when().get("/TaskInbox") - .then() - .statusCode(200); - - given().when().get("/TaskDetails/a1e139d5-4e77-48c9-84ae-34578e904e5a") - .then() - .statusCode(200); - - given().when().get("/Another") - .then() - .statusCode(404); - } -} diff --git a/trusty-ui/.dockerignore b/trusty-ui/.dockerignore deleted file mode 100644 index b86c7ac340..0000000000 --- a/trusty-ui/.dockerignore +++ /dev/null @@ -1,4 +0,0 @@ -* -!target/*-runner -!target/*-runner.jar -!target/lib/* \ No newline at end of file diff --git a/trusty-ui/README.md b/trusty-ui/README.md deleted file mode 100644 index 71ddda6545..0000000000 --- a/trusty-ui/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# trusty-ui project - -This project uses Quarkus, the Supersonic Subatomic Java Framework. - -If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ . - -## Running the application in dev mode - -You can run your application in dev mode that enables live coding using: -``` -mvn quarkus:dev -``` -Note: Live coding of the React JS frontend application is not yet in place. - -## Packaging and running the application - -The application is packageable using: -``` -mvn package -``` -It produces the executable `trusty-ui-999-SNAPSHOT-runner.jar` file in `/target` directory. -Be aware that it’s not an _über-jar_ as the dependencies are copied into the `target/lib` directory. - -The application is now runnable using: -``` -java -jar target/trusty-ui-999-SNAPSHOT-runner.jar -``` - -## Creating a native executable - -You can create a native executable using: -``` -mvn package -Dnative -``` - -Or you can use Docker to build the native executable using: -``` -mvn package -Dnative -Dquarkus.native.container-build=true -``` - -You can then execute your binary: `./target/trusty-ui-999-SNAPSHOT-runner` - -If you want to learn more about building native executables, please consult https://quarkus.io/guides/building-native-image-guide . - -## Packaging together with the React app - -The application makes use of a separately developed [React UI application](../ui-packages/packages/trusty/package.json). The JS based frontend can be built as part of the build of this project by using the profile defined in dependency [ui-packages](../ui-packages/pom.xml), invoked by default. Using the property `-Dskip.ui.build` as in following command you can skip the build of UI and use what is already built in the respective package: -``` -mvn package -Dskip.ui.build -``` - -To prepare all the dependencies needed for the build of UI, there's a maven profile activated by default. Using the `-Dskip.ui.deps` property you can skip the profile. - - -The single command to disable both UI build related profiles is: -``` -mvn package -Dskip.ui.deps -Dskip.ui.build -``` - -## Creating a native executable -The native build of the application bundling in the React JS frontend does not differ from the instructions above. The only thing that's new is again the invocation of UI specific profiles. -``` -mvn package -Dui -Dnative -``` - -## Working with trusty-ui features - -# TODO https://issues.redhat.com/browse/KOGITO-3183 - -## Enabling Keycloak security - -### Starting and Configuring the Keycloak Server - -To start a Keycloak Server you can use Docker and just run the following command: - -``` -docker run -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin -e KEYCLOAK_IMPORT=/tmp/kogito-realm.json -v {absolute_path}/kogito-apps/config/kogito-realm.json:/tmp/kogito-realm.json -p 8280:8080 jboss/keycloak -``` - -You should be able to access your Keycloak Server at [localhost:8280/auth](http://localhost:8280) -and verify keycloak server is running properly: log in as the admin user to access the Keycloak Administration Console. -Username should be admin and password admin. - -To change any of this client configuration access to http://localhost:8280/auth/admin/master/console/#/realms/kogito. - -### Starting Kogito Trusty UI in dev mode - -Start the trusty ui at port 8380: - -``` -mvn clean compile quarkus:dev -Dquarkus.http.port=8380 -``` - diff --git a/trusty-ui/pom.xml b/trusty-ui/pom.xml deleted file mode 100644 index a0d9a16430..0000000000 --- a/trusty-ui/pom.xml +++ /dev/null @@ -1,199 +0,0 @@ - - - - - - org.kie.kogito - kogito-apps-build-parent - 999-SNAPSHOT - ../kogito-apps-build-parent/pom.xml - - 4.0.0 - - trusty-ui - Kogito Apps :: Trusty UI - - - org.kie.kogito.trusty.ui - ../ui-packages/packages/trusty - - - - - - io.quarkus - quarkus-container-image-jib - - - - io.quarkus - quarkus-reactive-routes - - - io.quarkus - quarkus-resteasy-qute - - - io.quarkus - quarkus-junit5 - test - - - io.rest-assured - rest-assured - test - - - org.kie.kogito - kogito-apps-ui-packages - pom - - - io.quarkus - quarkus-oidc - - - - - io.quarkus - quarkus-smallrye-health - - - - org.keycloak - keycloak-core - test - - - org.kie.kogito - kogito-quarkus-test-utils - test - - - - - - io.quarkus - quarkus-maven-plugin - - true - - - - - build - - - - - - - maven-resources-plugin - - - copy-resources - process-resources - - copy-resources - - - ${basedir}/target/classes/META-INF/resources/ - - - ${path.to.frontend.app}/dist-standalone - - index.html - - false - - - - - - - - maven-surefire-plugin - - - org.jboss.logmanager.LogManager - ${container.image.keycloak} - - - - - org.apache.maven.plugins - maven-failsafe-plugin - - - integration-test - - integration-test - - - - **/Keycloak*IT.java - - - - - testWithKeycloak - - integration-test - - - - **/Keycloak*IT.java - - - keycloak-test - true - confidential - /* - role-policy1 - - - - - verify - - verify - - - - - - - - - native - - - native - - - - native - - - - diff --git a/trusty-ui/src/main/docker/Dockerfile.jvm b/trusty-ui/src/main/docker/Dockerfile.jvm deleted file mode 100644 index ebce5c42e2..0000000000 --- a/trusty-ui/src/main/docker/Dockerfile.jvm +++ /dev/null @@ -1,47 +0,0 @@ -#### -# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode -# -# Before building the docker image run: -# -# mvn package -# -# Then, build the image with: -# -# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/trusty-ui-jvm . -# -# Then run the container using: -# -# docker run -i --rm -p 8080:8080 quarkus/trusty-ui-jvm -# -### -FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1 - -ARG JAVA_PACKAGE=java-11-openjdk-headless -ARG RUN_JAVA_VERSION=1.3.8 - -ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' - -# Install java and the run-java script -# Also set up permissions for user `1001` -RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \ - && microdnf update \ - && microdnf clean all \ - && mkdir /deployments \ - && chown 1001 /deployments \ - && chmod "g+rwX" /deployments \ - && chown 1001:root /deployments \ - && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \ - && chown 1001 /deployments/run-java.sh \ - && chmod 540 /deployments/run-java.sh \ - && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security - -# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size. -ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" - -COPY target/lib/* /deployments/lib/ -COPY target/*-runner.jar /deployments/app.jar - -EXPOSE 8080 -USER 1001 - -ENTRYPOINT [ "/deployments/run-java.sh" ] diff --git a/trusty-ui/src/main/docker/Dockerfile.native b/trusty-ui/src/main/docker/Dockerfile.native deleted file mode 100644 index 91d7d9a9f1..0000000000 --- a/trusty-ui/src/main/docker/Dockerfile.native +++ /dev/null @@ -1,30 +0,0 @@ -#### -# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode -# -# Before building the docker image run: -# -# mvn package -Pnative -Dquarkus.native.container-build=true -# -# Then, build the image with: -# -# docker build -f src/main/docker/Dockerfile.native -t quarkus/trusty-ui . -# -# Then run the container using: -# -# docker run -i --rm -p 8080:8080 quarkus/trusty-ui -# -### -FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1 -WORKDIR /work/ -COPY target/*-runner /work/application - -# set up permissions for user `1001` -RUN chmod 775 /work /work/application \ - && chown -R 1001 /work \ - && chmod -R "g+rwX" /work \ - && chown -R 1001:root /work - -EXPOSE 8080 -USER 1001 - -CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] \ No newline at end of file diff --git a/trusty-ui/src/main/java/org/kie/kogito/trusty/ui/VertxRouter.java b/trusty-ui/src/main/java/org/kie/kogito/trusty/ui/VertxRouter.java deleted file mode 100644 index 0772bb1576..0000000000 --- a/trusty-ui/src/main/java/org/kie/kogito/trusty/ui/VertxRouter.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.trusty.ui; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.quarkus.qute.Location; -import io.quarkus.qute.Template; -import io.vertx.core.http.HttpHeaders; -import io.vertx.ext.web.Router; -import io.vertx.ext.web.RoutingContext; -import io.vertx.ext.web.handler.StaticHandler; - -import jakarta.annotation.PostConstruct; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.event.Observes; - -@ApplicationScoped -public class VertxRouter { - - private static final Logger LOGGER = LoggerFactory.getLogger(VertxRouter.class); - - @Location("index") - Template indexTemplate; - - private String index; - - @PostConstruct - public void init() { - index = indexTemplate.render(); - } - - void setupRouter(@Observes Router router) { - router.route("/").handler(ctx -> ctx.response().putHeader("location", "/audit").setStatusCode(302).end()); - router.route("/audit*").handler(ctx -> handle(ctx)); - router.route().handler(StaticHandler.create()); - } - - private void handle(RoutingContext context) { - try { - context.response() - .putHeader(HttpHeaders.CACHE_CONTROL, "no-cache") - .putHeader(HttpHeaders.CONTENT_TYPE, "text/html;charset=utf8") - .end(index); - } catch (Exception ex) { - LOGGER.error("Error handling index.html", ex); - context.fail(500, ex); - } - } -} diff --git a/trusty-ui/src/main/resources/application.properties b/trusty-ui/src/main/resources/application.properties deleted file mode 100644 index a3a905c8c4..0000000000 --- a/trusty-ui/src/main/resources/application.properties +++ /dev/null @@ -1,46 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -quarkus.http.cors=true -kogito.trusty.http.url=${KOGITO_TRUSTY_HTTP_URL:http://localhost:8180} - -# Container image -quarkus.container-image.build=${quarkus.build.image:true} -quarkus.container-image.group=org.kie.kogito - -#oidc -quarkus.oidc.enabled=true -quarkus.oidc.tenant-enabled=false - -#oidc - keycloak -%keycloak.quarkus.oidc.enabled=true -%keycloak.quarkus.oidc.tenant-enabled=true -%keycloak.quarkus.oidc.auth-server-url=http://localhost:8280/auth/realms/kogito -%keycloak.quarkus.oidc.client-id=kogito-service -%keycloak.quarkus.oidc.credentials.secret=secret -%keycloak.quarkus.oidc.application-type=service -%keycloak.quarkus.oidc.web-app-tenant.auth-server-url=http://localhost:8280/auth/realms/kogito -%keycloak.quarkus.oidc.web-app-tenant.client-id=kogito-service -%keycloak.quarkus.oidc.web-app-tenant.credentials.secret=secret -%keycloak.quarkus.oidc.web-app-tenant.application-type=web-app -# HTTP Security Configuration -quarkus.http.auth.permission.authenticated.paths=/* -quarkus.http.auth.permission.authenticated.policy=permit -%keycloak.quarkus.http.auth.permission.authenticated.paths=/* -%keycloak.quarkus.http.auth.permission.authenticated.policy=authenticated \ No newline at end of file diff --git a/trusty-ui/src/main/resources/templates/index.html b/trusty-ui/src/main/resources/templates/index.html deleted file mode 100644 index 0bbfade4f4..0000000000 --- a/trusty-ui/src/main/resources/templates/index.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - Kogito - TrustyAI - - - - - - - -
- - - diff --git a/trusty-ui/src/test/java/org/kie/kogito/trusty/ui/KeycloakTrustyUIServiceIT.java b/trusty-ui/src/test/java/org/kie/kogito/trusty/ui/KeycloakTrustyUIServiceIT.java deleted file mode 100644 index a26c0c1e07..0000000000 --- a/trusty-ui/src/test/java/org/kie/kogito/trusty/ui/KeycloakTrustyUIServiceIT.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.trusty.ui; - -import org.apache.http.HttpStatus; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.keycloak.representations.AccessTokenResponse; -import org.kie.kogito.test.quarkus.QuarkusTestProperty; -import org.kie.kogito.testcontainers.KogitoKeycloakContainer; -import org.kie.kogito.testcontainers.quarkus.KeycloakQuarkusTestResource; - -import io.quarkus.test.common.QuarkusTestResource; -import io.quarkus.test.common.ResourceArg; -import io.quarkus.test.junit.QuarkusTest; - -import static io.restassured.RestAssured.given; -import static org.kie.kogito.testcontainers.quarkus.KeycloakQuarkusTestResource.KOGITO_OIDC_TENANTS; - -@QuarkusTest -@QuarkusTestResource(value = KeycloakQuarkusTestResource.class, initArgs = { @ResourceArg(name = KOGITO_OIDC_TENANTS, value = "web-app-tenant") }) -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -class KeycloakTrustyUIServiceIT { - - private static final String VALID_USER = "jdoe"; - private static final String TRUSTY_UI_ENDPOINT = "/"; - - @QuarkusTestProperty(name = KeycloakQuarkusTestResource.KOGITO_KEYCLOAK_PROPERTY) - String keycloakURL; - - @Test - void shouldReturnUnauthorized() { - given().get(TRUSTY_UI_ENDPOINT) - .then().statusCode(HttpStatus.SC_UNAUTHORIZED); - } - - @Test - void shouldReturnOkWhenValidUser() { - given().auth().oauth2(getAccessToken(VALID_USER)).get(TRUSTY_UI_ENDPOINT) - .then().statusCode(HttpStatus.SC_OK); - } - - private String getAccessToken(String userName) { - return given().param("grant_type", "password") - .param("username", userName) - .param("password", userName) - .param("client_id", KogitoKeycloakContainer.CLIENT_ID) - .param("client_secret", KogitoKeycloakContainer.CLIENT_SECRET) - .when() - .post(keycloakURL + "/protocol/openid-connect/token") - .as(AccessTokenResponse.class).getToken(); - } -} diff --git a/trusty-ui/src/test/java/org/kie/kogito/trusty/ui/NativeStaticContentTestIT.java b/trusty-ui/src/test/java/org/kie/kogito/trusty/ui/NativeStaticContentTestIT.java deleted file mode 100644 index cbfdfba23f..0000000000 --- a/trusty-ui/src/test/java/org/kie/kogito/trusty/ui/NativeStaticContentTestIT.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.trusty.ui; - -import io.quarkus.test.junit.QuarkusIntegrationTest; - -@QuarkusIntegrationTest -public class NativeStaticContentTestIT extends StaticContentTest { - // Execute the same tests but in native mode. -} diff --git a/trusty-ui/src/test/java/org/kie/kogito/trusty/ui/StaticContentTest.java b/trusty-ui/src/test/java/org/kie/kogito/trusty/ui/StaticContentTest.java deleted file mode 100644 index 55b2a20f39..0000000000 --- a/trusty-ui/src/test/java/org/kie/kogito/trusty/ui/StaticContentTest.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.trusty.ui; - -import org.junit.jupiter.api.Test; - -import io.quarkus.test.junit.QuarkusTest; -import io.restassured.http.ContentType; -import io.vertx.core.http.HttpHeaders; - -import static io.restassured.RestAssured.given; -import static org.hamcrest.Matchers.containsString; - -@QuarkusTest -public class StaticContentTest { - - @Test - public void testIndexHtml() { - given().contentType(ContentType.JSON).when().get("/").then() - .statusCode(200) - .body(containsString("Kogito - TrustyAI")); - } - - @Test - public void testHeaders() { - given().contentType(ContentType.JSON).when().get("/").then() - .statusCode(200) - .header(HttpHeaders.CACHE_CONTROL.toString(), "no-cache") - .header(HttpHeaders.CONTENT_TYPE.toString(), "text/html;charset=utf8"); - } - - @Test - public void testHandlePath() { - given().when().get("/audit") - .then() - .statusCode(200); - - given().when().get("/audit/decision/9cf2179f-4fed-4793-b674-a19c45e6cbff/outcomes") - .then() - .statusCode(200); - } -} diff --git a/trusty-ui/src/test/resources/application.properties b/trusty-ui/src/test/resources/application.properties deleted file mode 100644 index e1eb0d84dd..0000000000 --- a/trusty-ui/src/test/resources/application.properties +++ /dev/null @@ -1,39 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -quarkus.http.cors=true -kogito.trusty.http.url=${KOGITO_TRUSTY_HTTP_URL:http://localhost:8180} - -quarkus.oidc.enabled=true -quarkus.oidc.tenant-enabled=false - -# Keycloak oidc -%keycloak-test.quarkus.oidc.enabled=true -%keycloak-test.quarkus.oidc.tenant-enabled=true -#%keycloak-test.quarkus.oidc.auth-server-url=http://localhost:8281/auth/realms/kogito -%keycloak-test.quarkus.oidc.client-id=kogito-app -%keycloak-test.quarkus.oidc.credentials.secret=secret -%keycloak-test.quarkus.oidc.application-type=service -#%keycloak-test.quarkus.oidc.web-app-tenant.auth-server-url=http://localhost:8281/auth/realms/kogito -%keycloak-test.quarkus.oidc.web-app-tenant.client-id=kogito-app -%keycloak-test.quarkus.oidc.web-app-tenant.credentials.secret=secret -%keycloak-test.quarkus.oidc.web-app-tenant.application-type=web-app - -# Not using Dev service in test, but rather org.kie.kogito.testcontainers.quarkus.KeycloakQuarkusTestResource -quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/ui-packages/.eslintrc.js b/ui-packages/.eslintrc.js deleted file mode 100755 index a20e6c2355..0000000000 --- a/ui-packages/.eslintrc.js +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -module.exports = { - root: true, - parser: '@typescript-eslint/parser', - plugins: ['@typescript-eslint'], - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:react/recommended', - 'plugin:react-hooks/recommended' - ], - rules: { - 'prefer-spread': 'off', - '@typescript-eslint/no-unused-vars': 'off', - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-non-null-assertion': 'off', - '@typescript-eslint/ban-types': 'off', - '@typescript-eslint/no-inferrable-types': 'off', - '@typescript-eslint/no-empty-interface': [ - 'error', - { allowSingleExtends: true } - ], - '@typescript-eslint/no-empty-function': 'off', - 'no-fallthrough': 'off', - 'no-case-declarations': 'off', - 'no-unexpected-multiline': 'off', - 'react/prop-types': 'off', - 'react/display-name': 'off', - 'react/jsx-no-target-blank': 'off' - }, - settings: { - react: { - version: 'detect' - } - } -}; diff --git a/ui-packages/.prettierignore b/ui-packages/.prettierignore deleted file mode 100755 index b28b04f643..0000000000 --- a/ui-packages/.prettierignore +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/ui-packages/.prettierrc b/ui-packages/.prettierrc deleted file mode 100755 index bf81c2da51..0000000000 --- a/ui-packages/.prettierrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "semi": true, - "singleQuote": true, - "trailingComma": "none", - "tabWidth": 2, - "useTabs": false, - "bracketSpacing": true -} diff --git a/ui-packages/babel.config.js b/ui-packages/babel.config.js deleted file mode 100644 index 44ed493bc6..0000000000 --- a/ui-packages/babel.config.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -module.exports = { - presets: [ - [ - '@babel/env', - { - modules: 'cjs', - targets: { - node: 'current' - } - } - ], - '@babel/react' - ] -}; diff --git a/ui-packages/clean-frontend.sh b/ui-packages/clean-frontend.sh deleted file mode 100755 index 5cafec5ec4..0000000000 --- a/ui-packages/clean-frontend.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - - -find . -type d -name 'node_modules' -prune -exec rm -rf {} \; -find . -type d -name 'node' -prune -exec rm -rf {} \; -find . -type d -name 'dist' -prune -exec rm -rf {} \; -find . -type f -name 'package-lock.json' -prune -exec rm -rf {} \; -find . -type f -name '*.log' -prune -exec rm -rf {} \; - diff --git a/ui-packages/package.json b/ui-packages/package.json deleted file mode 100644 index 38001af4fe..0000000000 --- a/ui-packages/package.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "name": "kogito-apps", - "version": "0.0.0", - "license": "Apache-2.0", - "private": true, - "scripts": { - "init": "pnpm bootstrap", - "bootstrap": "pnpm install --config.strict-peer-dependencies=false", - "build:prod": "pnpm --filter \"./packages/**\" build:prod", - "test": "pnpm run --filter \"./packages/**\" test:coverage", - "test:it": "pnpm run --filter \"./packages/**\" test:it", - "locktt": "locktt", - "test:e2e:trusty": "pnpm -r -F @kogito-apps/trusty test:e2e:trusty", - "format:all": "pnpm -r format --stream --", - "format:check": "prettier --check .", - "build:prod:swf": "pnpm -r -F @kogito-apps/runtime-tools-dev-ui-webapp... build:prod", - "test:swf": "pnpm --filter @kogito-apps/runtime-tools-dev-ui-webapp... test", - "prepare": "cd .. && husky install" - }, - "devDependencies": { - "@kie-tools/dashbuilder-client": "^0.32.0", - "@kie/lock-treatment-tool": "^0.2.4", - "@types/node": "^18.16.19", - "@typescript-eslint/eslint-plugin": "^4.33.0", - "@typescript-eslint/parser": "^4.33.0", - "eslint": "^7.32.0", - "eslint-config-prettier": "^6.15.0", - "eslint-plugin-cypress": "^2.13.3", - "eslint-plugin-prettier": "^3.4.1", - "eslint-plugin-react": "^7.32.2", - "eslint-plugin-react-hooks": "^4.6.0", - "husky": "^8.0.3", - "lint-staged": "^10.5.4", - "pnpm": "8.7.0", - "prettier": "^2.8.8", - "pretty-quick": "^3.1.3" - }, - "resolutions": { - "@patternfly/react-core": "^4.276.6", - "@types/express-serve-static-core": "^4.17.21", - "@types/minimatch": "^3.0.5", - "axios": "0.21.2", - "d3": "^7.0.0", - "d3-ease": "^3.0.1", - "d3-interpolate": "^3.0.1", - "fast-xml-parser": "^4.2.4", - "minimatch": "^3.0.5", - "@types/react": "17.0.5", - "ua-parser-js": "0.7.35", - "yaml": "2.3.1", - "monaco-editor": "^0.39.0", - "react-monaco-editor": "^0.54.0", - "monaco-yaml": "^4.0.4" - }, - "engines": { - "node": ">=18", - "pnpm": "8.7.0" - }, - "lint-staged": { - "packages/**/*.{ts,tsx}": [ - "pnpm pretty-quick --staged", - "eslint --fix" - ] - }, - "kieTools": { - "requiredPreinstalledCliCommands": [ - "pnpm", - "npm" - ] - } -} diff --git a/ui-packages/packages/cloud-event-form/.eslintrc.json b/ui-packages/packages/cloud-event-form/.eslintrc.json deleted file mode 100644 index d71a6a20d3..0000000000 --- a/ui-packages/packages/cloud-event-form/.eslintrc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parser": "@typescript-eslint/parser", - "rules": { - "@typescript-eslint/no-empty-interface": "off" - } -} \ No newline at end of file diff --git a/ui-packages/packages/cloud-event-form/__mocks__/monacoMock.js b/ui-packages/packages/cloud-event-form/__mocks__/monacoMock.js deleted file mode 100644 index 72fd39ac0a..0000000000 --- a/ui-packages/packages/cloud-event-form/__mocks__/monacoMock.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -export default { - languages: { - register: function (language) {}, - setMonarchTokensProvider: function (name, tokens) {}, - registerCompletionItemProvider: function (name, provider) {} - }, - editor: { - defineTheme: function (name, theme) {} - } -}; diff --git a/ui-packages/packages/cloud-event-form/config/Jest-config/babel-jest-wrapper.js b/ui-packages/packages/cloud-event-form/config/Jest-config/babel-jest-wrapper.js deleted file mode 100644 index de03a7c210..0000000000 --- a/ui-packages/packages/cloud-event-form/config/Jest-config/babel-jest-wrapper.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -module.exports = require('babel-jest').createTransformer({ - rootMode: 'upward' -}); diff --git a/ui-packages/packages/cloud-event-form/config/Jest-config/fileMocks.js b/ui-packages/packages/cloud-event-form/config/Jest-config/fileMocks.js deleted file mode 100755 index 8f4478c812..0000000000 --- a/ui-packages/packages/cloud-event-form/config/Jest-config/fileMocks.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -const path = require('path'); - -module.exports = { - process(src, filename) { - return `module.exports = ${JSON.stringify(path.basename(filename))}`; - } -}; diff --git a/ui-packages/packages/cloud-event-form/config/Jest-config/global-setup.js b/ui-packages/packages/cloud-event-form/config/Jest-config/global-setup.js deleted file mode 100644 index 443daa8b72..0000000000 --- a/ui-packages/packages/cloud-event-form/config/Jest-config/global-setup.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -module.exports = async () => { - process.env.TZ = 'UTC'; -}; diff --git a/ui-packages/packages/cloud-event-form/config/Jest-config/styleMocks.js b/ui-packages/packages/cloud-event-form/config/Jest-config/styleMocks.js deleted file mode 100755 index ab22d88b5b..0000000000 --- a/ui-packages/packages/cloud-event-form/config/Jest-config/styleMocks.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -module.exports = {}; diff --git a/ui-packages/packages/cloud-event-form/config/Jest-config/test-setup.js b/ui-packages/packages/cloud-event-form/config/Jest-config/test-setup.js deleted file mode 100755 index 6835590294..0000000000 --- a/ui-packages/packages/cloud-event-form/config/Jest-config/test-setup.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -require('jest-canvas-mock'); -const { TextDecoder } = require('util'); - -global.TextDecoder = TextDecoder; diff --git a/ui-packages/packages/cloud-event-form/config/Jest-config/test-shim.js b/ui-packages/packages/cloud-event-form/config/Jest-config/test-shim.js deleted file mode 100755 index d9a5ffbee0..0000000000 --- a/ui-packages/packages/cloud-event-form/config/Jest-config/test-shim.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -global.requestAnimationFrame = function (callback) { - setTimeout(callback, 0); -}; diff --git a/ui-packages/packages/cloud-event-form/jest.config.js b/ui-packages/packages/cloud-event-form/jest.config.js deleted file mode 100644 index 850662d208..0000000000 --- a/ui-packages/packages/cloud-event-form/jest.config.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -module.exports = { - setupFiles: [ - './config/Jest-config/test-shim.js', - './config/Jest-config/test-setup.js', - 'core-js' - ], - moduleFileExtensions: ['ts', 'tsx', 'js'], - coveragePathIgnorePatterns: [ - './src/static', - './src/api/', - 'dist/', - './src/envelope/WorkflowFormEnvelope.tsx', - './src/embedded/tests/utils/Mocks.ts', - './src/envelope/tests/mocks/Mocks.ts', - './src/envelope/CloudEventFormEnvelope.tsx', - './src/envelope/index.ts' - ], - coverageReporters: [ - [ - 'lcov', - { - projectRoot: '../../' - } - ] - ], - transformIgnorePatterns: [], - transform: { - '^.+.jsx?$': './config/Jest-config/babel-jest-wrapper.js', - '^.+.(ts|tsx)$': 'ts-jest', - '.(jpg|jpeg|png|svg)$': './config/Jest-config/fileMocks.js' - }, - testMatch: ['**/tests/*.(ts|tsx|js)'], - moduleNameMapper: { - '.(scss|sass|css)$': 'identity-obj-proxy', - 'monaco- editor': '/__mocks__/monacoMock.js' - } -}; diff --git a/ui-packages/packages/cloud-event-form/package.json b/ui-packages/packages/cloud-event-form/package.json deleted file mode 100644 index 0bc493e658..0000000000 --- a/ui-packages/packages/cloud-event-form/package.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "name": "@kogito-apps/cloud-event-form", - "version": "1.0.0", - "description": "Cloud Event Form component.", - "private": true, - "main": "dist/index.js", - "types": "dist/index.d.ts", - "author": "", - "license": "Apache-2.0", - "repository": { - "type": "git", - "url": "https://github.com/kiegroup/kogito-apps.git" - }, - "files": [ - "dist/" - ], - "scripts": { - "precommit": "lint-staged", - "copy:css": "copyfiles -u 1 \"src/**/*.{sass,scss,css,svg,png}\" dist/", - "build": "rimraf dist && tsc -m commonjs -p tsconfig.json && pnpm run copy:css", - "build:prod": "pnpm run clean && pnpm run lint && pnpm run build", - "test:report": "pnpm run test --ci --reporters=jest-junit", - "test": "jest --runInBand --ci --reporters=default --reporters=jest-junit", - "update-snapshot": "jest --updateSnapshot", - "test:coverage": "rimraf coverage && pnpm run test --coverage", - "lint": "eslint './src/**/*.ts{,x}'", - "format": "prettier --config ../../.prettierrc --check --write './src/**/*.{tsx,ts,js}'", - "clean": "rimraf dist" - }, - "dependencies": { - "@kie-tools-core/envelope": "0.32.0", - "@kie-tools-core/envelope-bus": "0.32.0", - "@kogito-apps/components-common": "workspace:*", - "@patternfly/patternfly": "^4.224.2", - "@patternfly/react-code-editor": "^4.82.113", - "@patternfly/react-core": "^4.276.8", - "@patternfly/react-icons": "^4.93.6", - "lodash": "^4.17.21", - "react": "^17.0.2", - "react-dom": "^17.0.2" - }, - "devDependencies": { - "@babel/core": "^7.22.8", - "@kogito-apps/ouia-tools": "workspace:*", - "@testing-library/jest-dom": "^5.16.5", - "@testing-library/react": "^11.2.6", - "@types/jest": "^26.0.24", - "@types/react": "17.0.5", - "@types/react-dom": "^17.0.20", - "babel-jest": "^25.5.1", - "copyfiles": "^2.4.1", - "identity-obj-proxy": "^3.0.0", - "jest": "^26.6.3", - "jest-canvas-mock": "2.5.2", - "jest-junit": "^14.0.1", - "rimraf": "^3.0.2", - "ts-jest": "^26.5.6", - "typescript": "^4.9.5", - "uuid": "^3.4.0", - "waait": "^1.0.5" - } -} diff --git a/ui-packages/packages/cloud-event-form/src/api/CloudEventFormApi.ts b/ui-packages/packages/cloud-event-form/src/api/CloudEventFormApi.ts deleted file mode 100644 index 5f87d55bed..0000000000 --- a/ui-packages/packages/cloud-event-form/src/api/CloudEventFormApi.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -export interface CloudEventFormApi {} diff --git a/ui-packages/packages/cloud-event-form/src/api/CloudEventFormChannelApi.ts b/ui-packages/packages/cloud-event-form/src/api/CloudEventFormChannelApi.ts deleted file mode 100644 index b25dff6010..0000000000 --- a/ui-packages/packages/cloud-event-form/src/api/CloudEventFormChannelApi.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -export const KOGITO_PROCESS_REFERENCE_ID = 'kogitoprocrefid'; -export const KOGITO_BUSINESS_KEY = 'kogitobusinesskey'; - -export enum CloudEventMethod { - POST = 'POST', - PUT = 'PUT' -} - -export interface CloudEventRequest { - endpoint: string; - method: CloudEventMethod; - - headers: CloudEventHeaders; - data: string; -} - -export interface CloudEventHeaders { - type: string; // Type of the cloud event - source: string; // Source of the cloud event - - extensions: Record; -} - -export interface CloudEventFormChannelApi { - cloudEventForm__triggerCloudEvent(event: CloudEventRequest): Promise; -} diff --git a/ui-packages/packages/cloud-event-form/src/api/CloudEventFormDriver.ts b/ui-packages/packages/cloud-event-form/src/api/CloudEventFormDriver.ts deleted file mode 100644 index 720dcff6d5..0000000000 --- a/ui-packages/packages/cloud-event-form/src/api/CloudEventFormDriver.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { CloudEventRequest } from './CloudEventFormChannelApi'; - -/** - * Interface that defines a Driver for CloudEventForm views. - */ -export interface CloudEventFormDriver { - triggerCloudEvent(event: CloudEventRequest): Promise; -} diff --git a/ui-packages/packages/cloud-event-form/src/api/CloudEventFormEnvelopeApi.ts b/ui-packages/packages/cloud-event-form/src/api/CloudEventFormEnvelopeApi.ts deleted file mode 100644 index e1189606b8..0000000000 --- a/ui-packages/packages/cloud-event-form/src/api/CloudEventFormEnvelopeApi.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -export interface CloudEventFormEnvelopeApi { - cloudEventForm__init( - association: Association, - args: CloudEventFormInitArgs - ): Promise; -} - -export interface Association { - origin: string; - envelopeServerId: string; -} - -export type CloudEventFormDefaultValues = { - cloudEventSource?: string; - instanceId?: string; -}; - -export type CloudEventFormInitArgs = { - isNewInstanceEvent: boolean; - defaultValues?: CloudEventFormDefaultValues; -}; diff --git a/ui-packages/packages/cloud-event-form/src/api/index.ts b/ui-packages/packages/cloud-event-form/src/api/index.ts deleted file mode 100644 index 6125f1a122..0000000000 --- a/ui-packages/packages/cloud-event-form/src/api/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -export * from './CloudEventFormApi'; -export * from './CloudEventFormChannelApi'; -export * from './CloudEventFormDriver'; -export * from './CloudEventFormEnvelopeApi'; diff --git a/ui-packages/packages/cloud-event-form/src/embedded/EmbeddedCloudEventForm.tsx b/ui-packages/packages/cloud-event-form/src/embedded/EmbeddedCloudEventForm.tsx deleted file mode 100644 index 550417d6a7..0000000000 --- a/ui-packages/packages/cloud-event-form/src/embedded/EmbeddedCloudEventForm.tsx +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import React, { useCallback } from 'react'; -import { EnvelopeServer } from '@kie-tools-core/envelope-bus/dist/channel'; -import { - EmbeddedEnvelopeProps, - RefForwardingEmbeddedEnvelope -} from '@kie-tools-core/envelope/dist/embedded'; -import { - CloudEventFormApi, - CloudEventFormChannelApi, - CloudEventFormEnvelopeApi, - CloudEventFormDriver -} from '../api'; -import { init } from '../envelope'; -import { ContainerType } from '@kie-tools-core/envelope/dist/api'; -import { EmbeddedCloudEventFormChannelApiImpl } from './EmbeddedCloudEventFormChannelApiImpl'; - -export interface EmbeddedCloudEventFormProps { - targetOrigin: string; - driver: CloudEventFormDriver; - isNewInstanceEvent?: boolean; - defaultValues?: { - cloudEventSource?: string; - instanceId?: string; - }; -} - -export const EmbeddedCloudEventForm = React.forwardRef( - ( - props: EmbeddedCloudEventFormProps, - forwardedRef: React.Ref - ) => { - const refDelegate = useCallback( - ( - envelopeServer: EnvelopeServer< - CloudEventFormChannelApi, - CloudEventFormEnvelopeApi - > - ): CloudEventFormApi => ({}), - [] - ); - const pollInit = useCallback( - ( - envelopeServer: EnvelopeServer< - CloudEventFormChannelApi, - CloudEventFormEnvelopeApi - >, - container: () => HTMLDivElement - ) => { - init({ - config: { - containerType: ContainerType.DIV, - envelopeId: envelopeServer.id - }, - container: container(), - bus: { - postMessage(message, targetOrigin, transfer) { - window.postMessage(message, targetOrigin, transfer); - } - } - }); - return envelopeServer.envelopeApi.requests.cloudEventForm__init( - { - origin: envelopeServer.origin, - envelopeServerId: envelopeServer.id - }, - { - isNewInstanceEvent: props.isNewInstanceEvent ?? true, - defaultValues: props.defaultValues - } - ); - }, - [] - ); - return ( - - ); - } -); - -const EmbeddedCloudEventFormEnvelope = React.forwardRef< - CloudEventFormApi, - EmbeddedEnvelopeProps< - CloudEventFormChannelApi, - CloudEventFormEnvelopeApi, - CloudEventFormApi - > ->(RefForwardingEmbeddedEnvelope); diff --git a/ui-packages/packages/cloud-event-form/src/embedded/EmbeddedCloudEventFormChannelApiImpl.ts b/ui-packages/packages/cloud-event-form/src/embedded/EmbeddedCloudEventFormChannelApiImpl.ts deleted file mode 100644 index 22f86d8b59..0000000000 --- a/ui-packages/packages/cloud-event-form/src/embedded/EmbeddedCloudEventFormChannelApiImpl.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { - CloudEventRequest, - CloudEventFormChannelApi, - CloudEventFormDriver -} from '../api'; - -export class EmbeddedCloudEventFormChannelApiImpl - implements CloudEventFormChannelApi -{ - constructor(private readonly driver: CloudEventFormDriver) {} - - cloudEventForm__triggerCloudEvent(event: CloudEventRequest): Promise { - return this.driver.triggerCloudEvent(event); - } -} diff --git a/ui-packages/packages/cloud-event-form/src/embedded/index.ts b/ui-packages/packages/cloud-event-form/src/embedded/index.ts deleted file mode 100644 index af78c5d26f..0000000000 --- a/ui-packages/packages/cloud-event-form/src/embedded/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -export * from './EmbeddedCloudEventForm'; -export * from './EmbeddedCloudEventFormChannelApiImpl'; diff --git a/ui-packages/packages/cloud-event-form/src/embedded/tests/EmbeddedCloudEventForm.test.tsx b/ui-packages/packages/cloud-event-form/src/embedded/tests/EmbeddedCloudEventForm.test.tsx deleted file mode 100644 index c816d587b8..0000000000 --- a/ui-packages/packages/cloud-event-form/src/embedded/tests/EmbeddedCloudEventForm.test.tsx +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { render } from '@testing-library/react'; -import React from 'react'; -import { EmbeddedCloudEventForm } from '../EmbeddedCloudEventForm'; -import { MockedCloudEventFormDriver } from './utils/Mocks'; - -jest.mock('../../envelope/components/CloudEventForm/CloudEventForm'); - -describe('EmbeddedCloudEventForm tests', () => { - it('Snapshot', () => { - const props = { - targetOrigin: 'origin', - driver: new MockedCloudEventFormDriver() - }; - - const container = render().container; - - expect(container).toMatchSnapshot(); - - const contentDiv = container.querySelector('div'); - - expect(contentDiv).toBeTruthy(); - }); - - it('Snapshot - isNewInstanceEvent', () => { - const props = { - targetOrigin: 'origin', - driver: new MockedCloudEventFormDriver(), - isNewInstanceEvent: true - }; - - const container = render().container; - - expect(container).toMatchSnapshot(); - - const contentDiv = container.querySelector('div'); - - expect(contentDiv).toBeTruthy(); - }); - - it('Snapshot - defaultValue', () => { - const props = { - targetOrigin: 'origin', - driver: new MockedCloudEventFormDriver(), - defaultValues: { - cloudEventSource: '/local/source', - instanceId: '1234' - } - }; - - const container = render().container; - - expect(container).toMatchSnapshot(); - - const contentDiv = container.querySelector('div'); - - expect(contentDiv).toBeTruthy(); - }); -}); diff --git a/ui-packages/packages/cloud-event-form/src/embedded/tests/EmbeddedCloudEventFormChannelApiImpl.test.tsx b/ui-packages/packages/cloud-event-form/src/embedded/tests/EmbeddedCloudEventFormChannelApiImpl.test.tsx deleted file mode 100644 index f422aaf0f3..0000000000 --- a/ui-packages/packages/cloud-event-form/src/embedded/tests/EmbeddedCloudEventFormChannelApiImpl.test.tsx +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { - CloudEventFormChannelApi, - CloudEventFormDriver, - CloudEventMethod, - CloudEventRequest -} from '../../api'; -import { MockedCloudEventFormDriver } from './utils/Mocks'; -import { EmbeddedCloudEventFormChannelApiImpl } from '../EmbeddedCloudEventFormChannelApiImpl'; - -let driver: CloudEventFormDriver; -let api: CloudEventFormChannelApi; - -describe('EmbeddedCloudEventFormChannelApiImpl tests', () => { - beforeEach(() => { - jest.clearAllMocks(); - driver = new MockedCloudEventFormDriver(); - api = new EmbeddedCloudEventFormChannelApiImpl(driver); - }); - - it('cloudEventForm__triggerCloudEvent', () => { - const eventRequest: CloudEventRequest = { - method: CloudEventMethod.POST, - endpoint: '/', - headers: { - type: 'test', - source: 'test', - extensions: {} - }, - data: '' - }; - - api.cloudEventForm__triggerCloudEvent(eventRequest); - expect(driver.triggerCloudEvent).toHaveBeenCalledWith(eventRequest); - }); -}); diff --git a/ui-packages/packages/cloud-event-form/src/embedded/tests/__snapshots__/EmbeddedCloudEventForm.test.tsx.snap b/ui-packages/packages/cloud-event-form/src/embedded/tests/__snapshots__/EmbeddedCloudEventForm.test.tsx.snap deleted file mode 100644 index be49aa998d..0000000000 --- a/ui-packages/packages/cloud-event-form/src/embedded/tests/__snapshots__/EmbeddedCloudEventForm.test.tsx.snap +++ /dev/null @@ -1,19 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`EmbeddedCloudEventForm tests Snapshot - defaultValue 1`] = ` -
-
-
-`; - -exports[`EmbeddedCloudEventForm tests Snapshot - isNewInstanceEvent 1`] = ` -
-
-
-`; - -exports[`EmbeddedCloudEventForm tests Snapshot 1`] = ` -
-
-
-`; diff --git a/ui-packages/packages/cloud-event-form/src/embedded/tests/utils/Mocks.ts b/ui-packages/packages/cloud-event-form/src/embedded/tests/utils/Mocks.ts deleted file mode 100644 index 8884a3f1ca..0000000000 --- a/ui-packages/packages/cloud-event-form/src/embedded/tests/utils/Mocks.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { CloudEventFormDriver } from '../../../api'; - -export const MockedCloudEventFormDriver = jest.fn( - () => ({ - triggerCloudEvent: jest.fn() - }) -); diff --git a/ui-packages/packages/cloud-event-form/src/envelope/CloudEventFormEnvelope.tsx b/ui-packages/packages/cloud-event-form/src/envelope/CloudEventFormEnvelope.tsx deleted file mode 100644 index 17a83a0c86..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/CloudEventFormEnvelope.tsx +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import * as React from 'react'; -import * as ReactDOM from 'react-dom'; -import { EnvelopeBus } from '@kie-tools-core/envelope-bus/dist/api'; -import { Envelope, EnvelopeDivConfig } from '@kie-tools-core/envelope'; -import { CloudEventFormChannelApi, CloudEventFormEnvelopeApi } from '../api'; - -import './styles.css'; -import { - CloudEventFormEnvelopeView, - CloudEventFormEnvelopeViewApi -} from './CloudEventFormEnvelopeView'; -import { CloudEventFormEnvelopeApiImpl } from './CloudEventFormEnvelopeApiImpl'; - -/** - * Function that starts an Envelope application. - * - * @param args.container: The HTML element in which the WorkflowForm will render - * @param args.bus: The implementation of a `bus` that knows how to send messages to the Channel. - * - */ -export function init(args: { - config: EnvelopeDivConfig; - container: HTMLDivElement; - bus: EnvelopeBus; -}) { - /** - * Creates a new generic Envelope, typed with the right interfaces. - */ - const envelope = new Envelope< - CloudEventFormEnvelopeApi, - CloudEventFormChannelApi, - CloudEventFormEnvelopeViewApi, - undefined - >(args.bus, args.config); - - /** - * Function that knows how to render a CloudEventForm. - * In this case, it's a React application, but any other framework can be used. - * - * Returns a Promise<() => CloudEventFormEnvelopeViewApi> that can be used in CloudEventFormEnvelopeApiImpl. - */ - const envelopeViewDelegate = async () => { - const ref = React.createRef(); - return new Promise<() => CloudEventFormEnvelopeViewApi>((res) => { - args.container.className = 'kogito-cloud-event-form-container'; - ReactDOM.render( - , - args.container, - () => res(() => ref.current) - ); - }); - }; - - return envelope.start(envelopeViewDelegate, undefined, { - create: (apiFactoryArgs) => - new CloudEventFormEnvelopeApiImpl(apiFactoryArgs) - }); -} diff --git a/ui-packages/packages/cloud-event-form/src/envelope/CloudEventFormEnvelopeApiImpl.ts b/ui-packages/packages/cloud-event-form/src/envelope/CloudEventFormEnvelopeApiImpl.ts deleted file mode 100644 index eaa0e1773b..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/CloudEventFormEnvelopeApiImpl.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { EnvelopeApiFactoryArgs } from '@kie-tools-core/envelope'; -import { - Association, - CloudEventFormChannelApi, - CloudEventFormEnvelopeApi, - CloudEventFormInitArgs -} from '../api'; -import { CloudEventFormEnvelopeViewApi } from './CloudEventFormEnvelopeView'; - -/** - * Implementation of the CloudEventFormEnvelopeApi - */ -export class CloudEventFormEnvelopeApiImpl - implements CloudEventFormEnvelopeApi -{ - private view: () => CloudEventFormEnvelopeViewApi; - private capturedInitRequestYet = false; - - constructor( - private readonly args: EnvelopeApiFactoryArgs< - CloudEventFormEnvelopeApi, - CloudEventFormChannelApi, - CloudEventFormEnvelopeViewApi, - undefined - > - ) {} - - private hasCapturedInitRequestYet() { - return this.capturedInitRequestYet; - } - - private ackCapturedInitRequest() { - this.capturedInitRequestYet = true; - } - - cloudEventForm__init = async ( - association: Association, - args: CloudEventFormInitArgs - ): Promise => { - this.args.envelopeClient.associate( - association.origin, - association.envelopeServerId - ); - - if (this.hasCapturedInitRequestYet()) { - return; - } - this.ackCapturedInitRequest(); - this.view = await this.args.viewDelegate(); - this.view().initialize(args); - }; -} diff --git a/ui-packages/packages/cloud-event-form/src/envelope/CloudEventFormEnvelopeView.tsx b/ui-packages/packages/cloud-event-form/src/envelope/CloudEventFormEnvelopeView.tsx deleted file mode 100644 index 5bdad7aaff..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/CloudEventFormEnvelopeView.tsx +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import React, { - useEffect, - useImperativeHandle, - useMemo, - useState -} from 'react'; -import { Bullseye } from '@patternfly/react-core/dist/js/layouts/Bullseye'; -import { - componentOuiaProps, - OUIAProps -} from '@kogito-apps/ouia-tools/dist/utils/OuiaUtils'; -import { KogitoSpinner } from '@kogito-apps/components-common/dist/components/KogitoSpinner'; -import { MessageBusClientApi } from '@kie-tools-core/envelope-bus/dist/api'; -import { - CloudEventFormChannelApi, - CloudEventFormDefaultValues, - CloudEventFormInitArgs -} from '../api'; -import '@patternfly/patternfly/patternfly.css'; -import CloudEventForm from './components/CloudEventForm/CloudEventForm'; -import { CloudEventFormEnvelopeViewDriver } from './CloudEventFormEnvelopeViewDriver'; - -export interface CloudEventFormEnvelopeViewApi { - initialize: (args: CloudEventFormInitArgs) => void; -} - -interface Props { - channelApi: MessageBusClientApi; -} - -export const CloudEventFormEnvelopeView = React.forwardRef< - CloudEventFormEnvelopeViewApi, - Props & OUIAProps ->(({ channelApi, ouiaId }, forwardedRef) => { - const [isLoading, setIsLoading] = useState(true); - const [isEnvelopeConnectedToChannel, setEnvelopeConnectedToChannel] = - useState(false); - const [isNewInstanceEvent, setIsNewInstanceEvent] = useState(false); - const [defaultValues, setDefaultValues] = - useState(); - - useImperativeHandle( - forwardedRef, - () => ({ - initialize: (args) => { - setEnvelopeConnectedToChannel(true); - setIsNewInstanceEvent(args.isNewInstanceEvent); - setDefaultValues(args.defaultValues); - } - }), - [] - ); - - useEffect(() => { - setIsLoading(false); - }, [isEnvelopeConnectedToChannel]); - - const driver = useMemo( - () => new CloudEventFormEnvelopeViewDriver(channelApi), - [channelApi] - ); - - if (isLoading) { - return ( - - - - ); - } - - return ( - - ); -}); - -export default CloudEventFormEnvelopeView; diff --git a/ui-packages/packages/cloud-event-form/src/envelope/CloudEventFormEnvelopeViewDriver.tsx b/ui-packages/packages/cloud-event-form/src/envelope/CloudEventFormEnvelopeViewDriver.tsx deleted file mode 100644 index 34d881b3b0..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/CloudEventFormEnvelopeViewDriver.tsx +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { MessageBusClientApi } from '@kie-tools-core/envelope-bus/dist/api'; -import { - CloudEventRequest, - CloudEventFormChannelApi, - CloudEventFormDriver -} from '../api'; - -export class CloudEventFormEnvelopeViewDriver implements CloudEventFormDriver { - constructor( - private readonly channelApi: MessageBusClientApi - ) {} - - triggerCloudEvent(event: CloudEventRequest): Promise { - return this.channelApi.requests.cloudEventForm__triggerCloudEvent(event); - } -} diff --git a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventCustomHeadersEditor/CloudEventCustomHeadersEditor.tsx b/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventCustomHeadersEditor/CloudEventCustomHeadersEditor.tsx deleted file mode 100644 index a0420e5929..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventCustomHeadersEditor/CloudEventCustomHeadersEditor.tsx +++ /dev/null @@ -1,190 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import React, { - useCallback, - useEffect, - useImperativeHandle, - useState -} from 'react'; -import uuidv4 from 'uuid'; -import { Button } from '@patternfly/react-core/dist/js/components/Button'; -import { Grid, GridItem } from '@patternfly/react-core/dist/js/layouts/Grid'; -import { Stack, StackItem } from '@patternfly/react-core/dist/js/layouts/Stack'; -import { TextInput } from '@patternfly/react-core/dist/js/components/TextInput'; -import { PlusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/plus-circle-icon'; -import { TrashIcon } from '@patternfly/react-icons/dist/esm/icons/trash-icon'; -import { - componentOuiaProps, - OUIAProps -} from '@kogito-apps/ouia-tools/dist/utils/OuiaUtils'; - -export interface CloudEventCustomHeadersEditorApi { - reset(): void; - getCustomHeaders(): Record; -} - -type CustomHeaderEntry = { - uuid: string; - key: string; - value: string; -}; - -const CloudEventCustomHeadersEditor = React.forwardRef< - CloudEventCustomHeadersEditorApi, - OUIAProps ->(({ ouiaId, ouiaSafe }, forwardedRef) => { - const [headers, setHeaders] = useState([]); - const [isNewHeader, setIsNewHeader] = useState(false); - - useEffect(() => { - setIsNewHeader(false); - }, [isNewHeader]); - - useImperativeHandle( - forwardedRef, - () => { - return { - reset(): void { - setHeaders([]); - setIsNewHeader(false); - }, - getCustomHeaders(): Record { - const result = {}; - headers - .filter((entry) => entry.key && entry.value) - .forEach((entry) => { - result[entry.key] = entry.value; - }); - return result; - } - }; - }, - [headers] - ); - - const addNewHeader = useCallback(() => { - const headersCopy = [...headers]; - headersCopy.push({ - uuid: uuidv4(), - key: '', - value: '' - }); - setHeaders(headersCopy); - setIsNewHeader(true); - }, [headers]); - - const deleteHeader = useCallback( - (index: number) => { - const headersCopy = [...headers]; - headersCopy.splice(index, 1); - setHeaders(headersCopy); - }, - [headers] - ); - - const updateHeaderKey = useCallback( - (index: number, value: string) => { - const headersCopy = [...headers]; - headersCopy[index].key = value; - setHeaders(headersCopy); - }, - [headers] - ); - - const updateHeaderValue = useCallback( - (index: number, value: string) => { - const headersCopy = [...headers]; - headersCopy[index].value = value; - setHeaders(headersCopy); - }, - [headers] - ); - - return ( -
- - - - - {headers.length > 0 && ( - - - -

Header Name

-
- -

Value

-
- {headers.map((header, index) => { - return ( - - - updateHeaderKey(index, value)} - autoFocus={isNewHeader && index === headers.length - 1} - data-testid="update-key" - /> - - - updateHeaderValue(index, value)} - data-testid="update-value" - /> - - - - - - ); - })} -
-
- )} -
-
- ); -}); - -export default CloudEventCustomHeadersEditor; diff --git a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventCustomHeadersEditor/__mocks__/CloudEventCustomHeadersEditor.tsx b/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventCustomHeadersEditor/__mocks__/CloudEventCustomHeadersEditor.tsx deleted file mode 100644 index 3bf3c367d1..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventCustomHeadersEditor/__mocks__/CloudEventCustomHeadersEditor.tsx +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import React from 'react'; -import { CloudEventCustomHeadersEditorApi } from '../CloudEventCustomHeadersEditor'; -import { OUIAProps } from '@kogito-apps/ouia-tools/dist/utils/OuiaUtils'; - -const MockedCloudEventCustomHeadersEditor = React.forwardRef< - CloudEventCustomHeadersEditorApi, - OUIAProps ->((props, ref) => { - return <>; -}); - -export default MockedCloudEventCustomHeadersEditor; diff --git a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventCustomHeadersEditor/tests/CloudEventCustomHeadersEditor.test.ts.tsx b/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventCustomHeadersEditor/tests/CloudEventCustomHeadersEditor.test.ts.tsx deleted file mode 100644 index 99a46ff35d..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventCustomHeadersEditor/tests/CloudEventCustomHeadersEditor.test.ts.tsx +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import React from 'react'; -import { fireEvent, render, screen } from '@testing-library/react'; -import { act } from 'react-dom/test-utils'; -import CloudEventCustomHeadersEditor, { - CloudEventCustomHeadersEditorApi -} from '../CloudEventCustomHeadersEditor'; - -jest.mock('uuid', () => { - let count = 0; - return () => count++; -}); - -const MockedComponent = (): React.ReactElement => { - return <>; -}; - -jest.mock('@patternfly/react-icons/dist/esm/icons/plus-circle-icon', () => - Object.assign({}, jest.requireActual('@patternfly/react-icons'), { - PlusCircleIcon: () => { - return ; - } - }) -); - -jest.mock('@patternfly/react-icons/dist/esm/icons/trash-icon', () => - Object.assign({}, jest.requireActual('@patternfly/react-icons'), { - TrashIcon: () => { - return ; - } - }) -); - -function addHeader( - container, - index: number, - header?: string, - headerValue?: string -) { - const addButton = screen.getByText('Add Header'); - - expect(addButton).toBeTruthy(); - - act(() => { - fireEvent.click(addButton); - }); - - const headerField = container.querySelector( - `[id = header-key-${index}-input]` - ); - expect(headerField).toBeTruthy(); - const valueField = container.querySelector( - `[id = header-value-${index}-input]` - ); - expect(valueField).toBeTruthy(); - const deleteButton = screen.getAllByLabelText('delete')[index]; - expect(deleteButton).toBeTruthy(); - - fireEvent.change(screen.getAllByTestId('update-key')[index], { - target: { value: header } - }); - fireEvent.change(screen.getAllByTestId('update-value')[index], { - target: { value: headerValue } - }); - return container; -} - -function deleteHeader(container, index: number) { - const deleteButton = screen.getAllByLabelText('delete')[index]; - expect(deleteButton).toBeTruthy(); - - act(() => { - fireEvent.click(deleteButton); - }); - - return container; -} - -describe('CloudEventCustomHeadersEditor tests', () => { - it('Snapshot - empty', () => { - let container; - act(() => { - container = render().container; - }); - - expect(container).toMatchSnapshot(); - - expect(screen.getByText('Add Header')).toBeTruthy(); - }); - - it('Add headers', () => { - const editorApiRef = React.createRef(); - let { container } = render( - - ); - - expect(screen.getByText('Add Header')).toBeTruthy(); - - container = addHeader(container, 0, 'key', 'value'); - - expect(container).toMatchSnapshot(); - - expect(container.querySelectorAll('input')).toHaveLength(2); - - container = addHeader(container, 1, 'key2', 'value2'); - addHeader(container, 2); - - const result = editorApiRef.current.getCustomHeaders(); - - expect(Object.keys(result)).toHaveLength(2); - expect(result).toHaveProperty('key', 'value'); - expect(result).toHaveProperty('key2', 'value2'); - }); - - it('Remove headers', () => { - const editorApiRef = React.createRef(); - let { container } = render( - - ); - - container = addHeader(container, 0, 'key', 'value'); - container = addHeader(container, 1, 'key2', 'value2'); - container = addHeader(container, 2, 'key3', 'value3'); - - let result = editorApiRef.current.getCustomHeaders(); - - expect(Object.keys(result)).toHaveLength(3); - - expect(result).toHaveProperty('key', 'value'); - expect(result).toHaveProperty('key2', 'value2'); - expect(result).toHaveProperty('key3', 'value3'); - - container = deleteHeader(container, 1); - - result = editorApiRef.current.getCustomHeaders(); - - expect(Object.keys(result)).toHaveLength(2); - - expect(result).toHaveProperty('key', 'value'); - expect(result).not.toHaveProperty('key2', 'value2'); - expect(result).toHaveProperty('key3', 'value3'); - - container = deleteHeader(container, 0); - - result = editorApiRef.current.getCustomHeaders(); - - expect(Object.keys(result)).toHaveLength(1); - expect(result).not.toHaveProperty('key', 'value'); - expect(result).not.toHaveProperty('key2', 'value2'); - expect(result).toHaveProperty('key3', 'value3'); - - result = editorApiRef.current.getCustomHeaders(); - - expect(result).not.toHaveProperty('key', 'value'); - expect(result).not.toHaveProperty('key2', 'value2'); - }); -}); diff --git a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventCustomHeadersEditor/tests/__snapshots__/CloudEventCustomHeadersEditor.test.ts.tsx.snap b/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventCustomHeadersEditor/tests/__snapshots__/CloudEventCustomHeadersEditor.test.ts.tsx.snap deleted file mode 100644 index 953c9f0a34..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventCustomHeadersEditor/tests/__snapshots__/CloudEventCustomHeadersEditor.test.ts.tsx.snap +++ /dev/null @@ -1,131 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`CloudEventCustomHeadersEditor tests Add headers 1`] = ` -
-
-
-
- -
-
-
-
-

- Header Name -

-
-
-

- Value -

-
-
- -
-
- -
-
-
-
-
-
-
-
-`; - -exports[`CloudEventCustomHeadersEditor tests Snapshot - empty 1`] = ` -
-
-
-
- -
-
-
-
-`; diff --git a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventFieldLabelIcon/CloudEventFieldLabelIcon.tsx b/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventFieldLabelIcon/CloudEventFieldLabelIcon.tsx deleted file mode 100644 index eba06a6610..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventFieldLabelIcon/CloudEventFieldLabelIcon.tsx +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import React from 'react'; -import { HelpIcon } from '@patternfly/react-icons/dist/esm/icons/help-icon'; -import { Popover } from '@patternfly/react-core/dist/js/components/Popover'; - -export interface CloudEventFieldLabelIconProps { - fieldId: string; - helpMessage: string | JSX.Element; - cloudEventHeader?: string; -} - -const CloudEventFieldLabelIcon: React.FC = ({ - fieldId, - helpMessage, - cloudEventHeader -}) => { - return ( - -
{helpMessage}
- {cloudEventHeader && ( -
- The value will be set in the{' '} - {`'${cloudEventHeader}'`}{' '} - header. -
- )} -
- } - > - - - ); -}; - -export default CloudEventFieldLabelIcon; diff --git a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventFieldLabelIcon/__mocks__/CloudEventFieldLabelIcon.tsx b/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventFieldLabelIcon/__mocks__/CloudEventFieldLabelIcon.tsx deleted file mode 100644 index 01f3839560..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventFieldLabelIcon/__mocks__/CloudEventFieldLabelIcon.tsx +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import React from 'react'; - -const MockedCloudEventFieldLabelIcon = (): React.ReactElement => { - return <>; -}; - -export default MockedCloudEventFieldLabelIcon; diff --git a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventFieldLabelIcon/tests/CloudEventFieldLabelIcon.test.tsx b/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventFieldLabelIcon/tests/CloudEventFieldLabelIcon.test.tsx deleted file mode 100644 index 745520d2a4..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventFieldLabelIcon/tests/CloudEventFieldLabelIcon.test.tsx +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import React from 'react'; -import { render } from '@testing-library/react'; -import CloudEventFieldLabelIcon from '../CloudEventFieldLabelIcon'; - -const MockedComponent = (): React.ReactElement => { - return <>; -}; - -jest.mock('@patternfly/react-icons/dist/esm/icons/help-icon', () => - Object.assign({}, jest.requireActual('@patternfly/react-icons'), { - HelpIcon: () => { - return ; - } - }) -); - -jest.mock('@patternfly/react-core/dist/js/components/Popover', () => - Object.assign({}, jest.requireActual('@patternfly/react-icons'), { - Popover: () => { - return ; - } - }) -); - -describe('CloudEventFieldLabelIcon tests', () => { - it('default snapshot test', () => { - const container = render( - - ).container; - - expect(container).toMatchSnapshot(); - }); - - it('default snapshot test - with header', () => { - const container = render( - - ).container; - - expect(container).toMatchSnapshot(); - }); -}); diff --git a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventFieldLabelIcon/tests/__snapshots__/CloudEventFieldLabelIcon.test.tsx.snap b/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventFieldLabelIcon/tests/__snapshots__/CloudEventFieldLabelIcon.test.tsx.snap deleted file mode 100644 index 8be4878b9a..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventFieldLabelIcon/tests/__snapshots__/CloudEventFieldLabelIcon.test.tsx.snap +++ /dev/null @@ -1,5 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`CloudEventFieldLabelIcon tests default snapshot test - with header 1`] = `
`; - -exports[`CloudEventFieldLabelIcon tests default snapshot test 1`] = `
`; diff --git a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventForm/CloudEventForm.tsx b/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventForm/CloudEventForm.tsx deleted file mode 100644 index cc467938b8..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventForm/CloudEventForm.tsx +++ /dev/null @@ -1,371 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { - componentOuiaProps, - OUIAProps -} from '@kogito-apps/ouia-tools/dist/utils/OuiaUtils'; -import { - CloudEventFormDefaultValues, - CloudEventFormDriver, - CloudEventMethod, - KOGITO_BUSINESS_KEY, - KOGITO_PROCESS_REFERENCE_ID -} from '../../../api'; -import { ActionListGroup } from '@patternfly/react-core/dist/js/components/ActionList'; -import { - Select, - SelectOption, - SelectVariant -} from '@patternfly/react-core/dist/js/components/Select'; -import { TextInput } from '@patternfly/react-core/dist/js/components/TextInput'; -import { Button } from '@patternfly/react-core/dist/js/components/Button'; -import { - Form, - FormGroup -} from '@patternfly/react-core/dist/js/components/Form'; -import { InputGroup } from '@patternfly/react-core/dist/js/components/InputGroup'; -import { ValidatedOptions } from '@patternfly/react-core/dist/js/helpers'; -import { - CodeEditor, - Language -} from '@patternfly/react-code-editor/dist/js/components/CodeEditor'; -import ExclamationCircleIcon from '@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon'; -import { - FormValidations, - validateCloudEventRequest -} from './validateCloudEventRequest'; -import CloudEventCustomHeadersEditor, { - CloudEventCustomHeadersEditorApi -} from '../CloudEventCustomHeadersEditor/CloudEventCustomHeadersEditor'; -import CloudEventFieldLabelIcon from '../CloudEventFieldLabelIcon/CloudEventFieldLabelIcon'; - -export interface CloudEventFormProps { - driver: CloudEventFormDriver; - isNewInstanceEvent?: boolean; - defaultValues?: CloudEventFormDefaultValues; -} - -export const CloudEventForm: React.FC = ({ - driver, - isNewInstanceEvent, - defaultValues, - ouiaId, - ouiaSafe -}) => { - const [validationState, setValidationState] = useState(); - - const customHeadersEditorApi = useRef(); - - const [isMethodOpen, setIsMethodOpen] = useState(false); - const [method, setMethod] = useState(CloudEventMethod.POST); - const [endpoint, setEndpoint] = useState('/'); - const [instanceId, setInstanceId] = useState(''); - const [businessKey, setBusinessKey] = useState(''); - const [eventType, setEventType] = useState(''); - const [eventSource, setEventSource] = useState('/from/form'); - const [eventData, setEventData] = useState(''); - - const resetForm = useCallback(() => { - setMethod(CloudEventMethod.POST); - setEndpoint('/'); - setEventType(''); - setEventSource(defaultValues?.cloudEventSource ?? '/from/form'); - setEventData(''); - setInstanceId(defaultValues?.instanceId ?? ''); - setBusinessKey(''); - customHeadersEditorApi?.current.reset(); - }, [defaultValues]); - - useEffect(() => { - setEventSource(defaultValues?.cloudEventSource ?? '/from/form'); - setInstanceId(defaultValues?.instanceId ?? ''); - }, [defaultValues]); - - const getValidatedOption = useCallback( - (fieldId: string): ValidatedOptions => { - return getValidationMessage(fieldId) - ? ValidatedOptions.error - : ValidatedOptions.default; - }, - [validationState] - ); - - const getValidationMessage = useCallback( - (fieldId: string): string => { - return validationState && validationState.getFieldValidation(fieldId); - }, - [validationState] - ); - - const doTrigger = useCallback(() => { - const extensions = { - ...customHeadersEditorApi?.current.getCustomHeaders() - }; - - if (isNewInstanceEvent) { - businessKey && (extensions[KOGITO_BUSINESS_KEY] = businessKey); - } else { - instanceId && (extensions[KOGITO_PROCESS_REFERENCE_ID] = instanceId); - } - - const eventRequest = { - endpoint: endpoint, - method: method, - data: eventData, - headers: { - type: eventType, - source: eventSource, - extensions - } - }; - - const validations = validateCloudEventRequest(eventRequest); - - setValidationState(validations); - - if (!validations.isValid()) { - return; - } - - driver.triggerCloudEvent(eventRequest).then((response) => { - resetForm(); - }); - }, [ - method, - endpoint, - eventType, - eventSource, - eventData, - instanceId, - businessKey - ]); - - return ( -
-
- } - validated={getValidatedOption('endpoint')} - labelIcon={ - - } - > - - - - - - } - validated={getValidatedOption('eventType')} - labelIcon={ - - } - > - - - - } - fieldId="eventSource" - > - - - {!isNewInstanceEvent && ( - - } - > - - - )} - {isNewInstanceEvent && ( - - } - > - - - )} - -

- Sets the custom headers that will be added into the Cloud - Event. -

-

- Press the{' '} - Add Header button - to start adding new headers. -

-

- Headers with empty Name won't be added into the Cloud - Event. -

-
- } - /> - } - > - - - } - validated={getValidatedOption('eventData')} - labelIcon={ - - } - > - - - - - - - -
- ); -}; - -export default CloudEventForm; diff --git a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventForm/__mocks__/CloudEventForm.tsx b/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventForm/__mocks__/CloudEventForm.tsx deleted file mode 100644 index 6027064328..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventForm/__mocks__/CloudEventForm.tsx +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import React from 'react'; - -const MockedCloudEventForm = (): React.ReactElement => { - return <>; -}; - -export default MockedCloudEventForm; diff --git a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventForm/tests/CloudEventForm.test.tsx b/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventForm/tests/CloudEventForm.test.tsx deleted file mode 100644 index d1f827107a..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventForm/tests/CloudEventForm.test.tsx +++ /dev/null @@ -1,244 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import React from 'react'; -import CloudEventForm from '../CloudEventForm'; -import { fireEvent, render, screen } from '@testing-library/react'; -import { CloudEventFormDriver } from '../../../../api'; -import { CloudEventCustomHeadersEditorApi } from '../../CloudEventCustomHeadersEditor/CloudEventCustomHeadersEditor'; -import { act } from 'react-dom/test-utils'; -import wait from 'waait'; - -const MockedComponent = (): React.ReactElement => { - return <>; -}; - -jest.mock('@patternfly/react-code-editor/dist/js/components/CodeEditor', () => - Object.assign({}, jest.requireActual('@patternfly/react-code-editor'), { - CodeEditor: () => { - return ; - }, - Language: () => { - return { - json: 'json' - }; - } - }) -); - -jest.mock('@patternfly/react-icons', () => - Object.assign({}, jest.requireActual('@patternfly/react-icons'), { - ExclamationCircleIcon: () => { - return ; - } - }) -); - -jest.mock('@patternfly/react-core/dist/js/components/Select', () => - Object.assign({}, jest.requireActual('@patternfly/react-core'), { - Select: () => { - return ; - }, - SelectOption: () => { - return ; - }, - SelectVariant: { - single: 'single' - } - }) -); - -jest.mock('@patternfly/react-core/dist/js/components/TextInput', () => - Object.assign({}, jest.requireActual('@patternfly/react-core'), { - TextInput: () => { - return ; - } - }) -); - -jest.mock('../../CloudEventFieldLabelIcon/CloudEventFieldLabelIcon'); - -const driver: CloudEventFormDriver = { - triggerCloudEvent: jest.fn() -}; - -const headersEditorApi: CloudEventCustomHeadersEditorApi = { - reset: jest.fn(), - getCustomHeaders: jest.fn() -}; - -const headers = { - header1: 'value1', - header2: 'value2' -}; - -jest.spyOn(React, 'useRef').mockReturnValue({ - current: { reset: jest.fn(), getCustomHeaders: jest.fn() } -}); - -const triggerCloudEventSpy = jest - .spyOn(driver, 'triggerCloudEvent') - .mockReturnValue(Promise.resolve()); -jest.spyOn(headersEditorApi, 'getCustomHeaders').mockReturnValue(headers); - -describe('CloudEventForm tests', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('Snapshot - new instance', () => { - const { container } = render( - - ); - - expect(container.querySelector('[for="endpoint"]')).toBeTruthy(); - expect(container.querySelector('[for="eventType"]')).toBeTruthy(); - expect(container.querySelector('[for="eventSource"]')).toBeTruthy(); - expect(container.querySelector('[for="instanceId"]')).toBeFalsy(); - expect(container.querySelector('[for="businessKey"]')).toBeTruthy(); - expect(container).toMatchSnapshot(); - }); - - it('Snapshot - new instance - default values', () => { - const { container } = render( - - ); - - expect(container.querySelector('[for="eventSource"]')).toBeTruthy(); - - expect(container.querySelector('[for="instanceId"]')).toBeFalsy(); - - expect(container).toMatchSnapshot(); - }); - - it('Snapshot - send cloud event', () => { - const { container } = render(); - - expect(container.querySelector('[for="endpoint"]')).toBeTruthy(); - expect(container.querySelector('[for="eventType"]')).toBeTruthy(); - expect(container.querySelector('[for="eventSource"]')).toBeTruthy(); - expect(container.querySelector('[for="instanceId"]')).toBeTruthy(); - expect(container.querySelector('[for="businessKey"]')).toBeFalsy(); - - expect(container).toMatchSnapshot(); - }); - - it('Snapshot - send cloud event - default values', () => { - const { container } = render( - - ); - - expect(container.querySelector('[for="eventSource"]')).toBeTruthy(); - - expect(container.querySelector('[for="instanceId"]')).toBeTruthy(); - - expect(container).toMatchSnapshot(); - }); - - it('Trigger - Validation failure', () => { - const { container } = render( - - ); - expect(container.querySelector('[for="endpoint"]')).toBeTruthy(); - expect(container.querySelector('[for="eventType"]')).toBeTruthy(); - - fireEvent.click(screen.getByText('Trigger')); - - expect(headersEditorApi.reset).not.toHaveBeenCalled(); - expect(driver.triggerCloudEvent).not.toHaveBeenCalled(); - }); - - it('Trigger - success', async () => { - const { container } = render( - - ); - expect(container.querySelector('[for="eventType"]')).toBeTruthy(); - - expect( - container.querySelector( - '[data-ouia-component-type="custom-headers-editor"]' - ) - ).toBeTruthy(); - - const triggerButton = screen.getByText('Trigger'); - - expect(triggerButton).toBeTruthy(); - - fireEvent.click(screen.getByText('Trigger')); - }); - - it('Reset', async () => { - const { container } = render( - - ); - expect(container.querySelector('[for="endpoint"]')).toBeTruthy(); - expect(container.querySelector('[for="eventType"]')).toBeTruthy(); - expect( - container.querySelector( - '[data-ouia-component-type="custom-headers-editor"]' - ) - ).toBeTruthy(); - - await act(async () => { - fireEvent.click(screen.getByText('Reset')); - wait(); - }); - - expect(container.querySelector('[for="endpoint"]')).toBeTruthy(); - expect(container.querySelector('[for="eventType"]')).toBeTruthy(); - }); - - it('Reset- without default value', async () => { - const { container } = render(); - expect(container.querySelector('[for="endpoint"]')).toBeTruthy(); - expect(container.querySelector('[for="eventType"]')).toBeTruthy(); - expect( - container.querySelector( - '[data-ouia-component-type="custom-headers-editor"]' - ) - ).toBeTruthy(); - await act(async () => { - fireEvent.click(screen.getByText('Reset')); - wait(); - }); - }); -}); diff --git a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventForm/tests/__snapshots__/CloudEventForm.test.tsx.snap b/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventForm/tests/__snapshots__/CloudEventForm.test.tsx.snap deleted file mode 100644 index 4a38b3eac9..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventForm/tests/__snapshots__/CloudEventForm.test.tsx.snap +++ /dev/null @@ -1,961 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`CloudEventForm tests Snapshot - new instance - default values 1`] = ` -
-
-
-
-
- - -
-
-
- -
-
-
-
- - -
-
- -
-
-
-
- - -
-
- -
-
-
-
- - -
-
- -
-
-
-
- - -
-
-
-
-
- -
-
-
- -
-
-
-
- - -
-
- -
-
-
- - -
- -
-
-`; - -exports[`CloudEventForm tests Snapshot - new instance 1`] = ` -
-
-
-
-
- - -
-
-
- -
-
-
-
- - -
-
- -
-
-
-
- - -
-
- -
-
-
-
- - -
-
- -
-
-
-
- - -
-
-
-
-
- -
-
-
- -
-
-
-
- - -
-
- -
-
-
- - -
- -
-
-`; - -exports[`CloudEventForm tests Snapshot - send cloud event - default values 1`] = ` -
-
-
-
-
- - -
-
-
- -
-
-
-
- - -
-
- -
-
-
-
- - -
-
- -
-
-
-
- - -
-
- -
-
-
-
- - -
-
-
-
-
- -
-
-
- -
-
-
-
- - -
-
- -
-
-
- - -
- -
-
-`; - -exports[`CloudEventForm tests Snapshot - send cloud event 1`] = ` -
-
-
-
-
- - -
-
-
- -
-
-
-
- - -
-
- -
-
-
-
- - -
-
- -
-
-
-
- - -
-
- -
-
-
-
- - -
-
-
-
-
- -
-
-
- -
-
-
-
- - -
-
- -
-
-
- - -
- -
-
-`; diff --git a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventForm/tests/validateCloudEventRequest.test.ts b/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventForm/tests/validateCloudEventRequest.test.ts deleted file mode 100644 index 3d6c29ee1f..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventForm/tests/validateCloudEventRequest.test.ts +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { validateCloudEventRequest } from '../validateCloudEventRequest'; -import { CloudEventMethod } from '../../../../api'; - -describe('validateCloudEventRequest tests', () => { - it('Valid result', () => { - const validation = validateCloudEventRequest({ - method: CloudEventMethod.POST, - endpoint: '/', - data: '{"name": "Jon Snow"}', - headers: { - type: 'any', - source: 'any', - extensions: {} - } - }); - - expect(validation.isValid()).toBeTruthy(); - }); - - it('Invalid result - endpoint', () => { - const validation = validateCloudEventRequest({ - method: CloudEventMethod.POST, - endpoint: '', - data: '{"name": "Jon Snow"}', - headers: { - type: 'any', - source: 'any', - extensions: {} - } - }); - - expect(validation.isValid()).toBeFalsy(); - expect(validation.getFieldValidation('endpoint')).not.toBeUndefined(); - expect(validation.getFieldValidation('eventType')).toBeUndefined(); - expect(validation.getFieldValidation('eventData')).toBeUndefined(); - }); - - it('Invalid result - event type', () => { - const validation = validateCloudEventRequest({ - method: CloudEventMethod.POST, - endpoint: '/', - data: '{"name": "Jon Snow"}', - headers: { - type: '', - source: 'any', - extensions: {} - } - }); - - expect(validation.isValid()).toBeFalsy(); - expect(validation.getFieldValidation('endpoint')).toBeUndefined(); - expect(validation.getFieldValidation('eventType')).not.toBeUndefined(); - expect(validation.getFieldValidation('eventData')).toBeUndefined(); - }); - - it('Invalid result - event data', () => { - const eventRequest = { - method: CloudEventMethod.POST, - endpoint: '/', - data: 'this should break because is not a json string', - headers: { - type: 'any', - source: 'any', - extensions: {} - } - }; - - let validation = validateCloudEventRequest(eventRequest); - - expect(validation.isValid()).toBeFalsy(); - expect(validation.getFieldValidation('endpoint')).toBeUndefined(); - expect(validation.getFieldValidation('eventType')).toBeUndefined(); - expect(validation.getFieldValidation('eventData')).not.toBeUndefined(); - - eventRequest.data = - '"a string is a valid json value but not a valid payload"'; - - validation = validateCloudEventRequest(eventRequest); - - expect(validation.isValid()).toBeFalsy(); - expect(validation.getFieldValidation('endpoint')).toBeUndefined(); - expect(validation.getFieldValidation('eventType')).toBeUndefined(); - expect(validation.getFieldValidation('eventData')).not.toBeUndefined(); - }); -}); diff --git a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventForm/validateCloudEventRequest.ts b/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventForm/validateCloudEventRequest.ts deleted file mode 100644 index 1eb475a8d9..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/components/CloudEventForm/validateCloudEventRequest.ts +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import isPlainObject from 'lodash/isPlainObject'; -import { CloudEventRequest } from '../../../api'; - -export interface FormValidations { - isValid(): boolean; - - getFieldValidation(fieldId: string): undefined | string; -} - -class FormValidationsImpl implements FormValidations { - constructor(private readonly validations: Record) {} - - getFieldValidation(fieldId: string): string | undefined { - return this.validations[fieldId]; - } - - isValid(): boolean { - return Object.keys(this.validations).length == 0; - } -} - -export function validateCloudEventRequest( - eventRequest: CloudEventRequest -): FormValidations { - const validations = {}; - - if (!eventRequest.endpoint) { - validations['endpoint'] = 'The Cloud Event endpoint cannot be empty.'; - } - - if (!eventRequest.headers.type) { - validations['eventType'] = 'The Cloud Event type cannot be empty.'; - } - - if (eventRequest.data) { - try { - const json = JSON.parse(eventRequest.data); - if (!isPlainObject(json)) { - throw new Error('not an object'); - } - } catch (err) { - validations['eventData'] = - 'The Cloud Event data should have a JSON format.'; - } - } - - return new FormValidationsImpl(validations); -} diff --git a/ui-packages/packages/cloud-event-form/src/envelope/index.ts b/ui-packages/packages/cloud-event-form/src/envelope/index.ts deleted file mode 100644 index a8f7138141..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -export * from './CloudEventFormEnvelope'; diff --git a/ui-packages/packages/cloud-event-form/src/envelope/styles.css b/ui-packages/packages/cloud-event-form/src/envelope/styles.css deleted file mode 100644 index 3d5f73b6f2..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/styles.css +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -.kogito-cloud-event-form-container { - height: 100%; -} diff --git a/ui-packages/packages/cloud-event-form/src/envelope/tests/CloudEventFormEnvelopeApiImpl.test.tsx b/ui-packages/packages/cloud-event-form/src/envelope/tests/CloudEventFormEnvelopeApiImpl.test.tsx deleted file mode 100644 index 0dafc24cdc..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/tests/CloudEventFormEnvelopeApiImpl.test.tsx +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { - MockedCloudEventFormEnvelopeViewApi, - MockedEnvelopeClientDefinition -} from './mocks/Mocks'; -import { EnvelopeApiFactoryArgs } from '@kie-tools-core/envelope'; -import { EnvelopeClient } from '@kie-tools-core/envelope-bus/dist/envelope'; -import { CloudEventFormChannelApi, CloudEventFormEnvelopeApi } from '../../api'; -import { CloudEventFormEnvelopeViewApi } from '../CloudEventFormEnvelopeView'; -import { CloudEventFormEnvelopeApiImpl } from '../CloudEventFormEnvelopeApiImpl'; - -describe('CloudEventFormEnvelopeApiImpl tests', () => { - it('initialize', async () => { - const envelopeClient = new MockedEnvelopeClientDefinition(); - const view = new MockedCloudEventFormEnvelopeViewApi(); - - const args: EnvelopeApiFactoryArgs< - CloudEventFormEnvelopeApi, - CloudEventFormChannelApi, - CloudEventFormEnvelopeViewApi, - undefined - > = { - envelopeClient: envelopeClient as EnvelopeClient< - CloudEventFormEnvelopeApi, - CloudEventFormChannelApi - >, - envelopeContext: undefined, - viewDelegate: () => Promise.resolve(() => view) - }; - - const envelopeApi = new CloudEventFormEnvelopeApiImpl(args); - - envelopeApi.cloudEventForm__init( - { - envelopeServerId: 'envelopeServerId', - origin: 'origin' - }, - { - isNewInstanceEvent: true, - defaultValues: { - cloudEventSource: '/local/test', - instanceId: '1234' - } - } - ); - - expect(envelopeClient.associate).toHaveBeenCalledWith( - 'origin', - 'envelopeServerId' - ); - const calledView = await view.initialize; - expect(calledView).toHaveBeenCalled(); - }); -}); diff --git a/ui-packages/packages/cloud-event-form/src/envelope/tests/CloudEventFormEnvelopeView.test.tsx b/ui-packages/packages/cloud-event-form/src/envelope/tests/CloudEventFormEnvelopeView.test.tsx deleted file mode 100644 index 0e4e0aa152..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/tests/CloudEventFormEnvelopeView.test.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import React from 'react'; -import { render } from '@testing-library/react'; -import { act } from 'react-dom/test-utils'; -import CloudEventFormEnvelopeView, { - CloudEventFormEnvelopeViewApi -} from '../CloudEventFormEnvelopeView'; -import { MockedMessageBusClientApi } from './mocks/Mocks'; - -describe('CloudEventFormEnvelopeView tests', () => { - beforeAll(() => { - Object.defineProperty(window, 'matchMedia', { - writable: true, - value: jest.fn().mockImplementation((query) => ({ - matches: false, - media: query, - onchange: null, - addEventListener: jest.fn(), - removeEventListener: jest.fn(), - dispatchEvent: jest.fn() - })) - }); - }); - it('Snapshot', () => { - const channelApi = new MockedMessageBusClientApi(); - - const forwardRef = React.createRef(); - - const container = render( - - ).container; - - expect(container).toMatchSnapshot(); - - act(() => { - if (forwardRef.current) { - forwardRef.current.initialize({ - isNewInstanceEvent: true, - defaultValues: { - cloudEventSource: '/local/test', - instanceId: '1234' - } - }); - } - }); - - const checkWorkflowForm = container.querySelector( - '[data-ouia-component-type="workflow-form"]' - ); - expect(checkWorkflowForm).toBeTruthy(); - }); -}); diff --git a/ui-packages/packages/cloud-event-form/src/envelope/tests/CloudEventFormEnvelopeViewDriver.test.ts b/ui-packages/packages/cloud-event-form/src/envelope/tests/CloudEventFormEnvelopeViewDriver.test.ts deleted file mode 100755 index 1dbe0068fd..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/tests/CloudEventFormEnvelopeViewDriver.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { - MessageBusClientApi, - RequestPropertyNames -} from '@kie-tools-core/envelope-bus/dist/api'; -import { MockedMessageBusClientApi } from './mocks/Mocks'; -import { - CloudEventFormChannelApi, - CloudEventMethod, - CloudEventRequest -} from '../../api'; -import { CloudEventFormEnvelopeViewDriver } from '../CloudEventFormEnvelopeViewDriver'; - -let channelApi: MessageBusClientApi; -let requests: Pick< - CloudEventFormChannelApi, - RequestPropertyNames ->; -let driver: CloudEventFormEnvelopeViewDriver; - -describe('CloudEventFormEnvelopeViewDriver tests', () => { - beforeEach(() => { - jest.clearAllMocks(); - channelApi = new MockedMessageBusClientApi(); - requests = channelApi.requests; - driver = new CloudEventFormEnvelopeViewDriver(channelApi); - }); - - describe('Requests', () => { - it('trigger cloud event', () => { - const eventRequest: CloudEventRequest = { - method: CloudEventMethod.POST, - endpoint: '/', - headers: { - type: 'test', - source: 'test', - extensions: {} - }, - data: '' - }; - driver.triggerCloudEvent(eventRequest); - - expect(requests.cloudEventForm__triggerCloudEvent).toHaveBeenCalledWith( - eventRequest - ); - }); - }); -}); diff --git a/ui-packages/packages/cloud-event-form/src/envelope/tests/__snapshots__/CloudEventFormEnvelopeView.test.tsx.snap b/ui-packages/packages/cloud-event-form/src/envelope/tests/__snapshots__/CloudEventFormEnvelopeView.test.tsx.snap deleted file mode 100644 index 939859f1cf..0000000000 --- a/ui-packages/packages/cloud-event-form/src/envelope/tests/__snapshots__/CloudEventFormEnvelopeView.test.tsx.snap +++ /dev/null @@ -1,689 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`CloudEventFormEnvelopeView tests Snapshot 1`] = ` -
-
-
-
-
- - - -
-
-
-
- -
- -
- -
-
-
-
- - - -
-
- - -
-
-
-
- - - -
-
- - -
-
-
-
- - - -
-
- - -
-
-
-
- - - -
-
-
-
-
- -
-
-
- -
-
-
-
- - - -
-
- -
-
-
-
-
-
-
-
-
-
-