LayerDescriptionController.java
/*
* Copyright (C) 2022 B3Partners B.V.
*
* SPDX-License-Identifier: MIT
*/
package org.tailormap.api.controller;
import static org.tailormap.api.persistence.helper.TMAttributeTypeHelper.isGeometry;
import static org.tailormap.api.persistence.helper.TMFeatureTypeHelper.getConfiguredAttributes;
import io.micrometer.core.annotation.Timed;
import java.io.Serializable;
import java.util.Set;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.server.ResponseStatusException;
import org.tailormap.api.annotation.AppRestController;
import org.tailormap.api.persistence.Application;
import org.tailormap.api.persistence.Form;
import org.tailormap.api.persistence.GeoService;
import org.tailormap.api.persistence.TMFeatureType;
import org.tailormap.api.persistence.helper.TMFeatureTypeHelper;
import org.tailormap.api.persistence.json.AppLayerSettings;
import org.tailormap.api.persistence.json.AppTreeLayerNode;
import org.tailormap.api.persistence.json.GeoServiceLayer;
import org.tailormap.api.persistence.json.TMAttributeDescriptor;
import org.tailormap.api.persistence.json.TMAttributeType;
import org.tailormap.api.persistence.json.TMGeometryType;
import org.tailormap.api.repository.FeatureSourceRepository;
import org.tailormap.api.repository.FormRepository;
import org.tailormap.api.viewer.model.Attribute;
import org.tailormap.api.viewer.model.LayerDetails;
import org.tailormap.api.viewer.model.LayerDetailsForm;
@AppRestController
@Validated
@RequestMapping(
path = "${tailormap-api.base-path}/{viewerKind}/{viewerName}/layer/{appLayerId}/describe",
produces = MediaType.APPLICATION_JSON_VALUE)
public class LayerDescriptionController {
private final FeatureSourceRepository featureSourceRepository;
private final TMFeatureTypeHelper featureTypeHelper;
private final FormRepository formRepository;
public LayerDescriptionController(
FeatureSourceRepository featureSourceRepository,
TMFeatureTypeHelper featureTypeHelper,
FormRepository formRepository) {
this.featureSourceRepository = featureSourceRepository;
this.featureTypeHelper = featureTypeHelper;
this.formRepository = formRepository;
}
@Transactional
@GetMapping
@Timed(value = "get_layer_description", description = "Get layer description")
public ResponseEntity<Serializable> getAppLayerDescription(
@ModelAttribute Application application,
@ModelAttribute AppTreeLayerNode appTreeLayerNode,
@ModelAttribute GeoService service,
@ModelAttribute GeoServiceLayer layer) {
if (layer == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Can't find layer " + appTreeLayerNode);
}
TMFeatureType tmft = service.findFeatureTypeForLayer(layer, featureSourceRepository);
if (tmft == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Layer does not have feature type");
}
LayerDetails r = new LayerDetails()
.id(appTreeLayerNode.getId())
.serviceId(appTreeLayerNode.getServiceId())
.featureTypeName(tmft.getName())
.geometryAttribute(tmft.getDefaultGeometryAttribute())
// TODO deduce from tmft.getDefaultGeometryAttribute()
.geometryAttributeIndex(null /* TODO */)
.geometryType(tmft.getDefaultGeometryDescriptor()
.map(TMAttributeDescriptor::getType)
.map(TMAttributeType::getValue)
.map(TMGeometryType::fromValue)
.orElse(null))
.editable(TMFeatureTypeHelper.isEditable(application, appTreeLayerNode, tmft))
.attachmentAttributes(featureTypeHelper.getAttachmentAttributesWithMaxFileUploadSize(tmft).stream()
.toList());
AppLayerSettings appLayerSettings = application.getAppLayerSettings(appTreeLayerNode);
Form form;
if (appLayerSettings.getFormId() != null) {
form = formRepository.findById(appLayerSettings.getFormId()).orElse(null);
if (form != null) {
r.setForm(new LayerDetailsForm().options(form.getOptions()).fields(form.getFields()));
}
}
Set<String> editableAttributes = TMFeatureTypeHelper.getEditableAttributes(tmft, appLayerSettings);
getConfiguredAttributes(tmft, appLayerSettings).values().stream()
.map(attributeWithSettings -> {
TMAttributeDescriptor a = attributeWithSettings.attributeDescriptor();
return new Attribute()
.name(a.getName())
.alias(attributeWithSettings.settings().getTitle())
// Only return generic 'geometry' type for now, frontend doesn't
// handle different geometry types. For the default geometry
// attribute there is a specific geometry type set
.type(isGeometry(a.getType()) ? TMAttributeType.GEOMETRY : a.getType())
// primary key can never be edited
.editable(!a.getName().equals(tmft.getPrimaryKeyAttribute())
&& editableAttributes.contains(a.getName()))
.defaultValue(a.getDefaultValue())
.nullable(a.getNullable());
})
.forEach(r::addAttributesItem);
return ResponseEntity.ok(r);
}
}