Skip to content

Commit f2a2f97

Browse files
committed
featuretype validation, wgs84 use in capabilities, admission checks test
1 parent 0c70f55 commit f2a2f97

25 files changed

+529
-85
lines changed

api/v2beta1/shared_conversion.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ func ConvertV2DataToV3(v2 Data) pdoknlv3.Data {
164164
if v2.Tif != nil {
165165
v3.TIF = &pdoknlv3.TIF{
166166
BlobKey: v2.Tif.BlobKey,
167-
Resample: v2.Tif.Resample,
167+
Resample: *v2.Tif.Resample,
168168
Offsite: v2.Tif.Offsite,
169169
GetFeatureInfoIncludesClass: smoothoperatorutils.PointerVal(v2.Tif.GetFeatureInfoIncludesClass, false),
170170
}
@@ -201,7 +201,7 @@ func ConvertV3DataToV2(v3 pdoknlv3.Data) Data {
201201
v2.Tif = &Tif{
202202
BlobKey: v3.TIF.BlobKey,
203203
Offsite: v3.TIF.Offsite,
204-
Resample: v3.TIF.Resample,
204+
Resample: &v3.TIF.Resample,
205205
GetFeatureInfoIncludesClass: &v3.TIF.GetFeatureInfoIncludesClass,
206206
}
207207
}

api/v2beta1/wfs_conversion.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ func convertV2FeatureTypeToV3(src FeatureType) pdoknlv3.FeatureType {
180180

181181
if src.Extent != nil {
182182
featureTypeV3.Bbox = &pdoknlv3.FeatureBbox{
183-
DefaultCRS: smoothoperatormodel.ExtentToBBox(*src.Extent),
183+
DefaultCRS: smoothoperatorutils.Pointer(smoothoperatormodel.ExtentToBBox(*src.Extent)),
184184
}
185185
}
186186

api/v3/shared_types.go

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,6 @@ type Data struct {
171171
type Gpkg struct {
172172
// Blobkey identifies the location/bucket of the .gpkg file
173173
// +kubebuilder:validation:Pattern:=^.+\/.+\/.+\.gpkg$
174-
// +kubebuilder:validation:MinLength:=1
175174
BlobKey string `json:"blobKey"`
176175

177176
// TableName is the table within the geopackage
@@ -180,7 +179,6 @@ type Gpkg struct {
180179

181180
// GeometryType of the table, must match an OGC type
182181
// +kubebuilder:validation:Pattern:=`^(Multi)?(Point|LineString|Polygon)$`
183-
// +kubebuilder:validation:MinLength:=1
184182
GeometryType string `json:"geometryType"`
185183

186184
// Columns to visualize for this table
@@ -209,18 +207,15 @@ type Postgis struct {
209207
// +kubebuilder:validation:Type=object
210208
type TIF struct {
211209
// BlobKey to the TIFF file
212-
// +kubebuilder:validation:Pattern=`\.(tif?f|vrt)$`
213-
// +kubebuilder:validation:MinLength:=1
210+
// +kubebuilder:validation:Pattern:=`^.+\/.+\/.+\.(tif?f|vrt)$`
214211
BlobKey string `json:"blobKey"`
215212

216213
// This option can be used to control the resampling kernel used sampling raster images, optional
217-
// +kubebuilder:validation:MinLength:=1
218214
// +kubebuilder:validation:Pattern=`(NEAREST|AVERAGE|BILINEAR)`
219215
// +kubebuilder:default=NEAREST
220-
Resample *string `json:"resample,omitempty"`
216+
Resample string `json:"resample,omitempty"`
221217

222218
// Sets the color index to treat as transparent for raster layers, optional, hex or rgb
223-
// +kubebuilder:validation:MinLength:=1
224219
// +kubebuilder:validation:Pattern=`(#[0-9A-F]{6}([0-9A-F]{2})?)|([0-9]{1,3}\s[0-9]{1,3}\s[0-9]{1,3})`
225220
Offsite *string `json:"offsite,omitempty"`
226221

api/v3/shared_validation.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,12 @@ func ValidateInspire[O WMSWFS](obj O, allErrs *field.ErrorList) {
128128
}
129129
}
130130

131+
if obj.Type() == ServiceTypeWFS && len(datasetIDs) > 1 {
132+
*allErrs = append(*allErrs, field.Invalid(
133+
field.NewPath("spec").Child("service").Child("featureTypes[*]").Child("datasetMetadataUrl").Child("csw").Child("metadataIdentifier"),
134+
datasetIDs,
135+
"when Inspire, all featureTypes need use the same datasetMetadataUrl.csw.metadataIdentifier",
136+
))
137+
}
138+
131139
}

api/v3/wfs_types.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ type WFSService struct {
137137

138138
// Default CRS (DataEPSG)
139139
// +kubebuilder:validation:Pattern:="^EPSG:(28992|25831|25832|3034|3035|3857|4258|4326)$"
140-
// +kubebuilder:validation:MinLength:=1
141140
DefaultCrs string `json:"defaultCrs"`
142141

143142
// Other supported CRS
@@ -169,7 +168,6 @@ func (s WFSService) KeywordsIncludingInspireKeyword() []string {
169168

170169
// HealthCheck is the struct with all fields to configure custom healthchecks
171170
type HealthCheckWFS struct {
172-
// +kubebuilder:validation:MinLength:=1
173171
// +kubebuilder:validation:XValidation:rule="self.contains('Service=WFS')",message="a valid healthcheck contains 'Service=WFS'"
174172
// +kubebuilder:validation:XValidation:rule="self.contains('Request=')",message="a valid healthcheck contains 'Request='"
175173
Querystring string `json:"querystring"`
@@ -187,7 +185,7 @@ type Bbox struct {
187185
// FeatureType defines a WFS feature
188186
type FeatureType struct {
189187
// Name of the feature
190-
// +kubebuilder:validation:MinLength:=1
188+
// +kubebuilder:validation:Pattern:=`^\S+$`
191189
Name string `json:"name"`
192190

193191
// Title of the feature
@@ -217,14 +215,14 @@ type FeatureType struct {
217215
Data Data `json:"data"`
218216
}
219217

220-
// FeatureType bounding box, if provided it overrides the default extent
218+
// FeatureBbox is the optional featureType bounding box, if provided it overrides the default extent
221219
type FeatureBbox struct {
222-
// DefaultCRS defines the feature’s bounding box in the service’s own CRS
220+
// DefaultCRS defines the EXTENT/wfs_extent for the featureType for use in the mapfile
223221
//nolint:tagliatelle
224222
// +kubebuilder:validation:Type=object
225-
DefaultCRS smoothoperatormodel.BBox `json:"defaultCRS"`
223+
DefaultCRS *smoothoperatormodel.BBox `json:"defaultCRS,omitempty"`
226224

227-
// WGS84, if provided, gives the same bounding box reprojected into EPSG:4326.
225+
// WGS84, if provided, gives the same bounding box reprojected into EPSG:4326 for use in the capabilities.
228226
// +kubebuilder:validation:Type=object
229227
WGS84 *smoothoperatormodel.BBox `json:"wgs84,omitempty"`
230228
}

api/v3/wfs_validation.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package v3
22

33
import (
4+
"slices"
45
"strings"
56

67
sharedValidation "github.com/pdok/smooth-operator/pkg/validation"
@@ -69,4 +70,66 @@ func ValidateWFS(wfs *WFS, warnings *[]string, allErrs *field.ErrorList) {
6970

7071
podSpecPatch := wfs.Spec.PodSpecPatch
7172
ValidateEphemeralStorage(podSpecPatch, allErrs)
73+
74+
ValidateFeatureTypes(wfs, warnings, allErrs)
75+
}
76+
77+
func ValidateFeatureTypes(wfs *WFS, warnings *[]string, allErrs *field.ErrorList) {
78+
names := []string{}
79+
path := field.NewPath("spec").Child("service").Child("featureTypes")
80+
for index, featureType := range wfs.Spec.Service.FeatureTypes {
81+
if slices.Contains(names, featureType.Name) {
82+
*allErrs = append(*allErrs, field.Duplicate(
83+
path.Index(index).Child("name"),
84+
featureType.Name,
85+
))
86+
}
87+
88+
if !slices.Contains(names, featureType.Name) {
89+
names = append(names, featureType.Name)
90+
}
91+
92+
if wfs.Spec.Service.Mapfile != nil && featureType.Bbox != nil && featureType.Bbox.DefaultCRS != nil {
93+
sharedValidation.AddWarning(
94+
warnings,
95+
*path.Index(index).Child("bbox").Child("defaultCrs"),
96+
"is not used when service.mapfile is configured",
97+
wfs.GroupVersionKind(),
98+
wfs.GetName(),
99+
)
100+
}
101+
102+
if tif := featureType.Data.TIF; tif != nil {
103+
if tif.Resample != "NEAREST" {
104+
sharedValidation.AddWarning(
105+
warnings,
106+
*path.Index(index).Child("data").Child("tif").Child("resample"),
107+
"is not used when service.mapfile is configured",
108+
wfs.GroupVersionKind(),
109+
wfs.GetName(),
110+
)
111+
}
112+
113+
if tif.Offsite != nil {
114+
sharedValidation.AddWarning(
115+
warnings,
116+
*path.Index(index).Child("data").Child("tif").Child("offsite"),
117+
"is not used when service.mapfile is configured",
118+
wfs.GroupVersionKind(),
119+
wfs.GetName(),
120+
)
121+
}
122+
123+
if tif.GetFeatureInfoIncludesClass {
124+
sharedValidation.AddWarning(
125+
warnings,
126+
*path.Index(index).Child("data").Child("tif").Child("getFeatureInfoIncludesClass"),
127+
"is not used when service.mapfile is configured",
128+
wfs.GroupVersionKind(),
129+
wfs.GetName(),
130+
)
131+
}
132+
}
133+
134+
}
72135
}

api/v3/zz_generated.deepcopy.go

Lines changed: 5 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/pdok.nl_wfs.yaml

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,6 @@ spec:
445445
pattern: (image/png|text/xml|text/html)
446446
type: string
447447
querystring:
448-
minLength: 1
449448
type: string
450449
x-kubernetes-validations:
451450
- message: a valid healthcheck contains 'Service=WFS'
@@ -1126,7 +1125,6 @@ spec:
11261125
type: integer
11271126
defaultCrs:
11281127
description: Default CRS (DataEPSG)
1129-
minLength: 1
11301128
pattern: ^EPSG:(28992|25831|25832|3034|3035|3857|4258|4326)$
11311129
type: string
11321130
featureTypes:
@@ -1142,7 +1140,7 @@ spec:
11421140
description: Optional feature bbox
11431141
properties:
11441142
defaultCRS:
1145-
description: DefaultCRS defines the feature’s bounding box in the service’s own CRS
1143+
description: DefaultCRS defines the EXTENT/wfs_extent for the featureType for use in the mapfile
11461144
properties:
11471145
maxx:
11481146
description: Rechtsonder X coördinaat
@@ -1167,7 +1165,7 @@ spec:
11671165
- miny
11681166
type: object
11691167
wgs84:
1170-
description: WGS84, if provided, gives the same bounding box reprojected into EPSG:4326.
1168+
description: WGS84, if provided, gives the same bounding box reprojected into EPSG:4326 for use in the capabilities.
11711169
properties:
11721170
maxx:
11731171
description: Rechtsonder X coördinaat
@@ -1191,8 +1189,6 @@ spec:
11911189
- minx
11921190
- miny
11931191
type: object
1194-
required:
1195-
- defaultCRS
11961192
type: object
11971193
data:
11981194
description: FeatureType data connection
@@ -1202,7 +1198,6 @@ spec:
12021198
properties:
12031199
blobKey:
12041200
description: Blobkey identifies the location/bucket of the .gpkg file
1205-
minLength: 1
12061201
pattern: ^.+\/.+\/.+\.gpkg$
12071202
type: string
12081203
columns:
@@ -1225,7 +1220,6 @@ spec:
12251220
type: array
12261221
geometryType:
12271222
description: GeometryType of the table, must match an OGC type
1228-
minLength: 1
12291223
pattern: ^(Multi)?(Point|LineString|Polygon)$
12301224
type: string
12311225
tableName:
@@ -1278,22 +1272,19 @@ spec:
12781272
properties:
12791273
blobKey:
12801274
description: BlobKey to the TIFF file
1281-
minLength: 1
1282-
pattern: \.(tif?f|vrt)$
1275+
pattern: ^.+\/.+\/.+\.(tif?f|vrt)$
12831276
type: string
12841277
getFeatureInfoIncludesClass:
12851278
default: false
12861279
description: '"When a band represents nominal or ordinal data the class name (from styling) can be included in the getFeatureInfo"'
12871280
type: boolean
12881281
offsite:
12891282
description: Sets the color index to treat as transparent for raster layers, optional, hex or rgb
1290-
minLength: 1
12911283
pattern: (#[0-9A-F]{6}([0-9A-F]{2})?)|([0-9]{1,3}\s[0-9]{1,3}\s[0-9]{1,3})
12921284
type: string
12931285
resample:
12941286
default: NEAREST
12951287
description: This option can be used to control the resampling kernel used sampling raster images, optional
1296-
minLength: 1
12971288
pattern: (NEAREST|AVERAGE|BILINEAR)
12981289
type: string
12991290
required:
@@ -1344,7 +1335,7 @@ spec:
13441335
type: array
13451336
name:
13461337
description: Name of the feature
1347-
minLength: 1
1338+
pattern: ^\S+$
13481339
type: string
13491340
title:
13501341
description: Title of the feature

0 commit comments

Comments
 (0)