22
22
import com .fasterxml .jackson .databind .JsonNode ;
23
23
import com .fasterxml .jackson .databind .node .JsonNodeFactory ;
24
24
import com .fasterxml .jackson .module .jsonSchema .JsonSchema ;
25
+ import com .fasterxml .jackson .module .jsonSchema .types .ArraySchema ;
25
26
import com .fasterxml .jackson .module .jsonSchema .types .ArraySchema .Items ;
27
+ import com .fasterxml .jackson .module .jsonSchema .types .IntegerSchema ;
28
+ import com .fasterxml .jackson .module .jsonSchema .types .NumberSchema ;
26
29
import com .fasterxml .jackson .module .jsonSchema .types .ObjectSchema ;
27
30
import com .fasterxml .jackson .module .jsonSchema .types .ObjectSchema .SchemaAdditionalProperties ;
28
31
import com .fasterxml .jackson .module .jsonSchema .types .ReferenceSchema ;
43
46
import io .fabric8 .generator .annotation .Nullable ;
44
47
import io .fabric8 .generator .annotation .Pattern ;
45
48
import io .fabric8 .generator .annotation .Required ;
49
+ import io .fabric8 .generator .annotation .Size ;
46
50
import io .fabric8 .generator .annotation .ValidationRule ;
47
51
import io .fabric8 .generator .annotation .ValidationRules ;
48
52
import io .fabric8 .kubernetes .api .model .GenericKubernetesResource ;
@@ -230,8 +234,16 @@ class PropertyMetadata {
230
234
private final String description ;
231
235
private final Object defaultValue ;
232
236
private Double min ;
237
+ private Boolean exclusiveMinimum ;
233
238
private Double max ;
239
+ private Boolean exclusiveMaximum ;
234
240
private String pattern ;
241
+ private Long minLength ;
242
+ private Long maxLength ;
243
+ private Long minItems ;
244
+ private Long maxItems ;
245
+ private Long minProperties ;
246
+ private Long maxProperties ;
235
247
private boolean nullable ;
236
248
private String format ;
237
249
private List <V > validationRules = new ArrayList <>();
@@ -254,15 +266,55 @@ public PropertyMetadata(JsonSchema value, BeanProperty beanProperty) {
254
266
if (value .isStringSchema ()) {
255
267
StringSchema stringSchema = value .asStringSchema ();
256
268
// only set if ValidationSchemaFactoryWrapper is used
257
- this .pattern = stringSchema .getPattern ();
258
- //this.maxLength = ofNullable(stringSchema.getMaxLength()).map(Integer::doubleValue).orElse(null);
259
- //this.minLength = ofNullable(stringSchema.getMinLength()).map(Integer::doubleValue).orElse(null);
260
- } else {
261
- // TODO: process the other schema types for validation values
269
+ pattern = ofNullable (beanProperty .getAnnotation (Pattern .class )).map (Pattern ::value )
270
+ .or (() -> ofNullable (stringSchema .getPattern ()))
271
+ .orElse (null );
272
+ minLength = findMinInSizeAnnotation (beanProperty )
273
+ .or (() -> ofNullable (stringSchema .getMinLength ()).map (Integer ::longValue ))
274
+ .orElse (null );
275
+ maxLength = findMaxInSizeAnnotation (beanProperty )
276
+ .or (() -> ofNullable (stringSchema .getMaxLength ()).map (Integer ::longValue ))
277
+ .orElse (null );
278
+ } else if (value .isIntegerSchema ()) {
279
+ // integerschema extends numberschema and must handled first
280
+ IntegerSchema integerSchema = value .asIntegerSchema ();
281
+ setMinMax (beanProperty ,
282
+ integerSchema .getMinimum (),
283
+ integerSchema .getExclusiveMinimum (),
284
+ integerSchema .getMaximum (),
285
+ integerSchema .getExclusiveMaximum ());
286
+ } else if (value .isNumberSchema ()) {
287
+ NumberSchema numberSchema = value .asNumberSchema ();
288
+ setMinMax (beanProperty ,
289
+ numberSchema .getMinimum (),
290
+ numberSchema .getExclusiveMinimum (),
291
+ numberSchema .getMaximum (),
292
+ numberSchema .getExclusiveMaximum ());
293
+ } else if (value .isArraySchema ()) {
294
+ ArraySchema arraySchema = value .asArraySchema ();
295
+ minItems = findMinInSizeAnnotation (beanProperty )
296
+ .or (() -> ofNullable (arraySchema .getMinItems ()).map (Integer ::longValue ))
297
+ .orElse (null );
298
+ maxItems = findMaxInSizeAnnotation (beanProperty )
299
+ .or (() -> ofNullable (arraySchema .getMaxItems ()).map (Integer ::longValue ))
300
+ .orElse (null );
301
+ } else if (value .isObjectSchema ()) {
302
+ // TODO: Could be also applied only on Maps instead of "all the rest"
303
+ minProperties = findMinInSizeAnnotation (beanProperty )
304
+ .orElse (null );
305
+ maxProperties = findMaxInSizeAnnotation (beanProperty )
306
+ .orElse (null );
262
307
}
263
308
264
309
collectValidationRules (beanProperty , validationRules );
265
310
311
+ // TODO: should probably move to a standard annotations
312
+ // see ValidationSchemaFactoryWrapper
313
+ nullable = beanProperty .getAnnotation (Nullable .class ) != null ;
314
+
315
+ // TODO: should the following be deprecated?
316
+ required = beanProperty .getAnnotation (Required .class ) != null ;
317
+
266
318
if (beanProperty .getMetadata ().getDefaultValue () != null ) {
267
319
defaultValue = toTargetType (beanProperty .getType (), beanProperty .getMetadata ().getDefaultValue ());
268
320
} else if (ofNullable (beanProperty .getAnnotation (Default .class )).map (Default ::value ).isPresent ()) {
@@ -271,16 +323,34 @@ public PropertyMetadata(JsonSchema value, BeanProperty beanProperty) {
271
323
} else {
272
324
defaultValue = null ;
273
325
}
326
+ }
274
327
275
- // TODO: should probably move to a standard annotations
276
- // see ValidationSchemaFactoryWrapper
277
- nullable = beanProperty .getAnnotation (Nullable .class ) != null ;
278
- max = ofNullable (beanProperty .getAnnotation (Max .class )).map (Max ::value ).orElse (max );
279
- min = ofNullable (beanProperty .getAnnotation (Min .class )).map (Min ::value ).orElse (min );
280
-
281
- // TODO: should the following be deprecated?
282
- required = beanProperty .getAnnotation (Required .class ) != null ;
283
- pattern = ofNullable (beanProperty .getAnnotation (Pattern .class )).map (Pattern ::value ).orElse (pattern );
328
+ private void setMinMax (BeanProperty beanProperty ,
329
+ Double minimum , Boolean exclusiveMinimum , Double maximum , Boolean exclusiveMaximum ) {
330
+ ofNullable (minimum ).ifPresent (v -> {
331
+ this .min = v ;
332
+ if (Boolean .TRUE .equals (exclusiveMinimum )) {
333
+ this .exclusiveMinimum = true ;
334
+ }
335
+ });
336
+ ofNullable (beanProperty .getAnnotation (Min .class )).ifPresent (a -> {
337
+ min = a .value ();
338
+ if (!a .inclusive ()) {
339
+ this .exclusiveMinimum = true ;
340
+ }
341
+ });
342
+ ofNullable (maximum ).ifPresent (v -> {
343
+ this .max = v ;
344
+ if (Boolean .TRUE .equals (exclusiveMaximum )) {
345
+ this .exclusiveMaximum = true ;
346
+ }
347
+ });
348
+ ofNullable (beanProperty .getAnnotation (Max .class )).ifPresent (a -> {
349
+ this .max = a .value ();
350
+ if (!a .inclusive ()) {
351
+ this .exclusiveMaximum = true ;
352
+ }
353
+ });
284
354
}
285
355
286
356
public void updateSchema (T schema ) {
@@ -294,10 +364,22 @@ public void updateSchema(T schema) {
294
364
}
295
365
}
296
366
if (nullable ) {
297
- schema .setNullable (nullable );
367
+ schema .setNullable (true );
298
368
}
299
369
schema .setMaximum (max );
370
+ schema .setExclusiveMaximum (exclusiveMaximum );
300
371
schema .setMinimum (min );
372
+ schema .setExclusiveMinimum (exclusiveMinimum );
373
+
374
+ schema .setMinLength (minLength );
375
+ schema .setMaxLength (maxLength );
376
+
377
+ schema .setMinItems (minItems );
378
+ schema .setMaxItems (maxItems );
379
+
380
+ schema .setMinProperties (minProperties );
381
+ schema .setMaxProperties (maxProperties );
382
+
301
383
schema .setPattern (pattern );
302
384
schema .setFormat (format );
303
385
if (preserveUnknownFields ) {
@@ -306,6 +388,18 @@ public void updateSchema(T schema) {
306
388
307
389
addToValidationRules (schema , validationRules );
308
390
}
391
+
392
+ private Optional <Long > findMinInSizeAnnotation (BeanProperty beanProperty ) {
393
+ return ofNullable (beanProperty .getAnnotation (Size .class ))
394
+ .map (Size ::min )
395
+ .filter (v -> v > 0 );
396
+ }
397
+
398
+ private Optional <Long > findMaxInSizeAnnotation (BeanProperty beanProperty ) {
399
+ return ofNullable (beanProperty .getAnnotation (Size .class ))
400
+ .map (Size ::max )
401
+ .filter (v -> v < Long .MAX_VALUE );
402
+ }
309
403
}
310
404
311
405
private T resolveObject (LinkedHashMap <String , String > visited , InternalSchemaSwaps schemaSwaps , JsonSchema jacksonSchema ,
@@ -514,24 +608,49 @@ private void handleTypeAnnotations(final T schema, BeanProperty beanProperty, Cl
514
608
515
609
AnnotatedElement member = beanProperty .getMember ().getAnnotated ();
516
610
AnnotatedType fieldType = null ;
517
- AnnotatedType type = null ;
611
+ AnnotatedType methodType = null ;
518
612
if (member instanceof Field ) {
519
613
fieldType = ((Field ) member ).getAnnotatedType ();
520
614
} else if (member instanceof Method ) {
521
615
fieldType = getFieldForMethod (beanProperty ).map (Field ::getAnnotatedType ).orElse (null );
522
- type = ((Method ) member ).getAnnotatedReceiverType ();
616
+ methodType = ((Method ) member ).getAnnotatedReceiverType ();
523
617
}
524
618
525
- Stream .of (fieldType , type )
619
+ Stream .of (fieldType , methodType )
526
620
.filter (o -> !Objects .isNull (o ))
527
621
.filter (AnnotatedParameterizedType .class ::isInstance )
528
622
.map (AnnotatedParameterizedType .class ::cast )
529
623
.map (AnnotatedParameterizedType ::getAnnotatedActualTypeArguments )
530
624
.map (a -> a [typeIndex ])
531
625
.forEach (at -> {
532
- Optional .ofNullable (at .getAnnotation (Pattern .class )).ifPresent (a -> schema .setPattern (a .value ()));
533
- Optional .ofNullable (at .getAnnotation (Min .class )).ifPresent (a -> schema .setMinimum (a .value ()));
534
- Optional .ofNullable (at .getAnnotation (Max .class )).ifPresent (a -> schema .setMaximum (a .value ()));
626
+ if ("string" .equals (schema .getType ())) {
627
+ ofNullable (at .getAnnotation (Pattern .class ))
628
+ .ifPresent (a -> schema .setPattern (a .value ()));
629
+
630
+ ofNullable (at .getAnnotation (Size .class ))
631
+ .map (Size ::min )
632
+ .filter (v -> v > 0 )
633
+ .ifPresent (schema ::setMinLength );
634
+
635
+ ofNullable (at .getAnnotation (Size .class ))
636
+ .map (Size ::max )
637
+ .filter (v -> v < Long .MAX_VALUE )
638
+ .ifPresent (schema ::setMaxLength );
639
+
640
+ } else if ("number" .equals (schema .getType ()) || "integer" .equals (schema .getType ())) {
641
+ ofNullable (at .getAnnotation (Min .class )).ifPresent (a -> {
642
+ schema .setMinimum (a .value ());
643
+ if (!a .inclusive ()) {
644
+ schema .setExclusiveMinimum (true );
645
+ }
646
+ });
647
+ ofNullable (at .getAnnotation (Max .class )).ifPresent (a -> {
648
+ schema .setMaximum (a .value ());
649
+ if (!a .inclusive ()) {
650
+ schema .setExclusiveMaximum (true );
651
+ }
652
+ });
653
+ }
535
654
});
536
655
}
537
656
0 commit comments