Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions api/v2beta1/wfs_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,13 @@ func (src *WFS) ToV3(dst *pdoknlv3.WFS) error {
AccessConstraints: smoothoperatormodel.URL{URL: accessConstraints},
DefaultCrs: src.Spec.Service.DataEPSG,
OtherCrs: []string{
"EPSG::25831",
"EPSG::25832",
"EPSG::3034",
"EPSG::3035",
"EPSG::3857",
"EPSG::4258",
"EPSG::4326",
"EPSG:25831",
"EPSG:25832",
"EPSG:3034",
"EPSG:3035",
"EPSG:3857",
"EPSG:4258",
"EPSG:4326",
},
FeatureTypes: make([]pdoknlv3.FeatureType, 0),
}
Expand Down
20 changes: 11 additions & 9 deletions api/v3/shared_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,13 @@ type WMSWFS interface {
Options() Options
HasPostgisData() bool

// URLPath returns the configured service URL
// URL returns the configured service URL
URL() smoothoperatormodel.URL
IngressRouteURLs() smoothoperatormodel.IngressRouteURLs

// DatasetMetadataIds returns list of all configured metadata identifiers configured on Layers or Featuretypes
DatasetMetadataIDs() []string

GeoPackages() []*Gpkg

ReadinessQueryString() (string, string, error)
Expand Down Expand Up @@ -115,18 +118,18 @@ type Inspire struct {
ServiceMetadataURL MetadataURL `json:"serviceMetadataUrl"`

// SpatialDatasetIdentifier is the ID uniquely identifying the dataset.
// +kubebuilder:validation:MinLength:=1
// +kubebuilder:validation:Pattern:=`^[0-9a-zA-Z]{8}\-[0-9a-zA-Z]{4}\-[0-9a-zA-Z]{4}\-[0-9a-zA-Z]{4}\-[0-9a-zA-Z]{12}$`
SpatialDatasetIdentifier string `json:"spatialDatasetIdentifier"`

// Language of the INSPIRE metadata record
// +kubebuilder:validation:MinLength:=1
// +kubebuilder:validation:Pattern:=`bul|cze|dan|dut|eng|est|fin|fre|ger|gre|hun|gle|ita|lav|lit|mlt|pol|por|rum|slo|slv|spa|swe`
Language string `json:"language"`
}

// +kubebuilder:validation:XValidation:rule="(has(self.csw) || has(self.custom)) && !(has(self.csw) && has(self.custom))", message="metadataUrl should have csw or custom, not both"
// +kubebuilder:validation:XValidation:rule="(has(self.csw) || has(self.custom)) && !(has(self.csw) && has(self.custom))", message="metadataUrl should have exactly 1 of csw or custom"
type MetadataURL struct {
// CSW describes a metadata record via a metadataIdentifier (UUID) as defined in the OwnerInfo.
CSW *Metadata `json:"csw"`
CSW *Metadata `json:"csw,omitempty"`

// Custom allows arbitrary href
Custom *Custom `json:"custom,omitempty"`
Expand All @@ -135,16 +138,15 @@ type MetadataURL struct {
// Metadata holds the UUID of a CSW metadata record
type Metadata struct {
// MetadataIdentifier is the record's UUID
// +kubebuilder:validation:MinLength:=1
// +kubebuilder:validation:Pattern:=`^[0-9a-zA-Z]{8}\-[0-9a-zA-Z]{4}\-[0-9a-zA-Z]{4}\-[0-9a-zA-Z]{4}\-[0-9a-zA-Z]{12}$`
MetadataIdentifier string `json:"metadataIdentifier"`
}

// Custom represents a non-CSW metadata link with a href and MIME type.
// +kubebuilder:validation:Type=object
type Custom struct {
// +kubebuilder:validation:Pattern=`^https?://.*$`
// +kubebuilder:validation:MinLength=1
Href string `json:"href"`
// Href of the custom metadata url
Href smoothoperatormodel.URL `json:"href"`

// MIME type of the custom link
// +kubebuilder:validation:MinLength=1
Expand Down
37 changes: 37 additions & 0 deletions api/v3/shared_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package v3

import (
"fmt"
"slices"

sharedValidation "github.com/pdok/smooth-operator/pkg/validation"
v1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -82,3 +83,39 @@ func ValidateEphemeralStorage(podSpecPatch v1.PodSpec, allErrs *field.ErrorList)
*allErrs = append(*allErrs, field.Required(path, ""))
}
}

func ValidateInspire[O WMSWFS](obj O, allErrs *field.ErrorList) {
if obj.Inspire() == nil {
return
}

datasetIDs := obj.DatasetMetadataIDs()
spatialID := obj.Inspire().SpatialDatasetIdentifier

if slices.Contains(datasetIDs, spatialID) {
*allErrs = append(*allErrs, field.Invalid(
field.NewPath("spec").Child("service").Child("inspire").Child("spatialDatasetIdentifier"),
spatialID,
"spatialDatasetIdentifier cannot also be used as an datasetMetadataUrl.csw.metadataIdentifier",
))
}

if serviceID := obj.Inspire().ServiceMetadataURL.CSW; serviceID != nil {
if slices.Contains(datasetIDs, serviceID.MetadataIdentifier) {
*allErrs = append(*allErrs, field.Invalid(
field.NewPath("spec").Child("service").Child("inspire").Child("csw").Child("metadataIdentifier"),
serviceID.MetadataIdentifier,
"serviceMetadataUrl.csw.metadataIdentifier cannot also be used as an datasetMetadataUrl.csw.metadataIdentifier",
))
}

if spatialID == serviceID.MetadataIdentifier {
*allErrs = append(*allErrs, field.Invalid(
field.NewPath("spec").Child("service").Child("inspire").Child("csw").Child("metadataIdentifier"),
serviceID.MetadataIdentifier,
"serviceMetadataUrl.csw.metadataIdentifier cannot also be used as the spatialDatasetIdentifier",
))
}
}

}
16 changes: 16 additions & 0 deletions api/v3/wfs_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ type WFSSpec struct {
Service WFSService `json:"service"`
}

// +kubebuilder:validation:XValidation:message="otherCrs can't contain the defaultCrs",rule="!has(self.otherCrs) || (has(self.otherCrs) && !(self.defaultCrs in self.otherCrs))",fieldPath=".otherCrs"
type WFSService struct {
// Geonovum subdomein
// +kubebuilder:validation:MinLength:=1
Expand Down Expand Up @@ -141,6 +142,7 @@ type WFSService struct {

// Other supported CRS
// +kubebuilder:validation:MinItems:=1
// +kubebuilder:validation:items:Pattern:="^EPSG:(28992|25831|25832|3034|3035|3857|4258|4326)$"
OtherCrs []string `json:"otherCrs,omitempty"`

// Service bounding box
Expand Down Expand Up @@ -283,6 +285,20 @@ func (wfs *WFS) URL() smoothoperatormodel.URL {
return wfs.Spec.Service.URL
}

func (wfs *WFS) DatasetMetadataIDs() []string {
ids := []string{}

for _, featureType := range wfs.Spec.Service.FeatureTypes {
if featureType.DatasetMetadataURL != nil && featureType.DatasetMetadataURL.CSW != nil {
if id := featureType.DatasetMetadataURL.CSW.MetadataIdentifier; !slices.Contains(ids, id) {
ids = append(ids, id)
}
}
}

return ids
}

func (wfs *WFS) GeoPackages() []*Gpkg {
gpkgs := make([]*Gpkg, 0)

Expand Down
2 changes: 2 additions & 0 deletions api/v3/wfs_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ func ValidateWFS(wfs *WFS, warnings *[]string, allErrs *field.ErrorList) {
sharedValidation.AddWarning(warnings, *path.Child("bbox"), "is not used when service.mapfile is configured", wfs.GroupVersionKind(), wfs.GetName())
}

ValidateInspire(wfs, allErrs)

if wfs.Spec.HorizontalPodAutoscalerPatch != nil {
ValidateHorizontalPodAutoscalerPatch(*wfs.Spec.HorizontalPodAutoscalerPatch, allErrs)
}
Expand Down
14 changes: 14 additions & 0 deletions api/v3/wms_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,20 @@ func (wms *WMS) URL() smoothoperatormodel.URL {
return wms.Spec.Service.URL
}

func (wms *WMS) DatasetMetadataIDs() []string {
ids := []string{}

for _, layer := range wms.Spec.Service.GetAllLayers() {
if layer.DatasetMetadataURL != nil && layer.DatasetMetadataURL.CSW != nil {
if id := layer.DatasetMetadataURL.CSW.MetadataIdentifier; !slices.Contains(ids, id) {
ids = append(ids, id)
}
}
}

return ids
}

func (wms *WMS) GeoPackages() []*Gpkg {
gpkgs := make([]*Gpkg, 0)

Expand Down
2 changes: 2 additions & 0 deletions api/v3/wms_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,8 @@ func ValidateWMS(wms *WMS, warnings *[]string, allErrs *field.ErrorList) {
))
}

ValidateInspire(wms, allErrs)

if wms.Spec.HorizontalPodAutoscalerPatch != nil {
ValidateHorizontalPodAutoscalerPatch(*wms.Spec.HorizontalPodAutoscalerPatch, allErrs)
}
Expand Down
3 changes: 2 additions & 1 deletion api/v3/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading