Skip to content

Commit f54229b

Browse files
jonassobottaJonas Sobotta
and
Jonas Sobotta
authored
Add Support for Patterns in JSON Schema (#312)
* add pattern annotation * Add Pattern Annotation in JSON Schema Writer * Do cleanup * restore original formatting * Add additional example for pattern test type * fix indentation * fix simple pattern test type --------- Co-authored-by: Jonas Sobotta <[email protected]>
1 parent 4eb0d7f commit f54229b

6 files changed

+201
-3
lines changed

src/zcl_aff_abap_doc_parser.clas.abap

+37-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ CLASS zcl_aff_abap_doc_parser DEFINITION
2020
content_media_type TYPE string VALUE `$contentMediaType`,
2121
content_encoding TYPE string VALUE `$contentEncoding`,
2222
enum_value TYPE string VALUE `$enumValue`,
23+
pattern TYPE string VALUE `$pattern`,
2324
END OF abap_doc_annotation.
2425

2526
TYPES:
@@ -41,6 +42,7 @@ CLASS zcl_aff_abap_doc_parser DEFINITION
4142
content_media_type TYPE string,
4243
content_encoding TYPE string,
4344
enum_value TYPE string,
45+
pattern TYPE string,
4446
END OF abap_doc.
4547

4648
METHODS: parse
@@ -99,6 +101,7 @@ CLASS zcl_aff_abap_doc_parser DEFINITION
99101
annotation_name TYPE string
100102
RETURNING
101103
VALUE(number) TYPE string,
104+
parse_pattern,
102105
check_next_word
103106
IMPORTING
104107
offset TYPE i
@@ -164,7 +167,7 @@ CLASS zcl_aff_abap_doc_parser IMPLEMENTATION.
164167
ENDMETHOD.
165168

166169
METHOD parse_description.
167-
FIND FIRST OCCURRENCE OF PCRE `(\$callbackClass|\$default|\$values|\$required|\$showAlways|\$minimum|\$maximum|\$exclusiveMinimum|\$exclusiveMaximum|\$multipleOf|\$maxLength|\$minLength|\$enumValue|\$contentMediaType|\$contentEncoding)`
170+
FIND FIRST OCCURRENCE OF PCRE `(\$callbackClass|\$default|\$values|\$required|\$showAlways|\$minimum|\$maximum|\$exclusiveMinimum|\$exclusiveMaximum|\$multipleOf|\$maxLength|\$minLength|\$enumValue|\$contentMediaType|\$contentEncoding|\$pattern)`
168171
IN abap_doc_string MATCH OFFSET DATA(offset).
169172
IF sy-subrc = 0.
170173
DATA(description) = abap_doc_string+0(offset).
@@ -195,6 +198,8 @@ CLASS zcl_aff_abap_doc_parser IMPLEMENTATION.
195198
parse_required( ).
196199
WHEN abap_doc_annotation-show_always.
197200
parse_show_always( ).
201+
WHEN abap_doc_annotation-pattern.
202+
parse_pattern( ).
198203
WHEN abap_doc_annotation-minimum OR abap_doc_annotation-maximum OR abap_doc_annotation-exclusive_minimum OR abap_doc_annotation-exclusive_maximum
199204
OR abap_doc_annotation-max_length OR abap_doc_annotation-multiple_of OR abap_doc_annotation-min_length.
200205
parse_number_annotations( key_word = key_word ).
@@ -543,4 +548,35 @@ CLASS zcl_aff_abap_doc_parser IMPLEMENTATION.
543548
ENDIF.
544549
ENDMETHOD.
545550

551+
METHOD parse_pattern.
552+
IF decoded_abap_doc-pattern IS NOT INITIAL.
553+
RETURN.
554+
ENDIF.
555+
556+
DATA(string_to_parse) = abap_doc_string.
557+
558+
FIND ALL OCCURRENCES OF PCRE `\$pattern[\s]*(:[\s]*)?'([^']*)'` IN string_to_parse RESULTS DATA(result_table).
559+
560+
IF lines( result_table ) = 0.
561+
DATA(msg) = parser_log->get_message_text( msgno = 109 msgv1 = CONV #( abap_doc_annotation-pattern ) ).
562+
parser_log->add_warning( message_text = msg component_name = component_name ).
563+
RETURN.
564+
ENDIF.
565+
write_log_for_multiple_entries( result_table = result_table annotaion = abap_doc_annotation-pattern ).
566+
567+
LOOP AT result_table ASSIGNING FIELD-SYMBOL(<entry>).
568+
IF lines( <entry>-submatches ) = 2 AND decoded_abap_doc-pattern IS INITIAL.
569+
DATA(submatch) = <entry>-submatches[ 2 ].
570+
IF submatch-length = 0.
571+
msg = parser_log->get_message_text( msgno = 109 msgv1 = CONV #( abap_doc_annotation-pattern ) ).
572+
parser_log->add_warning( message_text = msg component_name = component_name ).
573+
ENDIF.
574+
575+
decoded_abap_doc-pattern = substring( val = string_to_parse off = submatch-offset len = submatch-length ).
576+
ENDIF.
577+
ENDLOOP.
578+
579+
ENDMETHOD.
580+
546581
ENDCLASS.
582+

src/zcl_aff_abap_doc_parser.clas.testclasses.abap

+80-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ CLASS ltcl_aff_abap_doc_parser DEFINITION FINAL FOR TESTING
3939
METHODS content_media_multiple_entries FOR TESTING RAISING cx_static_check.
4040
METHODS content_media_type_used_wrong FOR TESTING RAISING cx_static_check.
4141
METHODS content_encoding_used_wrong FOR TESTING RAISING cx_static_check.
42-
42+
METHODS too_many_patterns FOR TESTING RAISING cx_static_check.
43+
METHODS pattern_with_colon FOR TESTING RAISING cx_static_check.
44+
METHODS pattern_no_single_quotes FOR TESTING RAISING cx_static_check.
45+
METHODS pattern_no_value FOR TESTING RAISING cx_static_check.
46+
METHODS pattern FOR TESTING RAISING cx_static_check.
4347

4448
ENDCLASS.
4549

@@ -582,4 +586,79 @@ CLASS ltcl_aff_abap_doc_parser IMPLEMENTATION.
582586
exp_component_name = `Component Name` ).
583587
ENDMETHOD.
584588

589+
METHOD pattern.
590+
DATA(abap_doc_to_parse) = `<p class="shorttext">Title</p> This is the description. $pattern '[a-z]*'`.
591+
DATA(act_abap_doc) = parser->parse(
592+
EXPORTING
593+
component_name = `Component Name`
594+
to_parse = abap_doc_to_parse
595+
CHANGING
596+
log = log ).
597+
exp_abap_doc = VALUE #( title = `Title` description = `This is the description.` pattern = `[a-z]*` ).
598+
cl_abap_unit_assert=>assert_equals( exp = exp_abap_doc act = act_abap_doc ).
599+
zcl_aff_tools_unit_test_helper=>assert_log_has_no_message( log = log message_severity_threshold = zif_aff_log=>c_message_type-info ).
600+
ENDMETHOD.
601+
602+
METHOD pattern_with_colon.
603+
DATA(abap_doc_to_parse) = `<p class="shorttext">Title</p> This is the description. $pattern : '[a-z]*'`.
604+
DATA(act_abap_doc) = parser->parse(
605+
EXPORTING
606+
component_name = `Component Name`
607+
to_parse = abap_doc_to_parse
608+
CHANGING
609+
log = log ).
610+
exp_abap_doc = VALUE #( title = `Title` description = `This is the description.` pattern = `[a-z]*` ).
611+
cl_abap_unit_assert=>assert_equals( exp = exp_abap_doc act = act_abap_doc ).
612+
zcl_aff_tools_unit_test_helper=>assert_log_has_no_message( log = log message_severity_threshold = zif_aff_log=>c_message_type-info ).
613+
ENDMETHOD.
614+
615+
METHOD pattern_no_single_quotes.
616+
DATA(abap_doc_to_parse) = `<p class="shorttext">Title</p> This is the description. $pattern [a-z]*`.
617+
DATA(act_abap_doc) = parser->parse(
618+
EXPORTING
619+
component_name = `Component Name`
620+
to_parse = abap_doc_to_parse
621+
CHANGING
622+
log = log ).
623+
exp_abap_doc = VALUE #( title = `Title` description = `This is the description.` ).
624+
cl_abap_unit_assert=>assert_equals( exp = exp_abap_doc act = act_abap_doc ).
625+
zcl_aff_tools_unit_test_helper=>assert_log_contains_text( log = log
626+
exp_text = `Annotation $pattern was used incorrectly`
627+
exp_type = zif_aff_log=>c_message_type-warning
628+
exp_component_name = `Component Name` ).
629+
ENDMETHOD.
630+
631+
METHOD pattern_no_value.
632+
DATA(abap_doc_to_parse) = `<p class="shorttext">Title</p> This is the description. $pattern ''`.
633+
DATA(act_abap_doc) = parser->parse(
634+
EXPORTING
635+
component_name = `Component Name`
636+
to_parse = abap_doc_to_parse
637+
CHANGING
638+
log = log ).
639+
exp_abap_doc = VALUE #( title = `Title` description = `This is the description.` ).
640+
cl_abap_unit_assert=>assert_equals( exp = exp_abap_doc act = act_abap_doc ).
641+
zcl_aff_tools_unit_test_helper=>assert_log_contains_text( log = log
642+
exp_text = `Annotation $pattern was used incorrectly`
643+
exp_type = zif_aff_log=>c_message_type-warning
644+
exp_component_name = `Component Name` ).
645+
ENDMETHOD.
646+
647+
METHOD too_many_patterns.
648+
DATA(abap_doc_to_parse) = `<p class="shorttext">Title</p> This is the description. $pattern '[a-z]*' $pattern '[A-Z]*'`.
649+
DATA(act_abap_doc) = parser->parse(
650+
EXPORTING
651+
component_name = `Component Name`
652+
to_parse = abap_doc_to_parse
653+
CHANGING
654+
log = log ).
655+
exp_abap_doc = VALUE #( title = `Title` description = `This is the description.` pattern = `[a-z]*` ).
656+
cl_abap_unit_assert=>assert_equals( exp = exp_abap_doc act = act_abap_doc ).
657+
zcl_aff_tools_unit_test_helper=>assert_log_contains_text( log = log
658+
exp_text = |There are several occurrences of annotation { zcl_aff_abap_doc_parser=>abap_doc_annotation-pattern } . First valid is used|
659+
exp_type = zif_aff_log=>c_message_type-info
660+
exp_component_name = `Component Name` ).
661+
662+
ENDMETHOD.
663+
585664
ENDCLASS.

src/zcl_aff_test_types.clas.abap

+22
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,28 @@ CLASS zcl_aff_test_types DEFINITION
6767
default_link TYPE default_link,
6868
END OF struc_link_wrong_type.
6969

70+
TYPES:
71+
"! $pattern '[a-Z]*'
72+
ty_string TYPE string.
73+
74+
TYPES:
75+
"! <p class="shorttext">Structure With Pattern Annotation</p>
76+
"! Structure with pattern annotation
77+
BEGIN OF string_pattern_complex,
78+
"! <p class="shorttext">String with pattern</p>
79+
"! description
80+
string_pattern TYPE ty_string,
81+
END OF string_pattern_complex.
82+
83+
TYPES:
84+
"! <p class="shorttext">Structure With Pattern Annotation</p>
85+
"! Structure with pattern annotation
86+
BEGIN OF string_pattern_simple,
87+
"! <p class="shorttext">String with pattern</p>
88+
"! description
89+
"! $pattern '[a-Z]*'
90+
string_pattern TYPE string,
91+
END OF string_pattern_simple.
7092

7193
TYPES:
7294
"! in ST val(I()) only allow integers

src/zcl_aff_writer.clas.abap

+3
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,9 @@ CLASS zcl_aff_writer IMPLEMENTATION.
567567
IF abap_doc_base-content_media_type IS INITIAL.
568568
abap_doc_base-content_media_type = abap_doc_additional-content_media_type.
569569
ENDIF.
570+
IF abap_doc_base-pattern IS INITIAL.
571+
abap_doc_base-pattern = abap_doc_additional-pattern.
572+
ENDIF.
570573
ENDMETHOD.
571574

572575

src/zcl_aff_writer_json_schema.clas.abap

+3
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,9 @@ CLASS zcl_aff_writer_json_schema IMPLEMENTATION.
440440
write_tag( `"pattern": "^[0-9]+$",` ).
441441
ENDIF.
442442
ENDIF.
443+
IF abap_doc-pattern IS NOT INITIAL.
444+
write_tag( |"pattern": "{ abap_doc-pattern }",| ).
445+
ENDIF.
443446
ENDMETHOD.
444447

445448

src/zcl_aff_writer_json_schema.clas.testclasses.abap

+56-1
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,9 @@ CLASS ltcl_json_writer_abap_doc DEFINITION FINAL FOR TESTING
559559
content_encoding FOR TESTING RAISING cx_static_check,
560560
content_media_type_integer FOR TESTING RAISING cx_static_check,
561561
content_media_type_string FOR TESTING RAISING cx_static_check,
562-
encoding_type_next_level FOR TESTING RAISING cx_static_check.
562+
encoding_type_next_level FOR TESTING RAISING cx_static_check,
563+
pattern_simple FOR TESTING RAISING cx_static_check,
564+
pattern_complex FOR TESTING RAISING cx_static_check.
563565

564566
ENDCLASS.
565567

@@ -2788,4 +2790,57 @@ CLASS ltcl_json_writer_abap_doc IMPLEMENTATION.
27882790
zcl_aff_tools_unit_test_helper=>assert_log_has_no_message( log = log message_severity_threshold = zif_aff_log=>c_message_type-info ).
27892791
ENDMETHOD.
27902792

2793+
2794+
METHOD pattern_simple.
2795+
DATA(act_schema) = test_generator->generate_type( VALUE zcl_aff_test_types=>string_pattern_simple( ) ).
2796+
DATA(exp_schema) = VALUE string_table(
2797+
( ` { ` )
2798+
( | "$comment": "This file is autogenerated, do not edit manually, see { zcl_aff_writer_json_schema=>c_link_to_repository } for more information.", | )
2799+
( | "$schema": "{ zcl_aff_writer_json_schema=>c_schema_specification }",| )
2800+
( | "$id": "{ schema_id }",| )
2801+
( ` "title": "Structure With Pattern Annotation", ` )
2802+
( ` "description": "Structure with pattern annotation", ` )
2803+
( ` "type": "object", ` )
2804+
( ` "properties": { ` )
2805+
( ` "stringPattern": { ` )
2806+
( ` "title": "String with pattern",` )
2807+
( ` "description": "description",` )
2808+
( ` "type": "string", ` )
2809+
( ` "pattern": "[a-Z]*" ` )
2810+
( ` } ` )
2811+
( ` }, ` )
2812+
( ` "additionalProperties": false ` )
2813+
( ` } ` )
2814+
( ) ).
2815+
zcl_aff_tools_unit_test_helper=>assert_equals_ignore_spaces( act_data = act_schema exp_data = exp_schema ).
2816+
log = cut->zif_aff_writer~get_log( ).
2817+
zcl_aff_tools_unit_test_helper=>assert_log_has_no_message( log = log message_severity_threshold = zif_aff_log=>c_message_type-info ).
2818+
ENDMETHOD.
2819+
2820+
METHOD pattern_complex.
2821+
DATA(act_schema) = test_generator->generate_type( VALUE zcl_aff_test_types=>string_pattern_complex( ) ).
2822+
DATA(exp_schema) = VALUE string_table(
2823+
( ` { ` )
2824+
( | "$comment": "This file is autogenerated, do not edit manually, see { zcl_aff_writer_json_schema=>c_link_to_repository } for more information.", | )
2825+
( | "$schema": "{ zcl_aff_writer_json_schema=>c_schema_specification }",| )
2826+
( | "$id": "{ schema_id }",| )
2827+
( ` "title": "Structure With Pattern Annotation", ` )
2828+
( ` "description": "Structure with pattern annotation", ` )
2829+
( ` "type": "object", ` )
2830+
( ` "properties": { ` )
2831+
( ` "stringPattern": { ` )
2832+
( ` "title": "String with pattern",` )
2833+
( ` "description": "description",` )
2834+
( ` "type": "string", ` )
2835+
( ` "pattern": "[a-Z]*" ` )
2836+
( ` } ` )
2837+
( ` }, ` )
2838+
( ` "additionalProperties": false ` )
2839+
( ` } ` )
2840+
( ) ).
2841+
zcl_aff_tools_unit_test_helper=>assert_equals_ignore_spaces( act_data = act_schema exp_data = exp_schema ).
2842+
log = cut->zif_aff_writer~get_log( ).
2843+
zcl_aff_tools_unit_test_helper=>assert_log_has_no_message( log = log message_severity_threshold = zif_aff_log=>c_message_type-info ).
2844+
ENDMETHOD.
2845+
27912846
ENDCLASS.

0 commit comments

Comments
 (0)