Skip to content

Commit

Permalink
Add Support for Patterns in JSON Schema (#312)
Browse files Browse the repository at this point in the history
* 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 <jonas.sobotta@sap.com>
  • Loading branch information
jonassobotta and Jonas Sobotta authored Jun 24, 2024
1 parent 4eb0d7f commit f54229b
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 3 deletions.
38 changes: 37 additions & 1 deletion src/zcl_aff_abap_doc_parser.clas.abap
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ CLASS zcl_aff_abap_doc_parser DEFINITION
content_media_type TYPE string VALUE `$contentMediaType`,
content_encoding TYPE string VALUE `$contentEncoding`,
enum_value TYPE string VALUE `$enumValue`,
pattern TYPE string VALUE `$pattern`,
END OF abap_doc_annotation.

TYPES:
Expand All @@ -41,6 +42,7 @@ CLASS zcl_aff_abap_doc_parser DEFINITION
content_media_type TYPE string,
content_encoding TYPE string,
enum_value TYPE string,
pattern TYPE string,
END OF abap_doc.

METHODS: parse
Expand Down Expand Up @@ -99,6 +101,7 @@ CLASS zcl_aff_abap_doc_parser DEFINITION
annotation_name TYPE string
RETURNING
VALUE(number) TYPE string,
parse_pattern,
check_next_word
IMPORTING
offset TYPE i
Expand Down Expand Up @@ -164,7 +167,7 @@ CLASS zcl_aff_abap_doc_parser IMPLEMENTATION.
ENDMETHOD.

METHOD parse_description.
FIND FIRST OCCURRENCE OF PCRE `(\$callbackClass|\$default|\$values|\$required|\$showAlways|\$minimum|\$maximum|\$exclusiveMinimum|\$exclusiveMaximum|\$multipleOf|\$maxLength|\$minLength|\$enumValue|\$contentMediaType|\$contentEncoding)`
FIND FIRST OCCURRENCE OF PCRE `(\$callbackClass|\$default|\$values|\$required|\$showAlways|\$minimum|\$maximum|\$exclusiveMinimum|\$exclusiveMaximum|\$multipleOf|\$maxLength|\$minLength|\$enumValue|\$contentMediaType|\$contentEncoding|\$pattern)`
IN abap_doc_string MATCH OFFSET DATA(offset).
IF sy-subrc = 0.
DATA(description) = abap_doc_string+0(offset).
Expand Down Expand Up @@ -195,6 +198,8 @@ CLASS zcl_aff_abap_doc_parser IMPLEMENTATION.
parse_required( ).
WHEN abap_doc_annotation-show_always.
parse_show_always( ).
WHEN abap_doc_annotation-pattern.
parse_pattern( ).
WHEN abap_doc_annotation-minimum OR abap_doc_annotation-maximum OR abap_doc_annotation-exclusive_minimum OR abap_doc_annotation-exclusive_maximum
OR abap_doc_annotation-max_length OR abap_doc_annotation-multiple_of OR abap_doc_annotation-min_length.
parse_number_annotations( key_word = key_word ).
Expand Down Expand Up @@ -543,4 +548,35 @@ CLASS zcl_aff_abap_doc_parser IMPLEMENTATION.
ENDIF.
ENDMETHOD.

METHOD parse_pattern.
IF decoded_abap_doc-pattern IS NOT INITIAL.
RETURN.
ENDIF.

DATA(string_to_parse) = abap_doc_string.

FIND ALL OCCURRENCES OF PCRE `\$pattern[\s]*(:[\s]*)?'([^']*)'` IN string_to_parse RESULTS DATA(result_table).

IF lines( result_table ) = 0.
DATA(msg) = parser_log->get_message_text( msgno = 109 msgv1 = CONV #( abap_doc_annotation-pattern ) ).
parser_log->add_warning( message_text = msg component_name = component_name ).
RETURN.
ENDIF.
write_log_for_multiple_entries( result_table = result_table annotaion = abap_doc_annotation-pattern ).

LOOP AT result_table ASSIGNING FIELD-SYMBOL(<entry>).
IF lines( <entry>-submatches ) = 2 AND decoded_abap_doc-pattern IS INITIAL.
DATA(submatch) = <entry>-submatches[ 2 ].
IF submatch-length = 0.
msg = parser_log->get_message_text( msgno = 109 msgv1 = CONV #( abap_doc_annotation-pattern ) ).
parser_log->add_warning( message_text = msg component_name = component_name ).
ENDIF.

decoded_abap_doc-pattern = substring( val = string_to_parse off = submatch-offset len = submatch-length ).
ENDIF.
ENDLOOP.

ENDMETHOD.

ENDCLASS.

81 changes: 80 additions & 1 deletion src/zcl_aff_abap_doc_parser.clas.testclasses.abap
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ CLASS ltcl_aff_abap_doc_parser DEFINITION FINAL FOR TESTING
METHODS content_media_multiple_entries FOR TESTING RAISING cx_static_check.
METHODS content_media_type_used_wrong FOR TESTING RAISING cx_static_check.
METHODS content_encoding_used_wrong FOR TESTING RAISING cx_static_check.

METHODS too_many_patterns FOR TESTING RAISING cx_static_check.
METHODS pattern_with_colon FOR TESTING RAISING cx_static_check.
METHODS pattern_no_single_quotes FOR TESTING RAISING cx_static_check.
METHODS pattern_no_value FOR TESTING RAISING cx_static_check.
METHODS pattern FOR TESTING RAISING cx_static_check.

ENDCLASS.

Expand Down Expand Up @@ -582,4 +586,79 @@ CLASS ltcl_aff_abap_doc_parser IMPLEMENTATION.
exp_component_name = `Component Name` ).
ENDMETHOD.

METHOD pattern.
DATA(abap_doc_to_parse) = `<p class="shorttext">Title</p> This is the description. $pattern '[a-z]*'`.
DATA(act_abap_doc) = parser->parse(
EXPORTING
component_name = `Component Name`
to_parse = abap_doc_to_parse
CHANGING
log = log ).
exp_abap_doc = VALUE #( title = `Title` description = `This is the description.` pattern = `[a-z]*` ).
cl_abap_unit_assert=>assert_equals( exp = exp_abap_doc act = act_abap_doc ).
zcl_aff_tools_unit_test_helper=>assert_log_has_no_message( log = log message_severity_threshold = zif_aff_log=>c_message_type-info ).
ENDMETHOD.

METHOD pattern_with_colon.
DATA(abap_doc_to_parse) = `<p class="shorttext">Title</p> This is the description. $pattern : '[a-z]*'`.
DATA(act_abap_doc) = parser->parse(
EXPORTING
component_name = `Component Name`
to_parse = abap_doc_to_parse
CHANGING
log = log ).
exp_abap_doc = VALUE #( title = `Title` description = `This is the description.` pattern = `[a-z]*` ).
cl_abap_unit_assert=>assert_equals( exp = exp_abap_doc act = act_abap_doc ).
zcl_aff_tools_unit_test_helper=>assert_log_has_no_message( log = log message_severity_threshold = zif_aff_log=>c_message_type-info ).
ENDMETHOD.

METHOD pattern_no_single_quotes.
DATA(abap_doc_to_parse) = `<p class="shorttext">Title</p> This is the description. $pattern [a-z]*`.
DATA(act_abap_doc) = parser->parse(
EXPORTING
component_name = `Component Name`
to_parse = abap_doc_to_parse
CHANGING
log = log ).
exp_abap_doc = VALUE #( title = `Title` description = `This is the description.` ).
cl_abap_unit_assert=>assert_equals( exp = exp_abap_doc act = act_abap_doc ).
zcl_aff_tools_unit_test_helper=>assert_log_contains_text( log = log
exp_text = `Annotation $pattern was used incorrectly`
exp_type = zif_aff_log=>c_message_type-warning
exp_component_name = `Component Name` ).
ENDMETHOD.

METHOD pattern_no_value.
DATA(abap_doc_to_parse) = `<p class="shorttext">Title</p> This is the description. $pattern ''`.
DATA(act_abap_doc) = parser->parse(
EXPORTING
component_name = `Component Name`
to_parse = abap_doc_to_parse
CHANGING
log = log ).
exp_abap_doc = VALUE #( title = `Title` description = `This is the description.` ).
cl_abap_unit_assert=>assert_equals( exp = exp_abap_doc act = act_abap_doc ).
zcl_aff_tools_unit_test_helper=>assert_log_contains_text( log = log
exp_text = `Annotation $pattern was used incorrectly`
exp_type = zif_aff_log=>c_message_type-warning
exp_component_name = `Component Name` ).
ENDMETHOD.

METHOD too_many_patterns.
DATA(abap_doc_to_parse) = `<p class="shorttext">Title</p> This is the description. $pattern '[a-z]*' $pattern '[A-Z]*'`.
DATA(act_abap_doc) = parser->parse(
EXPORTING
component_name = `Component Name`
to_parse = abap_doc_to_parse
CHANGING
log = log ).
exp_abap_doc = VALUE #( title = `Title` description = `This is the description.` pattern = `[a-z]*` ).
cl_abap_unit_assert=>assert_equals( exp = exp_abap_doc act = act_abap_doc ).
zcl_aff_tools_unit_test_helper=>assert_log_contains_text( log = log
exp_text = |There are several occurrences of annotation { zcl_aff_abap_doc_parser=>abap_doc_annotation-pattern } . First valid is used|
exp_type = zif_aff_log=>c_message_type-info
exp_component_name = `Component Name` ).

ENDMETHOD.

ENDCLASS.
22 changes: 22 additions & 0 deletions src/zcl_aff_test_types.clas.abap
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,28 @@ CLASS zcl_aff_test_types DEFINITION
default_link TYPE default_link,
END OF struc_link_wrong_type.

TYPES:
"! $pattern '[a-Z]*'
ty_string TYPE string.

TYPES:
"! <p class="shorttext">Structure With Pattern Annotation</p>
"! Structure with pattern annotation
BEGIN OF string_pattern_complex,
"! <p class="shorttext">String with pattern</p>
"! description
string_pattern TYPE ty_string,
END OF string_pattern_complex.

TYPES:
"! <p class="shorttext">Structure With Pattern Annotation</p>
"! Structure with pattern annotation
BEGIN OF string_pattern_simple,
"! <p class="shorttext">String with pattern</p>
"! description
"! $pattern '[a-Z]*'
string_pattern TYPE string,
END OF string_pattern_simple.

TYPES:
"! in ST val(I()) only allow integers
Expand Down
3 changes: 3 additions & 0 deletions src/zcl_aff_writer.clas.abap
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,9 @@ CLASS zcl_aff_writer IMPLEMENTATION.
IF abap_doc_base-content_media_type IS INITIAL.
abap_doc_base-content_media_type = abap_doc_additional-content_media_type.
ENDIF.
IF abap_doc_base-pattern IS INITIAL.
abap_doc_base-pattern = abap_doc_additional-pattern.
ENDIF.
ENDMETHOD.


Expand Down
3 changes: 3 additions & 0 deletions src/zcl_aff_writer_json_schema.clas.abap
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,9 @@ CLASS zcl_aff_writer_json_schema IMPLEMENTATION.
write_tag( `"pattern": "^[0-9]+$",` ).
ENDIF.
ENDIF.
IF abap_doc-pattern IS NOT INITIAL.
write_tag( |"pattern": "{ abap_doc-pattern }",| ).
ENDIF.
ENDMETHOD.


Expand Down
57 changes: 56 additions & 1 deletion src/zcl_aff_writer_json_schema.clas.testclasses.abap
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,9 @@ CLASS ltcl_json_writer_abap_doc DEFINITION FINAL FOR TESTING
content_encoding FOR TESTING RAISING cx_static_check,
content_media_type_integer FOR TESTING RAISING cx_static_check,
content_media_type_string FOR TESTING RAISING cx_static_check,
encoding_type_next_level FOR TESTING RAISING cx_static_check.
encoding_type_next_level FOR TESTING RAISING cx_static_check,
pattern_simple FOR TESTING RAISING cx_static_check,
pattern_complex FOR TESTING RAISING cx_static_check.

ENDCLASS.

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


METHOD pattern_simple.
DATA(act_schema) = test_generator->generate_type( VALUE zcl_aff_test_types=>string_pattern_simple( ) ).
DATA(exp_schema) = VALUE string_table(
( ` { ` )
( | "$comment": "This file is autogenerated, do not edit manually, see { zcl_aff_writer_json_schema=>c_link_to_repository } for more information.", | )
( | "$schema": "{ zcl_aff_writer_json_schema=>c_schema_specification }",| )
( | "$id": "{ schema_id }",| )
( ` "title": "Structure With Pattern Annotation", ` )
( ` "description": "Structure with pattern annotation", ` )
( ` "type": "object", ` )
( ` "properties": { ` )
( ` "stringPattern": { ` )
( ` "title": "String with pattern",` )
( ` "description": "description",` )
( ` "type": "string", ` )
( ` "pattern": "[a-Z]*" ` )
( ` } ` )
( ` }, ` )
( ` "additionalProperties": false ` )
( ` } ` )
( ) ).
zcl_aff_tools_unit_test_helper=>assert_equals_ignore_spaces( act_data = act_schema exp_data = exp_schema ).
log = cut->zif_aff_writer~get_log( ).
zcl_aff_tools_unit_test_helper=>assert_log_has_no_message( log = log message_severity_threshold = zif_aff_log=>c_message_type-info ).
ENDMETHOD.

METHOD pattern_complex.
DATA(act_schema) = test_generator->generate_type( VALUE zcl_aff_test_types=>string_pattern_complex( ) ).
DATA(exp_schema) = VALUE string_table(
( ` { ` )
( | "$comment": "This file is autogenerated, do not edit manually, see { zcl_aff_writer_json_schema=>c_link_to_repository } for more information.", | )
( | "$schema": "{ zcl_aff_writer_json_schema=>c_schema_specification }",| )
( | "$id": "{ schema_id }",| )
( ` "title": "Structure With Pattern Annotation", ` )
( ` "description": "Structure with pattern annotation", ` )
( ` "type": "object", ` )
( ` "properties": { ` )
( ` "stringPattern": { ` )
( ` "title": "String with pattern",` )
( ` "description": "description",` )
( ` "type": "string", ` )
( ` "pattern": "[a-Z]*" ` )
( ` } ` )
( ` }, ` )
( ` "additionalProperties": false ` )
( ` } ` )
( ) ).
zcl_aff_tools_unit_test_helper=>assert_equals_ignore_spaces( act_data = act_schema exp_data = exp_schema ).
log = cut->zif_aff_writer~get_log( ).
zcl_aff_tools_unit_test_helper=>assert_log_has_no_message( log = log message_severity_threshold = zif_aff_log=>c_message_type-info ).
ENDMETHOD.

ENDCLASS.

0 comments on commit f54229b

Please sign in to comment.