刘光辉
9 小时以前 06f314545ac3514d7ce28939920866ae8188d31c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
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);
        }
    }
}