package jnpf.limsService.provider; import jnpf.exception.DataException; import jnpf.limsService.LimsTestWorkItemSnapshotService; import lombok.Data; import lombok.AllArgsConstructor; import lombok.Value; import javax.sql.DataSource; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.function.Supplier; /** Reads persisted executable definitions; never accepts physical mappings from a caller. */ public class LimsElnReadonlyResolver { private final LimsElnSourceCatalog catalog; private final LimsElnSourceExecution execution; private final Supplier actors; public LimsElnReadonlyResolver(LimsElnSourceCatalog catalog, DataSource readOnlyDataSource, LimsTestWorkItemSnapshotService taskAccess, Supplier actors) { this.catalog = catalog; this.execution = new LimsElnSourceExecution(readOnlyDataSource, taskAccess); this.actors = actors; } public List resolve(List itemIds, List properties) { return resolve(itemIds, properties, actors.get()); } List resolve(List itemIds, List properties, LimsElnSourceCatalog.Actor actor) { LimsElnSourceCatalog.requireActor(actor); if (properties == null || properties.size() > 50) throw new DataException("Source request exceeds limits"); if (properties.stream().anyMatch(ref -> ref == null || ref.getRevision() < 1 || ref.getPropertyCode() == null || !ref.getPropertyCode().matches("[a-zA-Z][a-zA-Z0-9_.-]{0,79}"))) { throw new DataException("Invalid source request"); } List versions = new ArrayList<>(); for (PropertyRef property : new LinkedHashSet<>(properties)) { versions.add(catalog.published(property.getPropertyCode(), property.getRevision(), actor)); } return execution.execute(itemIds, versions, actor); } @Value public static class PropertyRef { String propertyCode; int revision; } public static class SourceFailure extends DataException { private final String code; public SourceFailure(String code, String message) { super(message); this.code = code; } public String getCode() { return code; } } @Value @AllArgsConstructor @com.fasterxml.jackson.databind.annotation.JsonNaming(com.fasterxml.jackson.databind.PropertyNamingStrategies.LowerCamelCaseStrategy.class) public static class Snapshot { String sourceRef; String propertyCode; int propertyRevision; String dataType; String outcomeCode; @com.fasterxml.jackson.databind.annotation.JsonSerialize(using = LimsElnSourceScalarSerializer.class) Object value; String acquiredAt; String lineage; String reasonCode; String formattedValue; public Snapshot(String sourceRef, String propertyCode, int propertyRevision, String dataType, String outcomeCode, Object value, String acquiredAt, String lineage) { this(sourceRef, propertyCode, propertyRevision, dataType, outcomeCode, value, acquiredAt, lineage, null, null); } } @Data public static class FrozenDefinition { private String datasetCode; private int datasetRevision; private String schema; private String table; private String idColumn; private String tenantColumn; private String deleteColumn; private Map fields; private String resultField; private List joins = new ArrayList<>(); private List conditions = new ArrayList<>(); private String scopeMode = "ALL"; private Map> columnIdentities; } @Data public static class Field { private String column; private String dataType; private int step; private String valueMode = "RAW"; private Map nameOptions; private String format = "RAW"; } @Data public static class Join { private String schema; private String table; private String tenantColumn; private int fromStep; private String fromColumn; private String targetColumn; private String deleteColumn; private String qualifierColumn; private String qualifierValue; } @Data public static class Condition { private String field; private String operator; private List values = new ArrayList<>(); } }