1
2
3
4
5
6 package org.tailormap.api.controller;
7
8 import static org.springframework.http.HttpStatus.BAD_REQUEST;
9 import static org.springframework.http.HttpStatus.NOT_FOUND;
10 import static org.springframework.http.HttpStatus.NOT_MODIFIED;
11 import static org.tailormap.api.service.UploadsService.createUploadResponseEntity;
12 import static org.tailormap.api.util.TMStringUtils.nullIfEmpty;
13
14 import jakarta.servlet.http.HttpServletRequest;
15 import java.util.Optional;
16 import java.util.UUID;
17 import java.util.regex.Pattern;
18 import org.apache.commons.lang3.ObjectUtils;
19 import org.gaul.modernizer_maven_annotations.SuppressModernizer;
20 import org.springframework.http.ResponseEntity;
21 import org.springframework.validation.annotation.Validated;
22 import org.springframework.web.bind.annotation.GetMapping;
23 import org.springframework.web.bind.annotation.ModelAttribute;
24 import org.springframework.web.bind.annotation.PathVariable;
25 import org.springframework.web.server.ResponseStatusException;
26 import org.tailormap.api.annotation.AppRestController;
27 import org.tailormap.api.persistence.Application;
28 import org.tailormap.api.persistence.GeoService;
29 import org.tailormap.api.persistence.Upload;
30 import org.tailormap.api.persistence.UploadCategory;
31 import org.tailormap.api.persistence.json.AppLayerSettings;
32 import org.tailormap.api.persistence.json.AppTreeLayerNode;
33 import org.tailormap.api.persistence.json.GeoServiceDefaultLayerSettings;
34 import org.tailormap.api.persistence.json.GeoServiceLayer;
35 import org.tailormap.api.persistence.json.GeoServiceLayerSettings;
36 import org.tailormap.api.repository.UploadRepository;
37 import org.tailormap.api.service.UploadsService;
38
39 @AppRestController
40 @Validated
41 public class LayerAttachedUploadsController {
42 private final UploadsService uploadsService;
43 private final UploadRepository uploadRepository;
44 private final UploadsController uploadsController;
45
46 public LayerAttachedUploadsController(
47 UploadsService uploadsService, UploadRepository uploadRepository, UploadsController uploadsController) {
48 this.uploadsService = uploadsService;
49 this.uploadRepository = uploadRepository;
50 this.uploadsController = uploadsController;
51 }
52
53 @GetMapping(
54 path = {
55
56 "/api/app/{viewerName}/layer/{appLayerId}/uploads/{category}/{id}",
57 "/api/app/{viewerName}/layer/{appLayerId}/uploads/{category}/{id}/{filename}"
58 })
59 public ResponseEntity<byte[]> getLayerAttachedUpload(
60 @ModelAttribute AppTreeLayerNode appTreeLayerNode,
61 @ModelAttribute GeoService service,
62 @ModelAttribute GeoServiceLayer layer,
63 @ModelAttribute Application application,
64 HttpServletRequest request,
65 @PathVariable UploadCategory category,
66 @PathVariable(name = "id") UUID id,
67 @PathVariable(name = "filename", required = false) String filename) {
68
69 if (!category.isRestricted()) {
70
71
72
73 return uploadsController.getUpload(request, category, id, filename);
74 }
75
76 switch (category) {
77 case LAYER_ATTACHED_FILE ->
78 validateUploadIsInDescription(id, service, layer, application, appTreeLayerNode);
79 case LEGEND -> {
80 validateLegendIsAttached(id, service, layer, application, appTreeLayerNode);
81 }
82 default ->
83 throw new ResponseStatusException(
84 BAD_REQUEST, "Uploads for category " + category + " are not accessible via this endpoint");
85 }
86
87 if (!uploadsService.checkIfModifiedSince(id, request.getDateHeader("If-Modified-Since"))) {
88 return ResponseEntity.status(NOT_MODIFIED).build();
89 }
90
91
92
93 Upload upload = uploadRepository
94 .findWithContentByIdAndCategory(id, category)
95 .orElseThrow(() -> new ResponseStatusException(NOT_FOUND));
96 return createUploadResponseEntity(upload);
97 }
98
99
100
101
102
103
104
105
106
107
108
109
110 private void validateUploadIsInDescription(
111 UUID id,
112 GeoService service,
113 GeoServiceLayer layer,
114 Application application,
115 AppTreeLayerNode appTreeLayerNode)
116 throws ResponseStatusException {
117
118 Pattern pattern =
119 Pattern.compile(Pattern.quote(UploadsService.UPLOAD_MARKDOWN_SCHEME) + Pattern.quote(id.toString()));
120
121 GeoServiceDefaultLayerSettings defaultLayerSettings = Optional.ofNullable(
122 service.getSettings().getDefaultLayerSettings())
123 .orElseGet(GeoServiceDefaultLayerSettings::new);
124 GeoServiceLayerSettings serviceLayerSettings = Optional.ofNullable(
125 service.getSettings().getLayerSettings().get(layer.getName()))
126 .orElseGet(GeoServiceLayerSettings::new);
127 AppLayerSettings appLayerSettings = application.getAppLayerSettings(appTreeLayerNode);
128
129 @SuppressModernizer
130
131 String description = ObjectUtils.firstNonNull(
132 nullIfEmpty(appLayerSettings.getDescription()),
133 nullIfEmpty(serviceLayerSettings.getDescription()),
134 nullIfEmpty(defaultLayerSettings.getDescription()));
135
136 if (description == null
137 || description.isBlank()
138 || !pattern.matcher(description).find()) {
139 throw new ResponseStatusException(
140 BAD_REQUEST, "Upload with id '" + id + "' is not attached to layer '" + layer.getName() + "'");
141 }
142 }
143
144
145
146
147
148
149
150
151
152
153
154 private void validateLegendIsAttached(
155 UUID id,
156 GeoService service,
157 GeoServiceLayer layer,
158 Application application,
159 AppTreeLayerNode appTreeLayerNode)
160 throws ResponseStatusException {
161
162 GeoServiceDefaultLayerSettings defaultLayerSettings = Optional.ofNullable(
163 service.getSettings().getDefaultLayerSettings())
164 .orElseGet(GeoServiceDefaultLayerSettings::new);
165
166 GeoServiceLayerSettings serviceLayerSettings = Optional.ofNullable(
167 service.getSettings().getLayerSettings().get(layer.getName()))
168 .orElseGet(GeoServiceLayerSettings::new);
169
170 @SuppressModernizer
171
172
173 String legendImageId = ObjectUtils.firstNonNull(
174 serviceLayerSettings.getLegendImageId(), defaultLayerSettings.getLegendImageId());
175
176 UUID legendUuid = null;
177 if (legendImageId != null && !legendImageId.isBlank()) {
178 try {
179 legendUuid = UUID.fromString(legendImageId);
180 } catch (IllegalArgumentException ignored) {
181
182 }
183 }
184
185 if (!id.equals(legendUuid)) {
186
187 validateUploadIsInDescription(id, service, layer, application, appTreeLayerNode);
188 }
189 }
190 }