io.yupiik.batch.ui.backend.JobExecutions Maven / Gradle / Ivy
/*
* Copyright (c) 2021 - Yupiik SAS - https://www.yupiik.com
* Licensed 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 io.yupiik.batch.ui.backend;
import io.yupiik.batch.runtime.util.Substitutor;
import io.yupiik.batch.ui.backend.configuration.Configuration;
import io.yupiik.batch.ui.backend.model.Job;
import io.yupiik.batch.ui.backend.model.Page;
import io.yupiik.batch.ui.backend.model.Status;
import io.yupiik.batch.ui.backend.model.Step;
import io.yupiik.batch.ui.backend.sql.IteratingResultset;
import io.yupiik.uship.jsonrpc.core.api.JsonRpc;
import io.yupiik.uship.jsonrpc.core.api.JsonRpcMethod;
import io.yupiik.uship.jsonrpc.core.api.JsonRpcParam;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import static java.util.Comparator.comparing;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toList;
@JsonRpc
@ApplicationScoped
public class JobExecutions {
@Inject
private DataSource dataSource;
@Inject
private Configuration configuration;
private String countAllJobs;
private String findLastExecutions;
@PostConstruct
private void init() {
final var simpleQueryInterpolator = new Substitutor(key -> switch (key) {
case "table" -> configuration.getJobTable();
default -> throw new IllegalStateException("Unknown key '" + key + "'");
});
countAllJobs = simpleQueryInterpolator.replace(configuration.getCountAllJobs());
findLastExecutions = simpleQueryInterpolator.replace(configuration.getFindLastExecutions());
}
@JsonRpcMethod(name = "yupiik-batch-executions", documentation = "Returns the paginated executions, not that steps are not populated.")
public Page findJobs(@JsonRpcParam(required = true, documentation = "Page to fetch.") final int page,
@JsonRpcParam(required = true, documentation = "Size of the page (max being 100).") final int pageSize) throws SQLException {
final int actualPageSize = Math.max(0, Math.min(50, pageSize));
try (final var connection = dataSource.getConnection();
final var countStmt = connection.createStatement();
final var itemStmt = connection.createStatement();
final var countResult = countStmt.executeQuery(countAllJobs);
// no real SQL injection possible so a plain statement is fine
final var itemResultSet = itemStmt.executeQuery(new Substitutor(key -> switch (key) {
case "table" -> configuration.getJobTable();
case "pageSize" -> String.valueOf(actualPageSize);
case "firstIndex" -> String.valueOf(actualPageSize * page);
case "lastIndex" -> String.valueOf(actualPageSize * (1 + page));
default -> throw new IllegalStateException("Unknown key '" + key + "'");
}).replace(configuration.getFindAllJobs()))) {
final long total = countResult.next() ? countResult.getLong(1) : 0;
final var items = IteratingResultset.toList(itemResultSet, r -> new Job(
r.getString(1),
r.getString(2),
ofNullable(r.getString(3)).map(Status::valueOf).orElse(null),
r.getString(4),
r.getObject(5, OffsetDateTime.class).withOffsetSameInstant(ZoneOffset.UTC),
r.getObject(6, OffsetDateTime.class).withOffsetSameInstant(ZoneOffset.UTC),
null));
return new Page<>(total, items);
}
}
@JsonRpcMethod(name = "yupiik-batch-last-executions", documentation = "Returns the last execution of each batch to be able to build an overview page.")
public Page findLastJobs() throws SQLException {
try (final var connection = dataSource.getConnection();
final var stmt = connection.createStatement();
final var result = stmt.executeQuery(findLastExecutions)) {
final var items = IteratingResultset.toList(result, r -> new Job(
r.getString(1),
r.getString(2),
ofNullable(r.getString(3)).map(Status::valueOf).orElse(null),
r.getString(4),
r.getObject(5, OffsetDateTime.class).withOffsetSameInstant(ZoneOffset.UTC),
r.getObject(6, OffsetDateTime.class).withOffsetSameInstant(ZoneOffset.UTC),
null));
return new Page<>(items.size(), items);
}
}
@JsonRpcMethod(name = "yupiik-batch-execution", documentation = "Returns the related job with its steps populated.")
public Job findJobById(@JsonRpcParam(required = true, documentation = "Job to fetch.") final String id) throws SQLException {
try (final var connection = dataSource.getConnection();
final var jobResultSetStmt = connection.prepareStatement(new Substitutor(key -> switch (key) {
case "table" -> configuration.getJobTable();
default -> throw new IllegalStateException("Unknown key '" + key + "'");
}).replace(configuration.getFindJobById()))) {
jobResultSetStmt.setString(1, id);
try (final var jobResultSet = jobResultSetStmt.executeQuery()) {
if (!jobResultSet.next()) {
throw new IllegalArgumentException("No job #" + id + " found.");
}
try (final var stepStmt = connection.prepareStatement(new Substitutor(key -> switch (key) {
case "table" -> configuration.getStepTable();
default -> throw new IllegalStateException("Unknown key '" + key + "'");
}).replace(configuration.getFindStepsByJobId()))) {
stepStmt.setString(1, id);
try (final var stepResultSet = stepStmt.executeQuery()) {
final var steps = IteratingResultset.toList(stepResultSet, r -> new Step(
r.getString(1),
r.getString(2),
ofNullable(r.getString(3)).map(Status::valueOf).orElse(null),
r.getString(4),
r.getObject(5, OffsetDateTime.class).withOffsetSameInstant(ZoneOffset.UTC),
r.getObject(6, OffsetDateTime.class).withOffsetSameInstant(ZoneOffset.UTC),
r.getString(7)));
return new Job(
jobResultSet.getString(1),
jobResultSet.getString(2),
ofNullable(jobResultSet.getString(3)).map(Status::valueOf).orElse(null),
jobResultSet.getString(4),
jobResultSet.getObject(5, OffsetDateTime.class).withOffsetSameInstant(ZoneOffset.UTC),
jobResultSet.getObject(6, OffsetDateTime.class).withOffsetSameInstant(ZoneOffset.UTC),
steps.stream().sorted(comparing(Step::started)).collect(toList()));
}
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy