package jnpf.limsController;
|
|
import cn.dev33.satoken.annotation.SaCheckLogin;
|
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
|
import com.fasterxml.jackson.databind.annotation.JsonNaming;
|
import jnpf.base.ActionResult;
|
import jnpf.exception.DataException;
|
import jnpf.limsService.provider.LimsElnReadonlyResolver.SourceFailure;
|
import jnpf.limsService.provider.LimsElnReadonlyResolver;
|
import jnpf.limsService.provider.LimsElnDatasetCatalog;
|
import jnpf.limsService.provider.LimsElnDataItemManager;
|
import jnpf.limsService.provider.LimsElnSourceCatalog;
|
import jnpf.limsService.provider.LimsElnSourceMetadata;
|
import jnpf.limsService.provider.LimsElnPreviewSampleCatalog;
|
import lombok.Data;
|
import org.springframework.web.bind.annotation.*;
|
|
/** Independent configuration endpoints; business task endpoints remain unchanged. */
|
@RestController
|
@RequestMapping("/eln/sources")
|
@SaCheckLogin
|
public class LimsElnSourceController {
|
private final LimsElnSourceMetadata metadata;
|
private final LimsElnDatasetCatalog datasets;
|
private final LimsElnDataItemManager dataItems;
|
private final LimsElnPreviewSampleCatalog previewSamples;
|
private final LimsElnSourceCatalog properties;
|
private final LimsElnReadonlyResolver resolver;
|
|
public LimsElnSourceController(LimsElnSourceMetadata metadata, LimsElnDatasetCatalog datasets,
|
LimsElnDataItemManager dataItems, LimsElnPreviewSampleCatalog previewSamples,
|
LimsElnSourceCatalog properties, LimsElnReadonlyResolver resolver) {
|
this.metadata = metadata;
|
this.datasets = datasets;
|
this.dataItems = dataItems;
|
this.previewSamples = previewSamples;
|
this.properties = properties;
|
this.resolver = resolver;
|
}
|
|
@GetMapping("/dataItems")
|
public ActionResult<?> dataItems() { return ok(dataItems.items()); }
|
|
@GetMapping("/sourcePaths")
|
public ActionResult<?> sourcePaths() { return ok(dataItems.sourcePaths()); }
|
|
@PostMapping("/sourcePaths")
|
public ActionResult<?> createSourcePath(@RequestBody LimsElnDataItemManager.SourcePathDraft command) {
|
return ok(dataItems.createSourcePath(command));
|
}
|
|
@GetMapping("/previewSamples")
|
public ActionResult<?> previewSamples(@RequestParam(value = "keyword", required = false) String keyword) {
|
return ok(previewSamples.search(keyword));
|
}
|
|
@PostMapping("/dataItems")
|
public ActionResult<?> createDataItem(@RequestBody LimsElnDataItemManager.DataItemDraft command) {
|
return ok(dataItems.create(command));
|
}
|
|
@PutMapping("/dataItems/{id}")
|
public ActionResult<?> saveDataItem(@PathVariable("id") String id,
|
@RequestBody LimsElnDataItemManager.DataItemDraft command) {
|
return ok(dataItems.save(id, command));
|
}
|
|
@PostMapping("/dataItems/{id}/preview")
|
public ActionResult<?> previewDataItem(@PathVariable("id") String id, @RequestBody Publication command) {
|
return ok(dataItems.preview(id, command.getSampleId()));
|
}
|
|
@PostMapping("/dataItems/{id}/publish")
|
public ActionResult<?> publishDataItem(@PathVariable("id") String id, @RequestBody Publication command) {
|
return ok(dataItems.publish(id, editVersion(command.getExpectedEditVersion()), command.getSampleId()));
|
}
|
|
@GetMapping("/objects")
|
public ActionResult<?> objects() { return ok(metadata.objects()); }
|
|
@GetMapping("/objects/{id}/fields")
|
public ActionResult<?> fields(@PathVariable("id") String id) { return ok(metadata.fields(id)); }
|
|
@GetMapping("/objects/{id}/fields/{fieldId}/options")
|
public ActionResult<?> options(@PathVariable("id") String id, @PathVariable("fieldId") String fieldId) {
|
return ok(metadata.options(id, fieldId));
|
}
|
|
@GetMapping("/enumObjects")
|
public ActionResult<?> enumObjects() { return ok(metadata.enumObjects()); }
|
|
@PostMapping("/enumOptions")
|
public ActionResult<?> enumOptions(@RequestBody LimsElnSourceMetadata.EnumSelection selection) {
|
return ok(metadata.enumOptions(selection));
|
}
|
|
@GetMapping("/datasets")
|
public ActionResult<?> datasets() { return ok(datasets.versions()); }
|
|
@GetMapping("/datasets/{id}")
|
public ActionResult<?> dataset(@PathVariable("id") String id) { return ok(datasets.detail(id)); }
|
|
@PutMapping("/datasets/{id}")
|
public ActionResult<?> updateDataset(@PathVariable("id") String id, @RequestBody DatasetEdit command) {
|
return ok(datasets.updateDraft(id, editVersion(command.getExpectedEditVersion()), command.getDraft()));
|
}
|
|
@PostMapping("/datasets/{id}/versions")
|
public ActionResult<?> newDatasetVersion(@PathVariable("id") String id, @RequestBody LimsElnDatasetCatalog.Draft draft) {
|
return ok(datasets.newVersion(id, draft));
|
}
|
|
@PostMapping("/datasets")
|
public ActionResult<?> createDataset(@RequestBody LimsElnDatasetCatalog.Draft draft) {
|
return ok(datasets.createDraft(draft));
|
}
|
|
@PostMapping("/datasets/{id}/validate")
|
public ActionResult<?> validateDataset(@PathVariable("id") String id) {
|
return ok(datasets.validateStructure(id));
|
}
|
|
@PostMapping("/datasets/{id}/preview")
|
public ActionResult<?> previewDataset(@PathVariable("id") String id, @RequestBody Publication command) {
|
return ok(datasets.preview(id, command.getSampleId()));
|
}
|
|
@PostMapping("/datasets/{id}/publish")
|
public ActionResult<?> publishDataset(@PathVariable("id") String id, @RequestBody Publication command) {
|
return ok(datasets.publish(id, editVersion(command.getExpectedEditVersion()), command.getSampleId()));
|
}
|
|
@GetMapping("/properties")
|
public ActionResult<?> properties() { return ok(properties.versions()); }
|
|
@GetMapping("/properties/{id}")
|
public ActionResult<?> property(@PathVariable("id") String id) { return ok(properties.detail(id)); }
|
|
@PutMapping("/properties/{id}")
|
public ActionResult<?> updateProperty(@PathVariable("id") String id, @RequestBody PropertyEdit command) {
|
if (command.getDraft() == null) throw new DataException("Source draft is required");
|
return ok(properties.updateDraft(id, editVersion(command.getExpectedEditVersion()), command.getDraft().draft()));
|
}
|
|
@PostMapping("/properties/{id}/versions")
|
public ActionResult<?> newPropertyVersion(@PathVariable("id") String id, @RequestBody PropertyDraft command) {
|
return ok(properties.newVersion(id, command.draft()));
|
}
|
|
@GetMapping("/properties/{code}/enabled")
|
public ActionResult<?> propertyEnabled(@PathVariable("code") String code) { return ok(properties.enabled(code)); }
|
|
@PutMapping("/properties/{code}/enabled")
|
public ActionResult<?> setPropertyEnabled(@PathVariable("code") String code, @RequestBody Enabled command) {
|
if (command.getEnabled() == null) throw new DataException("Enabled state is required");
|
properties.setEnabled(code, command.getEnabled());
|
return ok(command.getEnabled());
|
}
|
|
@GetMapping("/catalog")
|
public ActionResult<?> catalog() { return ok(properties.catalog()); }
|
|
@GetMapping("/references/{code}/{revision}")
|
public ActionResult<?> reference(@PathVariable("code") String code, @PathVariable("revision") int revision) {
|
return ok(properties.reference(code, revision));
|
}
|
|
@GetMapping("/references")
|
public ActionResult<?> references() { return ok(properties.references()); }
|
|
@PostMapping("/properties")
|
public ActionResult<?> createProperty(@RequestBody PropertyDraft command) {
|
return ok(properties.createDraft(command.draft()));
|
}
|
|
@PostMapping("/properties/{id}/publish")
|
public ActionResult<?> publishProperty(@PathVariable("id") String id, @RequestBody Publication command) {
|
return ok(properties.publish(id, editVersion(command.getExpectedEditVersion()), command.getSampleId()));
|
}
|
|
@PostMapping("/resolve")
|
public ActionResult<?> resolve(@RequestBody Resolve command) {
|
if (command.getProperties() == null || command.getProperties().size() > 50
|
|| command.getProperties().stream().anyMatch(ref -> ref == null || ref.getRevision() == null)) {
|
throw new DataException("Invalid source references");
|
}
|
return ok(resolver.resolve(command.getTestWorkItemIds(), command.getProperties().stream()
|
.map(ref -> new LimsElnReadonlyResolver.PropertyRef(ref.getPropertyCode(), ref.getRevision()))
|
.collect(java.util.stream.Collectors.toList())));
|
}
|
|
private static int editVersion(Integer value) {
|
if (value == null || value < 0) throw new DataException("Expected edit version is required");
|
return value;
|
}
|
|
private static ActionResult<?> ok(Object data) { return ActionResult.success("OK", data); }
|
|
@ExceptionHandler(DataException.class)
|
public ActionResult<?> sourceFailure(DataException failure) {
|
String errorCode = failure instanceof SourceFailure ? ((SourceFailure) failure).getCode() : "SOURCE_INVALID";
|
int status = "SOURCE_FORBIDDEN".equals(errorCode) ? 403 : "SOURCE_UNAVAILABLE".equals(errorCode) ? 503
|
: "SOURCE_CONFLICT".equals(errorCode) ? 409 : 400;
|
ActionResult<java.util.Map<String, String>> response = ActionResult.fail(status, failure.getMessage());
|
response.setData(java.util.Collections.singletonMap("errorCode", errorCode));
|
return response;
|
}
|
|
@ExceptionHandler(org.springframework.dao.DataAccessException.class)
|
public ActionResult<?> databaseFailure(org.springframework.dao.DataAccessException failure) {
|
Throwable cause = failure.getMostSpecificCause();
|
String state = cause instanceof java.sql.SQLException ? ((java.sql.SQLException) cause).getSQLState() : null;
|
if ("42501".equals(state)) return sourceFailure(new SourceFailure("SOURCE_FORBIDDEN", "Source database access denied"));
|
if (state != null && (state.startsWith("42") || state.startsWith("22"))) {
|
return sourceFailure(new SourceFailure("SOURCE_INVALID", "Source configuration is no longer readable"));
|
}
|
if ("57014".equals(state)) return sourceFailure(new SourceFailure("SOURCE_LIMIT", "Source query timed out"));
|
return sourceFailure(new SourceFailure("SOURCE_UNAVAILABLE", "Source database is unavailable"));
|
}
|
|
@ExceptionHandler(org.springframework.http.converter.HttpMessageNotReadableException.class)
|
public ActionResult<?> invalidRequest() {
|
return sourceFailure(new SourceFailure("SOURCE_INVALID", "Invalid source request"));
|
}
|
|
@Data
|
@JsonNaming(PropertyNamingStrategies.LowerCamelCaseStrategy.class)
|
public static class Publication {
|
private Integer expectedEditVersion;
|
private String sampleId;
|
}
|
|
@Data
|
@JsonNaming(PropertyNamingStrategies.LowerCamelCaseStrategy.class)
|
public static class DatasetEdit {
|
private Integer expectedEditVersion;
|
private LimsElnDatasetCatalog.Draft draft;
|
}
|
|
@Data
|
@JsonNaming(PropertyNamingStrategies.LowerCamelCaseStrategy.class)
|
public static class PropertyEdit {
|
private Integer expectedEditVersion;
|
private PropertyDraft draft;
|
}
|
|
@Data
|
public static class Enabled {
|
private Boolean enabled;
|
}
|
|
@Data
|
@JsonNaming(PropertyNamingStrategies.LowerCamelCaseStrategy.class)
|
public static class Resolve {
|
private java.util.List<String> testWorkItemIds;
|
private java.util.List<PropertyRef> properties;
|
}
|
|
@Data
|
@JsonNaming(PropertyNamingStrategies.LowerCamelCaseStrategy.class)
|
public static class PropertyRef {
|
private String propertyCode;
|
private Integer revision;
|
}
|
|
@Data
|
@JsonNaming(PropertyNamingStrategies.LowerCamelCaseStrategy.class)
|
public static class PropertyDraft {
|
private String propertyCode;
|
private String displayName;
|
private String dataType;
|
private LimsElnSourceCatalog.Definition definition;
|
|
LimsElnSourceCatalog.Draft draft() {
|
return new LimsElnSourceCatalog.Draft(propertyCode, displayName, dataType, definition);
|
}
|
}
|
}
|